94871


参考書を元にJavaを勉強しており、コードからデータベースへの登録をしようとしたのですが、登録されません。

コンソールで登録完了の文字の表示はされているのですが、問題のPostgreSQLへの登録が出来ていません。  
これは登録のコードの問題でしょうか。それともバージョンなどによる違いでしょうか？

バージョンは参考書に合わせて以下の通りです。  
Java 11  
PostgreSQL 12

### コード

・application.properties

[code]

    spring.datasource.driver-class-name=org.postgresql.Driver
    spring.datasource.url=jdbc:postgresql://localhost:5432/quizdb
    spring.datasource.username=postgres
    spring.datasource.password=postgres
    
[/code]

・登録を行うコード  
QuizApplication.java

[code]

    package com.example.quiz;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.example.quiz.entity.Quiz;
    import com.example.quiz.repository.QuizRepository;
    
    @SpringBootApplication
    public class QuizApplication {
        /** 起動メソッド */
        public static void main(String[] args) {
            SpringApplication.run(QuizApplication.class, args)
            .getBean(QuizApplication.class).execute();
        }
        /** 注入 */
        @Autowired
        QuizRepository repository;
        /** 実行メソッド */
        private void execute() {
            //登録処理
            //setup();
            //全件取得
            showList();
            //１件取得
            //showOne();
            //更新処理
            //updateQuiz();
            //削除処理
            //deleteQuiz();
        }
        /** === クイズを２件登録します === */
        private void setup() {
            //エンティティ生成
            Quiz quiz1 = new Quiz(null, "「Spring」はフレームワークですか？", true, "登録太郎");
            //登録実行
            quiz1 = repository.save(quiz1);
            //登録確認
            System.out.println("登録したデータは、" + quiz1 + "です。");
            //エンティティ生成
            Quiz quiz2 = new Quiz(null, "「Spring MVC」はバッチ処理機能を提供しますか？", false, "登録太郎");
            //登録実行
            quiz2 = repository.save(quiz2);
            //登録確認
            System.out.println("登録したデータは、" + quiz2 + "です。");
        }
        
        /** === 前権取得 === */
        private void showList() {
            System.out.println("--- 全件取得開始 ----");
            // リポジトリを仕様して前権取得を実施、結果を取得
            Iterable<Quiz> quizzes = repository.findAll();
            for (Quiz quiz : quizzes) {
                System.out.println(quiz);
            }
            System.out.println("--- 全件取得完了 ---");
        }
        
        /** === １件取得 === */
        private void showOne() {
            System.out.println("--- 1件取得開始 ---");
            //リポジトリを使用して１件取得を実施、結果を取得(戻り値はOptional)
            Optional<Quiz> quizOpt = repository.findById(1);
            //値存在チェック
            if (quizOpt.isPresent()) {
                System.out.println(quizOpt.get());
            } else {
                System.out.println("該当する問題が存在しません・・・");
            }
            System.out.println("--- １件取得完了 ---");
        }
        
        /** === 更新処理 === */
        private void updateQuiz() {
            System.out.println("--- 更新処理開始 ---");
            //更新したいエンティティを生成する
            Quiz quiz1 = new Quiz(1, "「スプリング」はフレームワークですか？", true, "変更タロウ");
            //更新実行
            quiz1 = repository.save(quiz1);
            //更新確認
            System.out.println("更新したデータは、" + quiz1 + "です。");
            System.out.println("--- 更新処理完了 ---");
        }
        
        /** === 削除処理 === */
        private void deleteQuiz() {
            System.out.println("--- 削除処理開始 ---");
            // 削除実行
            repository.deleteById(4);
            System.out.println("--- 削除処理完了 ---");
        }
        
        @RestController
        public class MyController {
    
            @Autowired
            private QuizRepository quizRepository;
    
            @GetMapping
            List<Quiz> index() {
                Iterable<Quiz> quizList = quizRepository.findAll();
                ArrayList<Quiz> result = new ArrayList<>();
                quizList.forEach(result::add);
                return result;
            }
        }
    }
    
[/code]

・Quiz.java

[code]

    package com.example.quiz.entry;
    
    import org.springframework.data.annotation.Id;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    /** quizテーブル用：Entity */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Quiz {
        /** 識別ID */
        @Id
        private Integer id;
        /** クイズの内容 */
        private String question;
        /** クイズの解答 */
        private Boolean answer;
        /** 作成者 */
        private String author;
    }
    
[/code]

・実行時の表示

[code]

    2023-05-17T18:41:07.946+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mcom.example.quiz.QuizApplication        [0;39m [2m:[0;39m No active profile set, falling back to 1 default profile: "default"
    [2m2023-05-17T18:41:07.988+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36m.e.DevToolsPropertyDefaultsPostProcessor[0;39m [2m:[0;39m Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
    [2m2023-05-17T18:41:07.989+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36m.e.DevToolsPropertyDefaultsPostProcessor[0;39m [2m:[0;39m For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
    [2m2023-05-17T18:41:08.477+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
    [2m2023-05-17T18:41:08.518+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36m.s.d.r.c.RepositoryConfigurationDelegate[0;39m [2m:[0;39m Finished Spring Data repository scanning in 38 ms. Found 1 JDBC repository interfaces.
    [2m2023-05-17T18:41:08.936+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat initialized with port(s): 8080 (http)
    [2m2023-05-17T18:41:08.945+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.apache.catalina.core.StandardService  [0;39m [2m:[0;39m Starting service [Tomcat]
    [2m2023-05-17T18:41:08.945+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.apache.catalina.core.StandardEngine   [0;39m [2m:[0;39m Starting Servlet engine: [Apache Tomcat/10.1.8]
    [2m2023-05-17T18:41:08.994+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.a.c.c.C.[Tomcat].[localhost].[/]      [0;39m [2m:[0;39m Initializing Spring embedded WebApplicationContext
    [2m2023-05-17T18:41:08.994+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mw.s.c.ServletWebServerApplicationContext[0;39m [2m:[0;39m Root WebApplicationContext: initialization completed in 1005 ms
    [2m2023-05-17T18:41:09.088+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Starting...
    [2m2023-05-17T18:41:09.215+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mcom.zaxxer.hikari.pool.HikariPool       [0;39m [2m:[0;39m HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@6df0cd48
    [2m2023-05-17T18:41:09.217+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mcom.zaxxer.hikari.HikariDataSource      [0;39m [2m:[0;39m HikariPool-1 - Start completed.
    [2m2023-05-17T18:41:09.345+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.b.d.a.OptionalLiveReloadServer      [0;39m [2m:[0;39m LiveReload server is running on port 35729
    [2m2023-05-17T18:41:09.709+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.b.w.embedded.tomcat.TomcatWebServer [0;39m [2m:[0;39m Tomcat started on port(s): 8080 (http) with context path ''
    [2m2023-05-17T18:41:09.719+09:00[0;39m [32m INFO[0;39m [35m14592[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mcom.example.quiz.QuizApplication        [0;39m [2m:[0;39m Started QuizApplication in 2.067 seconds (process running for 2.728)
    登録したデータは、Quiz(id=1, question=「Spring」はフレームワークですか？, answer=true, author=登録太郎)です。
    登録したデータは、Quiz(id=2, question=「Spring MVC」はバッチ処理機能を提供しますか？, answer=false, author=登録太郎)です。
    --- 全件取得開始 ----
    Quiz(id=1, question=「Spring」はフレームワークですか？, answer=true, author=登録太郎)
    Quiz(id=2, question=「Spring MVC」はバッチ処理機能を提供しますか？, answer=false, author=登録太郎)
    --- 全件取得完了 ---
    
[/code]


CC BY-SA 4.0
2023-05-02T16:43:13.193
94722
2023-05-18T06:39:41.980
2023-05-18T06:39:41.980
58130
58130
question
java postgresql spring-boot
データベースに登録できません


質問文から予想すると、commitしていないため、プログラム終了時にrollbackが行われ、結果としてデータが登録されていないように予想します。

`TransactionManager`を用いて明示的に`commit()`するか、`@Transactional`を付与したpublicメソッドを持つクラスにデータ登録処理を実装し直すと、データが登録されるようになると思います。(`@Transactional`を付与したメソッドを抜ける際にcommitされる)

参考にされている書籍だと、`@Transactional`を用いた方法が紹介されているのではないかと思います。

＃参考までに、机上調査の限りですが、`@Transactional`を期待通り動作させるためには、(`@Service`を付与した)Injectしたクラスのpublicメソッドである必要があるそうです。(そのクラスかpublicメソッドに`@Transactional`を付与する必要があるそうです)

* * *

### 追記

回答のコメントに対し、追記します。

> TransactionManagerはQuizApplication.javaの@Autowired以下に記述していくのが適切でしょうか？

Inject(`@Autowired`)の位置、という意味であれば、`QuizApplication`クラスのフィールド定義可能箇所であればどこでも良いと思います。`QuizRepository`の次に記述しても良いです。(その際、`@Autowired`アノテーションを忘れずに)

> データベースと繋ぐ設定のapplication.propertiesのコードは間違いという可能性があるでしょうか。

提示された内容を見る限り、明らかに間違っている点はないように見受けます。

＃認識した内容は以下の通り。

  * 接続ドライバは「`org.postgresql.Driver`」
  * `localhost`の`5432`ポートに接続。
  * 接続先データベースは`quizdb`
  * 接続ユーザは`postgres`。パスワードは`postgres`。


CC BY-SA 4.0
2023-05-03T11:07:03.310
94728
2023-05-06T01:19:46.337
2023-05-06T01:19:46.337
20098
20098
94722
answer


前述の内容で事象が解消しないのであれば、SQL文が期待通り発行されているか調査して、調査結果に応じてソースコードの修正等を行う必要があると思います。

SprintBootプログラムにてSQLログを出力して確認する方法と、PostgreSQLにてSQLログを出力して確認する方法があると考えますが、確実な個所としてPostgreSQLの方で確認するのはいかがでしょうか?

具体的には、`postgresql.conf`に`log_statement =
'all'`と記述してPostgreSQLを再起動することで、以降発行されるSQL文がPostgreSQLのログファイルに記録されるようになります。

＃その中に、`INSERT`文や`COMMIT`文があるかが判断基準になると思います。  
＃設定ができたかどうかの確認は、`psql`コマンドで`select * from quiz;`等実行してログに出るか確認すれば良いと思います。


CC BY-SA 4.0
2023-05-14T10:06:28.887
94842
2023-05-14T10:06:28.887
20098
94722
answer


(途中で例外が発生せずに) "`登録したデータは、(中略)です。`" というメッセージが出ているのであれば、おそらく正常に登録できています。  
つまり、確認方法に何らかの誤りがあることが考えられます。  
(どう誤っているかは質問文に情報が無いのでわかりません)

例えば次のようなコントローラでデータアクセスすると登録されているべきデータが帰ってくるのではないでしょうか。

[code]

    @RestController
    public class MyController {
    
        @Autowired
        private QuizRepository quizRepository;
    
        @GetMapping
        List<Quiz> index() {
            Iterable<Quiz> quizList = quizRepository.findAll();
            ArrayList<Quiz> result = new ArrayList<>();
            quizList.forEach(result::add);
            return result;
        }
    }
    
    
[/code]

( http://localhost:8080/ にアクセスする)

あるいは、書籍中の解説で、質問文中にある `showList()` メソッドを用いて確認しているような記述があるかもしれませんので探してみてください。

* * *

当初から質問文中のコードが変わっているようですが、元のコード(書籍にある通りのコード)で試してください。  
現在の質問文中にある例外は、元のコードでは発生していなかったはずです。

* * *

`QuizRepository` は [Spring Data JDBC](https://docs.spring.io/spring-
data/jdbc/docs/current/reference/html/) を利用した実装になっており、

[code]

    //登録実行
    quiz1 = repository.save(quiz1);
    
[/code]

で呼ばれている`save()`メッソドの実体は[`SimpleJdbcRepository`](https://github.com/spring-
projects/spring-data-relational/blob/3.1.0/spring-data-
jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java#L65-L69)にあります。

[code]

        @Transactional
        @Override
        public <S extends T> S save(S instance) {
            return entityOperations.save(instance);
        }
    
[/code]

このメソッドには `@Transactional` アノテーションが付与されているので、元のコードのままでもトランザクション内で実行されます。

* * *

([コメント](https://ja.stackoverflow.com/questions/94722/%e3%83%87%e3%83%bc%e3%82%bf%e3%83%99%e3%83%bc%e3%82%b9%e3%81%ab%e7%99%bb%e9%8c%b2%e3%81%a7%e3%81%8d%e3%81%be%e3%81%9b%e3%82%93/94871?noredirect=1#comment108497_94871)を受けて追記)

今の時点で確認すべきことは2点です。

  1. 上記の `MyController` 実装をアプリケーションに追加して実行し、 http://localhost:8080/ にアクセスしてみてください。DBに登録できているのであればそのデータが返ってくるはずです。
  2. 書籍に、保存したデータの確認方法が説明されている可能性がありますのでそれを探してみてください。おそらく `showList()` メソッドが利用されているはずです。


CC BY-SA 4.0
2023-05-16T17:33:13.323
94871
2023-05-17T09:25:21.720
2023-05-17T09:25:21.720
2808
2808
94722
answer