Search code examples
javaspring-boothibernatespring-mvcthymeleaf

Resolved [org.springframework.beans.TypeMismatchException String : Failed to convert value of type 'java.lang.String' to required type


So I am building a site where someone puts a post and people can make comments to the post and people can make comments to those comments -- nested comments, if you will. Post creation: works, the initials responses/comments: works, but for some reason when I fill out the form add the nested comment, I get this error:

WARN 52874 --- [io-8080-exec-10] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'codes.optiko.oc.model.Comment'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'Adding my comment to a response'; nested exception is java.lang.NumberFormatException: For input string: "Addingmycommenttoaresponse"]

The following is my bean/model:

package codes.optiko.oc.model;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.*;
import java.sql.Timestamp;

@Entity
@Table(name = "comments")
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @Column(nullable = false, columnDefinition = "text")
    private String comment;

//  foreign key: many comments to one user
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

//    foreign key: many comments to one response
    @ManyToOne
    @JoinColumn(name = "response_id")
    private Response response;

//    time stamps are sql-oriented
    @Column
    @CreationTimestamp
    private Timestamp createDate;

    @Column
    @UpdateTimestamp
    private Timestamp updateDate;


//************** CONSTRUCTORS ********************

//    empty constructor
    public Comment(){
    }

    public Comment(long id){
        this.id = id;
    }

    public Comment(long id, String comment){
        this.id = id;
        this.comment = comment;
    }

    public Comment(long id, String comment, User user){
        this.id = id;
        this.comment = comment;
        this.user = user;
    }

    public Comment(long id, String comment, User user, Response response){
        this.id = id;
        this.comment = comment;
        this.user = user;
        this.response = response;
    }

    public Comment(long id, String comment, User user, Response response, Timestamp createDate){
        this.id = id;
        this.comment = comment;
        this.user = user;
        this.response = response;
        this.createDate = createDate;
    }

    public Comment(long id, String comment, User user, Response response, Timestamp createDate, Timestamp updateDate){
        this.id = id;
        this.comment = comment;
        this.user = user;
        this.response = response;
        this.createDate = createDate;
        this.updateDate = updateDate;
    }

//************** GETTERS and SETTERS ********************

//    getters and setters
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getBody() {
        return comment;
    }

    public void setBody(String body) {
        this.comment = comment;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Timestamp getDate() {
        return createDate;
    }

    public void setDate(Timestamp date) {
        this.createDate = createDate;
    }

    public Timestamp getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Timestamp updateDate) {
        this.updateDate = updateDate;
    }

    public Response getResponse() {
        return response;
    }

    public void setResponse(Response response) {
        this.response = response;
    }

    public Timestamp getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Timestamp createDate) {
        this.createDate = createDate;
    }
}

The following is my controller (creating the comment):

@PostMapping("/posts/{post_id}/response/{response_id}/create-comment")
    public String createResponse(@PathVariable long post_id, @PathVariable long response_id, @ModelAttribute Comment comment) {
        Response response = responseRepo.getOne(response_id);
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        comment.setResponse(response);
        comment.setUser(user);
        comment.setCreateDate(new Timestamp(System.currentTimeMillis()));

        commentRepo.save(comment);

        return "redirect:/posts/" + post_id;
    }

The following is my controller (For displaying a single post -- where people can add comments):

@GetMapping("/posts/{id}")
    public String viewPost(@PathVariable long id, Model model) {
        model.addAttribute("post", postRepo.getPostById(id));
        model.addAttribute("responses", responseRepo.findByPostId(id));
        model.addAttribute("response", new Response());
        model.addAttribute("comments", commentRepo.findByResponseId(id));
        model.addAttribute("comment", new Comment());
        return "posts/show";
    }

The following is my view:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">

<head th:replace="partials/partials :: head ('Show Question')"></head>

<body>
<nav th:replace="partials/partials :: navbar"></nav>

<div class="container">
    <h2 th:text="${post.title}"></h2>
    <p style="padding-left: 2rem;" th:utext="${post.description}"></p>
    <p style="padding-left: 2rem;" th:text="${post.updateDate}"></p>
    <p style="padding-left: 2rem;" th:text="${post.user.username}"></p>

    <div th:unless="${#lists.isEmpty(responses)}">
        <h3>Responses:</h3>
        <!-- Display video responses -->
        <div class="post-div mb-3" th:each="res : ${responses}">
            <p th:text="'Posted by: ' + ${post.user.username}"></p>
            <div class="video-responsive" th:utext="${res.video}"></div>

            <!-- Display comments for video responses -->
            <div th:unless="${#lists.isEmpty(comments)}">
                <div class="post-div mb-3" th:each="com : ${comments}">
                    <p th:text="${com.comment}"></p>
                </div>
            </div>

            <!-- Add a comment -->
            <div sec:authorize="isAuthenticated()">
                <button class="add-comment btn btn-primary">Add a Comment</button>
                <form th:action="@{|/posts/${post.id}/response/${res.id}/create-comment|}" th:method="post" th:object="${comment}" class="comment-form">
                    <div class="form-group">
                        <label for="comment">Comment:</label>
                        <textarea class="form-control" id="comment" th:field="*{comment}"></textarea>
                    </div>
                    <button type="submit" class="btn btn-primary">Submit Comment</button>
                </form>
            </div>
            <div sec:authorize="!isAuthenticated()">
                <p class="font-weight-bold">Please <a href="/login">log in</a> or <a href="/register">register</a> to add a comment.</p>
            </div>
        </div>
    </div>

    <!-- Add a video response -->
    <div sec:authorize="isAuthenticated()">
        <button class="add-video-response btn btn-primary">Add a Video Response</button>
        <form th:action="@{|/posts/${post.id}/create-response|}" th:method="post" th:object="${response}" class="video-response-form">
            <div class="form-group">
                <label for="video">Video Embed URL:<br><span class="small">(YouTube, Vimeo, DailyMotion, etc...)</span></label>
                <textarea class="form-control" id="video" th:field="*{video}"></textarea>
            </div>
            <button type="submit" class="btn btn-primary">Submit Video Response</button>
        </form>
    </div>
    <div sec:authorize="!isAuthenticated()">
        <p class="font-weight-bold">Please <a href="/login">log in</a> or <a href="/register">register</a> to add a video response.</p>
    </div>
</div>

<standard_scripts th:replace="partials/partials :: footer">
</standard_scripts>
</body>
</html>

I tried having it print out to the console whatever is sent to the controller when they submit, but it seems like it errors out before even doing anything. I have been trying to figure this out for hours now, and have exhausted all of what I know to try and resolve the issue.

Thank you in advanced!


Solution

  • Note: Solution found in comments. Reposting as answer so it can be marked as solution

    Change the name of the Field Comment#comment. There is most likely a mixup in the html where it sends the comment string back to the function instead of the Comment object