質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

Q&A

解決済

2回答

4740閲覧

MySQL insert文が発行されない

encho

総合スコア182

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Spring

Spring Framework は、Javaプラットフォーム向けのオープンソースアプリケーションフレームワークです。 Java Platform上に、 Web ベースのアプリケーションを設計するための拡張機能が数多く用意されています。

Spring Boot

Spring Bootは、Javaのフレームワークの一つ。Springプロジェクトが提供する様々なフレームワークを統合した、アプリケーションを高速で開発するために設計されたフレームワークです。

0グッド

0クリップ

投稿2020/03/15 07:28

編集2020/03/15 08:06

#Spring Bootでメッセージアプリケーション
メッセージアプリケーションをSpring Bootで作っていますが
メッセージを送信した後にうまくDBに情報が保存されません。
どの部分に問題があるのかお分かりの方がいたらアドバイスをいただきたいです。

以下のように実行をしてみましたが、発行されたSQL文は

sql

1select message0_.message_id as message_1_4_, message0_.message_content as message_2_4_, message0_.message_time as message_3_4_, message0_.receiver_user_id as receiver4_4_, message0_.sender_user_id as sender_u5_4_ from message message0_ left outer join user user1_ on message0_.receiver_user_id=user1_.user_id where user1_.user_id=? 2

のようにselect文のみになっています。
本来はメッセージテーブルに送信した情報を保存したいです。
おそらくは@ManyToOneにおけるDBへの保存方法に問題があると思うのですが
具体的にどの部分に問題があるのかが見当がつきません。
何かアドバイスをいただけると幸いです。

使用環境
SpringBoot 2.2.4.
mysql 8.0.18

##insert文

java

1@Repository 2@Transactional 3public interface MessageReposioty extends JpaRepository<Message, Long>{ 4 List<Message> findBySenderUser_UserId(int userId); 5 List<Message> findByReceiverUser_UserId(int userId); 6 7 @Modifying 8 @Query(value = "insert into message (sender_user_id,receiver_user_id,message_content) " 9 + "VALUES (:senderUserId,:receiverUserId,:messageContent)", nativeQuery = true) 10 @Transactional 11 void sendMessage(@Param("senderUserId") int senderUserId, @Param("receiverUserId") int receiverUserId, 12 @Param("messageContent") String messageContent); 13}

##Entity

java

1@Entity 2@Transactional 3@NamedQuery(name="User.findAll", query="SELECT u FROM User u") 4public class User implements Serializable { 5 private static final long serialVersionUID = 1L; 6 7 @Id 8 @Column(name="user_id") 9 private int userId; 10 11 12 //bi-directional many-to-one association to Message 13 @OneToMany(mappedBy="senderUser") 14 private List<Message> messages1; 15 16 //bi-directional many-to-one association to Message 17 @OneToMany(mappedBy="receiverUser") 18 private List<Message> messages2; 19 20 public User() { 21 } 22 23 public int getUserId() { 24 return this.userId; 25 } 26 27 public void setUserId(int userId) { 28 this.userId = userId; 29 } 30 31 32 public List<Message> getMessages1() { 33 return this.messages1; 34 } 35 36 public void setMessages1(List<Message> messages1) { 37 this.messages1 = messages1; 38 } 39 40 public Message addMessages1(Message messages1) { 41 getMessages1().add(messages1); 42 messages1.setSenderUser(this); 43 44 return messages1; 45 } 46 47 public Message removeMessages1(Message messages1) { 48 getMessages1().remove(messages1); 49 messages1.setSenderUser(null); 50 51 return messages1; 52 } 53 54 public List<Message> getMessages2() { 55 return this.messages2; 56 } 57 58 public void setMessages2(List<Message> messages2) { 59 this.messages2 = messages2; 60 } 61 62 public Message addMessages2(Message messages2) { 63 getMessages2().add(messages2); 64 messages2.setReceiverUser(this); 65 66 return messages2; 67 } 68 69 public Message removeMessages2(Message messages2) { 70 getMessages2().remove(messages2); 71 messages2.setReceiverUser(null); 72 73 return messages2; 74 } 75 76 77}

java

1@Entity 2@NamedQuery(name="Message.findAll", query="SELECT m FROM Message m") 3public class Message implements Serializable { 4 private static final long serialVersionUID = 1L; 5 6 @Id 7 @GeneratedValue(strategy=GenerationType.IDENTITY) 8 @Column(name="message_id") 9 private long messageId; 10 11 @Column(name="message_content") 12 private String messageContent; 13 14 @Temporal(TemporalType.TIMESTAMP) 15 @Column(name="message_time") 16 private Date messageTime; 17 18 //送信者 19 @ManyToOne 20 @JoinColumn(name="sender_user_id") 21 private User senderUser; 22 23 //受信者 24 @ManyToOne 25 @JoinColumn(name="receiver_user_id") 26 private User receiverUser; 27 28 public Message() { 29 } 30 31 public long getMessageId() { 32 return this.messageId; 33 } 34 35 public void setMessageId(long messageId) { 36 this.messageId = messageId; 37 } 38 39 public String getMessageContent() { 40 return this.messageContent; 41 } 42 43 public void setMessageContent(String messageContent) { 44 this.messageContent = messageContent; 45 } 46 47 public Date getMessageTime() { 48 return this.messageTime; 49 } 50 51 public void setMessageTime(Date messageTime) { 52 this.messageTime = messageTime; 53 } 54 55 public User getSenderUser() { 56 return senderUser; 57 } 58 59 public void setSenderUser(User senderUser) { 60 this.senderUser = senderUser; 61 } 62 63 public User getReceiverUser() { 64 return receiverUser; 65 } 66 67 public void setReceiverUser(User receiverUser) { 68 this.receiverUser = receiverUser; 69 } 70

##controller

java

1@Controller 2@RequestMapping("profile") 3public class MessageController { 4 5 @Autowired 6 UserRepository userRepository; 7 @Autowired 8 MessageReposioty messageRepository; 9 10 @ModelAttribute 11 public MessageForm messageForm() { 12 return new MessageForm(); 13 } 14 15 @GetMapping("/{userId}/message") 16 String showMessage(@PathVariable("userId") Integer userId, 17 Model model) { 18 User senderUser = userRepository.findById(userId).orElse(null); 19 List<Message> recieveMessage = messageRepository.findByReceiverUser_UserId(userId); 20 model.addAttribute("recieveMessage",recieveMessage); 21 model.addAttribute("senderUser",senderUser); 22 return "profile/message"; 23 } 24 25 //送信時の処理 26 @PostMapping("/{userId}/message") 27 String sendMessage(@Validated MessageForm messageForm, 28 @AuthenticationPrincipal LoginUserDetails userDetails, 29 @PathVariable("userId") Integer userId, 30 Model model) { 31 User senderUser = userDetails.getUser(); 32 User receiverUser = userRepository.findById(userId).orElse(null); 33 User senderUser = userDetails.getUser(); 34 User receiverUser = userRepository.findById(userId).orElse(null); 35 int senderUserId = senderUser.getUserId(); 36 int receiverUserId = receiverUser.getUserId(); 37 String messageContent = messageForm.getMessageContent(); 38 //ここでinsertを実行 39 messageRepository.sendMessage(senderUserId, receiverUserId, messageContent); 40 return "redirect:{userId}/message"; 41 } 42} 43

##HTML

html

1<!DOCTYPE html> 2<html xmlns:th="http://www.thymeleaf.org" 3 xmlns:sec="http://www.thymeleaf.org/extras/spring-security"> 4<head> 5<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 6<title>Insert title here</title> 7</head> 8<body th:with="user = ${#authentication.principal.user}"> 9 <h1>メッセージ</h1> 10 <p th:text="${senderUser.userName}+'さんへメッセージを送る'"></p> 11 <form th:action="@{'/profile/'+${senderUser.userId}+'/message'}" th:object="${messageForm}"> 12 <textarea placeholder="メッセージを入力してください" th:field="*{messageContent}"> 13 </textarea> 14 <input type="submit"> 15 </form> 16</body> 17</html>

#作成したテーブル

sql

1 2--ユーザーテーブル-- 3CREATE TABLE IF NOT EXISTS user( 4 user_id INTEGER AUTO_INCREMENT, 5 user_name VARCHAR(12) NOT NULL, 6 PRIMARY KEY(user_id); 7) ENGINE=INNODB DEFAULT CHARSET=utf8; 8 9--メッセージテーブル-- 10CREATE TABLE IF NOT EXISTS message( 11 message_id BIGINT AUTO_INCREMENT NOT NULL, 12 sender_user_id INTEGER NOT NULL, 13 receiver_user_id INTEGER NOT NULL, 14 message_content VARCHAR(300), 15 message_time datetime DEFAULT CURRENT_TIMESTAMP, 16 PRIMARY KEY(message_id), 17 FOREIGN KEY (sender_user_id) 18 REFERENCES user(user_id) 19 ON DELETE CASCADE, 20 FOREIGN KEY (receiver_user_id) 21 REFERENCES user(user_id) 22 ON DELETE CASCADE 23) ENGINE=INNODB DEFAULT CHARSET=utf8; 24 25

#ER図
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Orlofsky

2020/03/15 07:54

SQLで使われるテーブルはCREATE TABLEされていなければなりません。テーブル内のデータはINSERTされていなければSELECTできません。ですから、質問にCREATE TABLEやINSERTでデータを提示した方が適切な回答が付き易いです。
encho

2020/03/15 08:07

ありがとうございます。 テーブルを作成したSQL分の追加を行いました。 MESSAGEテーブルにはまだ何もinsertされておらず これからINSERTをしようという状態です。
guest

回答2

0

ベストアンサー

このmethodを省略したフォームだとGETメソッドでメッセージを送信するので、実行されるコントローラメソッドはMessageController.showMessageになります。
これがinsert文が実行されない原因となります。

html

1<form th:action="@{'/profile/'+${senderUser.userId}+'/message'}" th:object="${messageForm}"> 2 3 <!-- 省略 --> 4 5</form>

修正は以下のようにフォームにmethod="POST"を追加すればよいと思います。これでPOSTメソッドでメッセージを送信するので、MessageController.sendMessageが実行されます。
他にも修正が必要な個所がありますが、とりあえずinsert文は実行されると思います。

html

1<form th:action="@{'/profile/'+${senderUser.userId}+'/message'}" th:object="${messageForm}" method="POST"> 2 3 <!-- 省略 --> 4 5</form>

今回のような不具合に早く気付くには、必要な個所にログを出力するコードを仕込んでおくのが効果的です。
以下はlogbackを利用したロギングの例です。メソッドの先頭で引数をdebugログに出力しています。

java

1@GetMapping("/{userId}/message") 2String showMessage(@PathVariable("userId") Integer userId, Model model) { 3 log.debug("showMessage userId:{}", userId); 4 5 // 省略 6 7 return "profile/message"; 8} 9 10@PostMapping("/{userId}/message") 11String sendMessage(@Validated MessageForm messageForm, @AuthenticationPrincipal LoginUserDetails userDetails, @PathVariable("userId") Integer userId, Model model) { 12 log.debug("sendMessage form:{}, userId:{}", messageForm, userId); 13 14 // 省略 15 16 // ↓ ちなみにこの書き方だと期待するリダイレクトになりません。 17 return "redirect:{userId}/message"; 18}

追記

以下は私だったらこう実装するという意味の内容です。なのでこうしなければならないという訳ではなく、あくまで参考程度のものです。また、コード全体ではなく必要な個所だけ抜粋しています。

エンティティクラス

java

1@Entity 2@NamedQuery(name="Message.findAll", query="SELECT m FROM Message m") 3public class Message implements Serializable { 4 5 @Id 6 @GeneratedValue(strategy=GenerationType.IDENTITY) 7 @Column(name="message_id") 8 // フィールドの型にプリミティブ型は使用しない 9 //private long messageId; 10 private Long messageId; 11 12 //処理のないデフォルトコンストラクタは書かない 13 //public Message() { 14 //} 15 16 public void setSenderUser(User senderUser) { 17 this.senderUser = senderUser; 18 this.senderUser.addSendMessage(this); 19 } 20 21 public void setReceiverUser(User receiverUser) { 22 this.receiverUser = receiverUser; 23 this.receiverUser.addReceiveMessage(this); 24 } 25 26}

java

1@Entity 2//Transactionalアノテーションはトランザクション境界を考えて付与する 3//@Transactional 4@NamedQuery(name="User.findAll", query="SELECT u FROM User u") 5public class User implements Serializable { 6 7 @Id 8 @Column(name="user_id") 9 // フィールドの型にプリミティブ型は使用しない 10 //private int userId; 11 private Integer userId; 12 13 @OneToMany(mappedBy="senderUser") 14 //意味の通じにくいフィールド名は避ける 15 //private List<Message> messages1; 16 private List<Message> sendMessages; 17 18 @OneToMany(mappedBy="receiverUser") 19 //意味の通じにくいフィールド名は避ける 20 //private List<Message> messages2; 21 private List<Message> receiveMessages; 22 23 //処理のないデフォルトコンストラクタは書かない 24 //public User() { 25 //} 26 27 public void addSendMessage(Message message) { 28 if (this.sendMessages == null) { 29 this.sendMessages = new ArrayList<>(); 30 } 31 this.sendMessages.add(message); 32 } 33 34 public void addReceiveMessage(Message message) { 35 if (this.receiveMessages == null) { 36 this.receiveMessages = new ArrayList<>(); 37 } 38 this.receiveMessages.add(message); 39 } 40 41}

リポジトリインターフェース
(※MessageReposiotyMessageRepositoryのtypoです)

java

1//不要なアノテーションは付与しない 2//@Repository 3//Transactionalアノテーションはトランザクション境界を考えて付与する 4//@Transactional 5public interface MessageRepository extends JpaRepository<Message, Long> { 6 //プリミティブ型を避ける 7 //List<Message> findBySenderUser_UserId(int userId); 8 //List<Message> findByReceiverUser_UserId(int userId); 9 List<Message> findBySenderUser_UserId(Integer userId); 10 List<Message> findByReceiverUser_UserId(Integer userId); 11 12 //可能な限りネイティブクエリを避ける 13 //今回はsaveメソッドで十分なので使用しない 14 //@Modifying 15 //@Query(value = "insert into message (sender_user_id,receiver_user_id,message_content) " 16 // + "VALUES (:senderUserId,:receiverUserId,:messageContent)", nativeQuery = true) 17 //@Transactional 18 //void sendMessage(@Param("senderUserId") int senderUserId, @Param("receiverUserId") int receiverUserId, 19 // @Param("messageContent") String messageContent); 20 21}

コントローラクラス

java

1@Controller 2@RequestMapping("profile") 3public class MessageController { 4 5 //コンストラクタインジェクションを利用する 6 //@Autowired 7 //UserRepository userRepository; 8 //@Autowired 9 //MessageRepository messageRepository; 10 11 private final UserRepository userRepository; 12 private final MessageRepository messageRepository; 13 14 public MessageController(UserRepository userRepository, MessageRepository messageRepository) { 15 this.userRepository = userRepository; 16 this.messageRepository = messageRepository; 17 } 18 19 //送信時の処理 20 @PostMapping("/{userId}/message") 21 // トランザクション境界をここに設定する(この例では) 22 // 本来ならサービスクラスを実装しそのメソッドにトランザクション境界を設定する 23 @Transactional(readOnly = false, timeout = 10) 24 String sendMessage(@Validated MessageForm messageForm, 25 // バリデーションを行うのならBindingResultでその結果を受け取る 26 BindingResult result, 27 @AuthenticationPrincipal LoginUserDetails userDetails, 28 @PathVariable("userId") Integer userId 29 // 使用しない引数は定義しない 30 /* Model model */) { 31 32 if (result.hasErrors()) { 33 // バリデーションエラー時の処理 34 } 35 36 //User senderUser = userDetails.getUser(); 37 User senderUser = userRepository.findById(userDetails.getUser().getUserId()).orElseThrow(); 38 39 //安易にnullを返さない。検索できなかったときにどうするか仕様を決めておく。 40 //User receiverUser = userRepository.findById(userId).orElse(null); 41 User receiverUser = userRepository.findById(userId).orElseThrow(); 42 43 //int senderUserId = senderUser.getUserId(); 44 //int receiverUserId = receiverUser.getUserId(); 45 46 String messageContent = messageForm.getMessageContent(); 47 48 Message message = new Message(); 49 message.setMessageContent(messageContent); 50 message.setMessageTime(new Date()); 51 message.setSenderUser(senderUser); 52 message.setReceiverUser(receiverUser); 53 messageRepository.save(message); 54 55 //リポジトリのsaveメソッドで永続化するので使用しない 56 //messageRepository.sendMessage(senderUserId, receiverUserId, messageContent); 57 return "redirect:/profile/" + userId + "/message"; 58 } 59 60}

投稿2020/03/15 11:08

編集2020/03/15 14:19
rubytomato

総合スコア1752

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

encho

2020/03/15 12:05

ご回答ありがとうございます。本当に根本的な部分のミスでしたのでお恥ずかしい限りです。 ロギングの件も参考にさせていただきます。
encho

2020/03/15 12:07

``` @PostMapping("/{userId}/message") String sendMessage(@Validated MessageForm messageForm, @AuthenticationPrincipal LoginUserDetails userDetails, @PathVariable("userId") Integer userId, Model model) { User senderUser = userDetails.getUser(); User receiverUser = userRepository.findById(userId).orElse(null); int senderUserId = senderUser.getUserId(); int receiverUserId = receiverUser.getUserId(); String messageContent = messageForm.getMessageContent(); messageRepository.sendMessage(senderUserId, receiverUserId, messageContent); return "redirect:/profile/"+userId+"/message"; } ```
encho

2020/03/15 12:09

以上のように改良することでリダイレクトDBへの保存含め問題なく機能するようになりました。 これ以外に変更が必要な箇所がありましたらご指摘いただけると幸いです。 ずっと止まってしまっていたので本当に感謝しています。ありがとうございました。
rubytomato

2020/03/15 14:21

機能するようになったということでよかったです。 私だったらこうするという視点で追記しましたのでご確認ください。
encho

2020/03/15 23:43

ご丁寧に参考のコードまで本当にありがとうございます。 初学者のため大変参考になる部分が多くありました 引き続き学習に励みスキルアップを図って行きたいと思います
guest

0

最初のSELECT文だけしか見ていません。
少し見易く整形すると読む気に慣れます。

SQL

1select 2 message0_.message_id as message_1_4_ 3 , message0_.message_content as message_2_4_ 4 , message0_.message_time as message_3_4_ 5 , message0_.receiver_user_id as receiver4_4_ 6 , message0_.sender_user_id as sender_u5_4_ 7from message message0 8left outer join user user1_ 9on message0_.receiver_user_id = user1_.user_id 10where user1_.user_id = ?

from message message0

message というテーブルに message0 という別名を付けていますが、
selectの並びや on には message0_ と最後にアンダーバーが付いた存在しない別名を使っているからエラーになります。

エラーにならなくても as message_1_4_ って列の別名にことさら可読性を低めてバグを生みやすくするのは止めたほうが良いです。ネーミングのセンスを疑われます。

投稿2020/03/15 08:06

Orlofsky

総合スコア16415

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

encho

2020/03/15 08:09

ありがとうございます。 repositoryを通して発行しているSQL文なので 勝手に命名が行われているという状態です。 テーブルと連携して発行していると思うのでここに問題があるというよりかは 他の部分に問題があるように考えていたのですが...
Orlofsky

2020/03/20 10:13

以前、全ての項目が連番、ってシステムからデータをもらっていたサブシステムで5%くらいだったか、本来の区分に設定されていけないデータがとんでもない区分で渡されて、連番が大好きな担当者は自分たちのバグを認めないことで解決したことに。もらっているデータ修復のためだけで月70~100時間の無駄な作業を強いられたことがあります。 同じようなことにならない事を祈ります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問