

@ManyToOne, @OneToMany
を利用し、ブログ（blogクラス）に対してのコメント（commentクラス）機能を追加できるようにしたいのですが「フィールド「ブログ」のオブジェクト「コメント」のフィールドエラー」という内容でエラーが生じコメントが表示されません。

①ブログは複数のコメントを持ち（１対多）、コメントは１つのブログに結びついている（多対１）。  
②blog,commentに各リポジトリインタフェースを作成。  
③コントローラにblogページを表示する際にcommentオブジェクトを作成しModelに追加。commentリポジトリインタフェースを使用し、フォーム送信された内容に投稿日時を付加して保存する。

XAMPPのMySQLを使用してます。commentテーブルにblog_idは生成されてリレーションはできているはずですが、結果表示が思うようにいきません。  
フィールドの誤字や引数に間違いがあるのではと思い確認しましたが特に間違いはありませんでした。  
ご教授願います。

application.properties

[code]

    spring.jpa.hibernate.ddl-auto=update
    spring.datasource.url=jdbc:mysql://localhost:3306/データベース名?serverTimezone=JST
    spring.datasource.username=ユーザー名
    spring.datasource.password=パスワード
    
[/code]

blog.java

[code]

    package com.example.demo;
    
    import java.time.LocalDateTime;
    import java.util.List;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    
    import lombok.Data;
    
    @Entity //JPAにテーブルに保存するクラス（エンティティ）であることを示す。
    @Data
    public class Blog {
        @Id //対応するテーブルのプライマリーキー（主キー）となるフィールド
        @GeneratedValue(strategy=GenerationType.AUTO) //プライマリーキーの値を連番で自動生成。
        private Integer id;
    
        private String title;
    
        private LocalDateTime postDateTime;
    
        @Column(length=1000) //テーブルの列を制御したい場合は@Columnをつける。フィールドの長さをデフォルトの255から1000にする。
        private String contents;
                                     //1対多の関係。ブログは複数のコメントを持つ。
        @OneToMany(mappedBy="blog") //mappedBy引数には「相手側が自分を参照する名前」を指定。
        private List<Comment> comments;
    }
    
    
[/code]

comment.java

[code]

    package com.example.demo;
    
    import java.time.LocalDateTime;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.ManyToOne;
    
    import lombok.Data;
    
    @Entity
    @Data
    public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String text;
    private LocalDateTime postDateTime;
    
    @ManyToOne         //多対１の関係。Commentは一つブログに結びついている。
    private Blog blog;
    
    }
    
[/code]

BlogRepository.java

[code]

    package com.example.demo;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface BlogRepository extends JpaRepository<Blog, Integer> {
    
    }
    
    
[/code]

CommentRepository.java

[code]

    package com.example.demo;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface CommentRepository extends JpaRepository<Comment, Integer>{
    
    }
    
[/code]

SampleController.java

[code]

    package com.example.demo;
    
    import java.time.LocalDateTime;
    import java.util.List;
    import java.util.Optional;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    
    @Controller
    public class SampleController {
    
        @Autowired //リポジトリインターフェース参照、追加、更新などのメソッドがあらかじめ定義されている。findAllメソッドを使用することで単純にテーブル内のレコードを一覧で返す。
        private BlogRepository blogRepository;
        @Autowired
        private CommentRepository commentRepository;
    
        @GetMapping("/")
        public String index(Model model) {
            List<Blog> blogs = blogRepository.findAll();
            model.addAttribute("blogs", blogs);
            return "index";
        }
    
        @GetMapping("/form")
        public String form(Blog blog) {
            return "form";
        }
    
        @PostMapping("/create")
        public String create(Blog blog) {
            blog.setPostDateTime(LocalDateTime.now());
            blogRepository.save(blog);
            //エンティティを保存するにはリポジトリインターフェースのsaveメソッドを使用。
            //エンティティをsaveすると@Idアノテーションを付けたフィールドに値が設定される。
            return "redirect:/blog/" + blog.getId();
        }
    
        @GetMapping("/blog/{id}")
        public String blog(@PathVariable Integer id, Model model) {
            Optional<Blog> blog = blogRepository.findById(id);
            //返されたオブジェクトがnullでなければこの処理を行うなど、nullの場合の処理を書きやすくするためのもの。
            //getメソッドを呼び出して中身を取り出す。
            model.addAttribute("blog", blog.get());
    
            Comment comment = new Comment();
            comment.setBlog(blog.get());
            model.addAttribute("comment", comment);
            return "blog";
        }
    
        @PostMapping("/comment")
        public String createComment(Comment comment) {
            comment.setPostDateTime(LocalDateTime.now());
            commentRepository.save(comment);
            return "redirect:/blog/" + comment.getBlog().getId();
        }
    
    }
    
[/code]

blog.html

[code]

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
    <meta charset="UTF-8">
    <title th:text="${blog.title}">ブログタイトル</title>
    </head>
    <body>
        <p>
            <a href="/">一覧に戻る</a>
        </p>
        <div th:object="${blog}">
                <h1 th:text="*{title}">タイトル</h1>
            <div>
                投稿日時
                <span th:text="*{postDateTime}">投稿日時</span>
            </div>
            <p>
                <th:block th:each="line : *{contents.split('\n')}">
                <th:block th:text="${line}"></th:block><br>
                </th:block>
            </p>
    
            <form action="/comment" method="post" th:object="${comment}">
                        コメントをどうぞ<br>
                <input type="hidden" name="blog" th:value="*{blog.id}">
                <input type="text" size="40" th:field="*{text}">
                <input type="submit">
            </form>
    
            <ul>
                <li th:each="c : *{comments}" th:object="${c}">
                    <span  th:text="*{postDateTime}"></span>
                    <span  th:text="*{text}"></span>
                </li>
            </ul>
    
        </div>
    </body>
    </html>
    
[/code]

エラー内容

[code]

    Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.
    
    Mon Jun 15 17:32:58 JST 2020
    There was an unexpected error (type=Bad Request, status=400).
    Validation failed for object='comment'. Error count: 1
    org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
    Field error in object 'comment' on field 'blog': rejected value [4]; codes [typeMismatch.comment.blog,typeMismatch.blog,typeMismatch.com.example.demo.Blog,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [comment.blog,blog]; arguments []; default message [blog]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'com.example.demo.Blog' for property 'blog'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'com.example.demo.Blog' for property 'blog': no matching editors or conversion strategy found]
    
[/code]


CC BY-SA 4.0
2020-06-15T08:38:55.253
67679
2020-11-16T00:37:55.993
2020-06-15T11:55:46.057
3060
40666
question
spring-boot jpa
Springboot JPAを使用しオブジェクト間の関連を設定。ブログにコメントが投稿できるようにしたい。


[英語版SO](https://stackoverflow.com/q/62480677/4506703)でも同様に質問があり、こちらはSprinb
Boot `2.3.1` で発生するとのことでした。  
同様のバージョンを利用しているのであれば、同原因、つまり[フレームワーク側のバグ](https://jira.spring.io/browse/DATACMNS-1743)であると思われます。

本来、JPA EntityのIDからEntityオブジェクトに変換してくれるコンバーター
[`ToEntityConverter`](https://github.com/spring-projects/spring-data-
commons/blob/2.3.1.RELEASE/src/main/java/org/springframework/data/repository/support/DomainClassConverter.java#L124-L130)があり、これが利用されるはずですが、バグによってこのコンバータが登録されておらず、該当のエラーが出るようになってしまっている、ようです。

Spring Boot のバージョンで言うと影響を受けるのは `2.1.15`, `2.2.8`, `2.3.1`
のようなので、ワークアラウンドとしてはこのバージョンを避ける、ということが挙げられます。

* * *

追記:

[2020-07-24にリリースされた2.3.2](https://spring.io/blog/2020/07/24/spring-
boot-2-3-2-available-now)及び2.4.0-M1, 2.2.9で修正されていました。

ただし、2.1系列については現時点(2.1.16)で何故か修正されていません。

  * <https://docs.spring.io/spring-data/commons/docs/current/changelog.txt>

* * *

追記2:

先日(2020-11-01) Spring Boot 2.1 系列が[EOLを迎えました](https://github.com/spring-
projects/spring-boot/wiki/Supported-Versions)が、[最終バージョンである
`2.1.18`](https://spring.io/blog/2020/10/29/spring-boot-2-1-18-available-
now)でも結局修正されずじまいのようです。


CC BY-SA 4.0
2020-06-20T09:15:34.227
67830
2020-11-16T00:37:55.993
2020-11-16T00:37:55.993
2808
2808
67679
answer