やりたいこと
- docker + spring boot で、簡単なCRUDのrestAPIを作成したいと考えています
問題
- curlコマンドでPOSTすると、リクエストパラメータをいれているにも関わらず、nullエラーのようなものが返ってきます
具体的なエラー部分
- アプリを実行後、ターミナルで
curl -X POST -H "Content-Type: application/json" -d '{"name":"aaa", "message":"bbb"}' localhost:8080/tweetとしたところ、Internal Server Errorが返ってきました。 - アプリのデバッグログには以下のようなログが出てきました
// 返ってきたエラー 2020-05-28 10:55:59.693 DEBUG 8975 --- [nio-8080-exec-6] org.hibernate.SQL : select next_val as id_val from hibernate_sequence for update 2020-05-28 10:55:59.696 DEBUG 8975 --- [nio-8080-exec-6] org.hibernate.SQL : update hibernate_sequence set next_val= ? where next_val=? 2020-05-28 10:55:59.704 DEBUG 8975 --- [nio-8080-exec-6] org.hibernate.SQL : insert into tweet (message, name, id) values (?, ?, ?) 2020-05-28 10:55:59.707 WARN 8975 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1048, SQLState: 23000 2020-05-28 10:55:59.707 ERROR 8975 --- [nio-8080-exec-6] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'message' cannot be null 2020-05-28 10:55:59.712 ERROR 8975 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause java.sql.SQLIntegrityConstraintViolationException: Column 'message' cannot be null ~~~
やったこと
- 下記のServiceクラスのコードを書き換えて、idだけにしたり、
nullable = trueにして試したりしましたが、エラーは治りませんでした - dockerとの接続がうまく行っていないのかと思いましたが、urlは問題なさそうなのでそこでエラーは発生しない
- 手動でmySQLに
tweetテーブルを作成し、id,name,messageの値もいれてPOSTしてみたけど、エラー変わらず - 手動で値をいれた状態でGETしたところ、データが入っているにも関わらず
[{}]と空のレスポンスが返ってきた - その際のデバッグログのSQL文を直接DBで叩いたら、正常にデータが取得できた
// 0_などはhibernateが出力するエイリアスらしいので、それを削除してDBに接続して以下を実行したら、正常に値が帰ってきた select tweet0_.id as id1_0_, tweet0_.message as message2_0_, tweet0_.name as name3_0_ from tweet tweet0_
- POST時に値をいれてリクエストしているのに、nullだと認識されてしまっているのが原因かと考えているのですが、有効な対処が見つからず難儀しています、、、
コード
dockerイメージ
yml
1// docker-compose.yml 2 3version: "3" 4services: 5 database: 6 image: mysql:5.7 7 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci 8 environment: 9 MYSQL_ROOT_PASSWORD: hoge 10 MYSQL_DATABASE: testdb 11 volumes: 12 - /tmp/test_mysql:/var/lib/mysql 13 ports: 14 - 3306:3306
Spring Bootのapplication.yml
spring: datasource: sqlScriptEncoding: UTF-8 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/testdb?useSSL=false&requireSSL=false username: root password: hoge # アプリケーション起動時にデータベースを初期化しないようにする jpa: hibernate: ddl-auto: update # spring boot起動時にDBにテーブル作成できない問題の対応 properties: hibernate: dialect: org.hibernate.dialect.MySQL57Dialect logging: level: sql: debug
Spring Bootコード
java
1// Tweet.java 2// Entityクラス 3 4@Entity 5@Data 6@NoArgsConstructor 7@AllArgsConstructor 8@Table(name = "tweet") 9public class Tweet { 10 11 @Id 12 @GeneratedValue 13 @Column 14 private int id; 15 16 @Column(nullable = false) 17 private String name; 18 19 @Column(nullable = false) 20 private String message; 21 22}
java
1// TweetRepository.java 2// Repositoryクラス 3 4@public interface TweetRepository extends JpaRepository<Tweet,Integer>{}
java
1// TweetService.java 2// Serviceクラス 3 4@Service 5@Transactional 6public class TweetService { 7 @Autowired 8 TweetRepository tweetRepository; 9 10 public Tweet postTweet(Tweet tweet){ 11 return tweetRepository.save(tweet); 12 } 13 14 public List<Tweet> getTweet(){ 15 return tweetRepository.findAll(); 16 } 17}
java
1// TweetController.java 2// コントローラー 3 4@RestController 5@RequestMapping("tweet") 6public class TweetController { 7 @Autowired 8 TweetService tweetService; 9 10 @RequestMapping(method = RequestMethod.POST) 11 Tweet postTweet(@RequestBody Tweet tweet) { 12 return tweetService.postTweet(tweet); 13 } 14 15 @RequestMapping(method = RequestMethod.GET) 16 List<Tweet> getTweet() { 17 return tweetService.getTweet(); 18 } 19}
以下の点についてご確認頂き、結果を質問内容に追記してください。
1) MySQLを立ち上げない状態でSpring Bootのアプリケーションを起動したときに、正常に起動できるか?(データベース接続エラーが起きるかどうか)
2) postTweetメソッドで受け取るTweetクラスの引数の値をSystem.out.println等で出力したときに、どのようなデータがセットされているか?
rubytomato様
ありがとうございます。
> 1) MySQLを立ち上げない状態でSpring Bootのアプリケーションを起動したときに、正常に起動できるか?(データベース接続エラーが起きるかどうか)
docker stop で環境を停止した状態で アプリを実行したところ、
起動に失敗しました。
いくつかエラーが出ましたが、主に以下の部分が原因のようです
```
~~~
2020-05-29 11:09:09.666 ERROR 35626 --- [ task-1] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
~~~
```
```
~~~
2020-05-29 11:09:11.331 WARN 35626 --- [ task-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 08S01
2020-05-29 11:09:11.332 ERROR 35626 --- [ task-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
~~~
```
> 2) postTweetメソッドで受け取るTweetクラスの引数の値をSystem.out.println等で出力したときに、どのようなデータがセットされているか?
`System.out.println(tweet);` とすると、
`com.example.demo.Tweet@591cece5` の値が出力されました。
rubytomato様
すいません、2) テストで引数のtweetの中身を取りたかったので、
Tweet.javaクラスの定数を全てpublicにしてcurlコマンドを叩いたところ、
正常にPOSTが実行されました。
問題は、単純にprivateな定数に他クラスからアクセスできなかった為、
nullとしてPOSTされてしまっており、エラーが発生したものと思われます。
ご助言のおかげで助かりました。
ありがとうございました。
rubytomato様
なんどもすいません、よくよく考えたら、Entityの値がprivateだからアクセスできずエラーが返ってきているのは少しおかしい気がしています
普通APIのEntityはprivateにして永続化するものだと思うのですが、、、
なので、Entityを全てprivateに戻して、
Tweet.javaのTweetクラス内にコンストラクタを追加しました
```
public Tweet(int id,String name, String message) {
this.id = id;
this.name = name;
this.message = message;
}
```
この状態でcurlコマンドを叩いたところ、正常に値がDBに保存されていた為、問題ないか、、とは思うのですが、
curlコマンドを叩いた他とのターミナルで返ってくる返り値が `{}` と空の状態になってしまいます、、
Lombokを使われていると思いますが、それが反映されていないように思います。
クラスに@Dataアノテーションを付けているのに
> `System.out.println(tweet);` とすると、
> `com.example.demo.Tweet@591cece5` の値が出力されました。
というような出力がされる点がそうです。通常はtoStringメソッドがLombokによって自動的に生成されます。
EclipseであればLombokのプラグインとアノテーションプロセッサーを有効にする必要があったと思います。
rubytomato様
> Lombokを使われていると思いますが、それが反映されていないように思います。
おっしゃる通りでした。
VSCodeでSTSの拡張を入れてSpring BootでAPIを作っていましたが、
Spring Bootによるlombokの依存関係だけだと正しく反映されないようです。
https://qiita.com/asukahime1021/items/1a7b5c1843168df27333
lombok単体の拡張を追加したところ、当初のコードで正常に動作致しました。
ありがとうございました!
回答1件
あなたの回答
tips
プレビュー