Angular, Spring, PostgreSQL(11)Springで検索処理を作る(実行編)

前回(No.10の記事)は、SpringのMySQL向け入門コンテンツを流用して、PostgreSQLへテーブルアクセスする、簡単なプログラムを組みました。

当然、動作確認までしなければ意味がないわけですが、ちゃんと動くまでには多少、いろいろあったので、今回はそのあたりをまとめておこうと思います。

※というか、以外とこっちのほうが(動かない>直す>動いた!という経験が)が大事

スポンサーリンク

最初はエラーばっかり

まあ最初なので、一発で動くなんてことはありえないわけで、少々はまりました。

DB接続エラー

最初の「application.properties」の記載は以下のとおり。

spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/ttsdb
spring.datasource.username=ttsuesr
spring.datasource.password=ttsuesr
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

PostgreSQLに接続成功している方々の定義をお借りしてきたりしているので、基本的に間違っていないはずなのだけれど、接続エラーが発生。

org.postgresql.util.PSQLException: FATAL: ユーザ"ttsuser"のパスワード認証に失敗しました(pgjdbc: server-encoding として windows-31j  を自動検出しました、メッセージが読めない場合はデータベースログおよび host, port, dbname, user, password, pg_dba.conf を確認してください)

なにがいけないのだろう?

前途多難を予感しながら素直な心で全てを疑ってみる。
認証失敗って言ってるんだから、認証までは行いにいってるよね?

ということは、「ttsuser」?、あ、「ttsusr」だ!

spring.datasource.username=ttsuser
  <<ttusrに直す
spring.datasource.password=ttsuser  <<ttusrに直す

単純に思い込みで間違っていた、というパターンですね。

こういうのって、以外と良くあるよね?(いや、自分だけかも?)、そしてエラーはまだ続く。

findByメソッドが作れない?

当初、カテゴリ・テーブルのカラム名は、以下のように定義していました。

「ca_id」「ca_name」等、 ”_”(アンダースコア)で、単語・用語を繋げていくパターン。

「スネークケース」と言われているやつ。

最初の部分は「category」は長いので(正しくはcategoriesかもだけど)シンプルに「ca」で。

なので当初の「Category.java」は、以下のとおり。

package com.chankazu.tts.accessingdatapostgresql;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity // This tells Hibernate to make a table out of this class
public class Category {

  @Id
  private Long ca_id;

  private String ca_name;

  protected Category() {}

  public Category(Long id, String name) {
    this.ca_id = id;
    this.ca_name = name;
  }

  @Override
  public String toString() {
    return String.format(
        "Category[id=%d, name='%s']",ca_id, ca_name);
  }

  public Long getCa_id() {
    return ca_id;
  }

  public void setCa_id(Long id) {
    this.ca_id = id;
  }

  public String getCa_name() {
    return ca_name;
  }

  public void setCa_name(String name) {
    this.ca_name = name;
  }

}

「ca_id」、「ca_name」という、Java側とテーブル側の定義が同じ状態。

この状態なので、「CategoryRepository」に定義するfindメソッドは以下にしました。

package com.chankazu.tts.accessingdatapostgresql;

import java.util.List;
import org.springframework.data.repository.CrudRepository;

public interface CategoryRepository extends CrudRepository<Category, Integer> {

  List<Category> findAll();

  Category findByCa_id(Long id);
}

けどこの状態だと、実行すると以下のエラーが発生。

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-12-28 09:29:19.512 ERROR 29860 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'categoryRepository' defined in com.chankazu.tts.accessingdatapostgresql.CategoryRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot resolve reference to bean 'jpaMappingContext' while setting bean property 'mappingContext'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaMappingContext': Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing column [ca_id] in table [category]

「ca_id」がダメ?、う~ん、よくわからんが、カラム名をうまく認識してくれないようなので、Java側もテーブル側も、名称はシンプルに「id」と「name」にしてみると、、、

通った!、これはつまり、 ”_”(アンダースコア)がだめなのか?

Spring Data JPAリポジトリメソッドは、アンダースコアを持つプロパティ名を認識しません

Spring Bootで変数名にスネークケースを使ってはまったこと

どうもそのようです。

試しに、テーブル側の定義を「ca_id」「ca_name」に戻して、Java側を「caId」「caName」にしてみたら、

package com.chankazu.tts.accessingdatapostgresql;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity // This tells Hibernate to make a table out of this class
public class Category {

  @Id
  private Long caId;

  private String caName;

  protected Category() {}

  public Category(Long id, String name) {
    this.caId = id;
    this.caName = name;
  }

  @Override
  public String toString() {
    return String.format(
        "Category[id=%d, name='%s']",caId, caName);
  }

  public Long getCaId() {
    return caId;
  }

  public void setCaId(Long id) {
    this.caId = id;
  }

  public String getCaName() {
    return caName;
  }

  public void setName(String name) {
    this.caName = name;
  }

}

通った、、、

つまり、Javaでの「caId」「caName」(キャメルケース)を、テーブル上の「ca_id」「ca_name」(スネークケース)に合わせようとするわけね、Spring JPAくんは。

う~ん、これは、便利なのだろうか?
余計なお世話のような気もするが。

こうした理由が知りたい。また、回避する方法もありそうだけれど、まずは勝手に変換されてしまうのを避けるため、テーブルのカラム名は、「id」「name」とシンプルにして、Java側もそれに倣うように、揃えるようにしました。

スポンサーリンク

BootRunで実行

Gradleタスク・ビューをみてみると、

「このプロジェクトを Spring Boot アプリケーションとして実行します。」というのがあるので、当初これで実行。

 > Task :bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)


・・・
・・・
 (途中省略)
・・・   
・・・
2020-12-28 11:05:35.504 DEBUG 24016 --- [           main] org.hibernate.SQL                        : 
    select
        category0_.ca_id as ca_id1_0_,
        category0_.ca_name as ca_name2_0_ 
    from
        category category0_
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=2, name='Brake']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=1, name='Engine
']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=3, name='Muffler']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=102, name='AAA102']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=103, name='AAA103']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=104, name='AAA104']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=105, name='AAA105']
2020-12-28 11:05:35.519  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : 
2020-12-28 11:05:35.536 DEBUG 24016 --- [           main] org.hibernate.SQL                        : 
    select
        category0_.ca_id as ca_id1_0_,
        category0_.ca_name as ca_name2_0_ 
    from
        category category0_ 
    where
        category0_.ca_id=?
2020-12-28 11:05:35.537 TRACE 24016 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [1]
2020-12-28 11:05:35.539  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category found with findById(1L):
2020-12-28 11:05:35.539  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : --------------------------------
2020-12-28 11:05:35.539  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=1, name='Engine
']
2020-12-28 11:05:35.539  INFO 24016 --- [           main] c.t.a.AccessingDataPostgresqlApplication : 

エラーで中断することがなくなって、ちゃんと動きます。カテゴリ・テーブルから Select してきているようだし、大丈夫そう。

そしてもう少しログをみてみると、起動時にSQLを生成するようです。

これって、毎回生成?、いっぱいあったら大変では?、という疑問がわいたが、とりあえず、放置しておきます。

そして、なんか、プログラムが終わらないっぽい。

自分で停止する必要がありそう。

ログの最初のほうをみてみると「Tomcat」が動いている。たぶんこれのせいですね。

2020-12-28 12:50:10.964  INFO 23176 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Starting AccessingDataPostgresqlApplication on MyComputer with PID 23176 (C:\workspace\tts\build\classes\java\main started by xxxxx in C:\workspace\tts)
2020-12-28 12:50:10.965  INFO 23176 --- [           main] c.t.a.AccessingDataPostgresqlApplication : No active profile set, falling back to default profiles: default
2020-12-28 12:50:11.350  INFO 23176 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-12-28 12:50:11.386  INFO 23176 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 31ms. Found 2 JPA repository interfaces.
2020-12-28 12:50:11.839  INFO 23176 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-12-28 12:50:11.846  INFO 23176 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-12-28 12:50:11.846  INFO 23176 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-12-28 12:50:11.918  INFO 23176 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-12-28 12:50:11.918  INFO 23176 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 926 ms
 

さらに、@SpringBootApplicationとか、@SpringBootTestとか、@Testとか、気になる指定(アノテーション)がいくつかあるけれど、それらは一旦おいておいて先に進みます。

JUnitで実行

そもそも、テストクラス「AccessingDataPostgresqlApplicationTests」は、JUnitっぽいのだから

package com.chankazu.tts.accessingdatapostgresql;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class AccessingDataPostgresqlApplicationTests {

	@Test
	void contextLoads() {
	}

}

「JUnitで実行」してみた。

うまくいったようです、ちゃんと終わります。


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.2.RELEASE)

2020-12-28 11:10:17.246  INFO 28756 --- [           main] .AccessingDataPostgresqlApplicationTests : Starting AccessingDataPostgresqlApplicationTests on MyComputer with PID 28756 (started by xxxxx in C:\pleiades\workspace\tts_old)
2020-12-28 11:10:17.247  INFO 28756 --- [           main] .AccessingDataPostgresqlApplicationTests : No active profile set, falling back to default profiles: default
2020-12-28 11:10:17.613  INFO 28756 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-12-28 11:10:17.659  INFO 28756 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 38ms. Found 1 JPA repository interfaces.
2020-12-28 11:10:18.060  INFO 28756 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-12-28 11:10:18.111  INFO 28756 --- [         task-1] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-12-28 11:10:18.156  INFO 28756 --- [         task-1] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.18.Final
2020-12-28 11:10:18.159  INFO 28756 --- [         task-1] org.hibernate.cfg.Environment            : HHH000205: Loaded properties from resource hibernate.properties: {hibernate.bytecode.use_reflection_optimizer=false, hibernate.jdbc.lob.non_contextual_creation=true}
2020-12-28 11:10:18.300  INFO 28756 --- [         task-1] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-12-28 11:10:18.335  WARN 28756 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-12-28 11:10:18.599  INFO 28756 --- [         task-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-12-28 11:10:18.710  INFO 28756 --- [         task-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-12-28 11:10:18.731  INFO 28756 --- [         task-1] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQL95Dialect
2020-12-28 11:10:18.843  INFO 28756 --- [           main] DeferredRepositoryInitializationListener : Triggering deferred initialization of Spring Data repositories…
2020-12-28 11:10:19.267  INFO 28756 --- [         task-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-12-28 11:10:19.276  INFO 28756 --- [         task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-12-28 11:10:19.483  INFO 28756 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-12-28 11:10:19.489  INFO 28756 --- [           main] .AccessingDataPostgresqlApplicationTests : Started AccessingDataPostgresqlApplicationTests in 2.447 seconds (JVM running for 3.236)
2020-12-28 11:10:19.490  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Categories found with findAll():
2020-12-28 11:10:19.490  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : -------------------------------
2020-12-28 11:10:19.598 DEBUG 28756 --- [           main] org.hibernate.SQL                        : 
    select
        category0_.ca_id as ca_id1_0_,
        category0_.ca_name as ca_name2_0_ 
    from
        category category0_
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=2, name='Brake']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=1, name='Engine
']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=3, name='Muffler']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=102, name='AAA102']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=103, name='AAA103']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=104, name='AAA104']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=105, name='AAA105']
2020-12-28 11:10:19.619  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : 
2020-12-28 11:10:19.648 DEBUG 28756 --- [           main] org.hibernate.SQL                        : 
    select
        category0_.ca_id as ca_id1_0_,
        category0_.ca_name as ca_name2_0_ 
    from
        category category0_ 
    where
        category0_.ca_id=?
2020-12-28 11:10:19.650 TRACE 28756 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [BIGINT] - [1]
2020-12-28 11:10:19.652  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category found with findById(1L):
2020-12-28 11:10:19.652  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : --------------------------------
2020-12-28 11:10:19.652  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : Category[id=1, name='Engine
']
2020-12-28 11:10:19.652  INFO 28756 --- [           main] c.t.a.AccessingDataPostgresqlApplication : 
2020-12-28 11:10:19.747  INFO 28756 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-12-28 11:10:19.748  INFO 28756 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
2020-12-28 11:10:19.749  INFO 28756 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2020-12-28 11:10:19.751  INFO 28756 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

完了しました。とりあえず、よしとします。

次回はコントローラーを作成

今回は、PostgreSQLのテーブル検索処理が出来ることを主眼にしたので、単なるアプリからの検索処理実行でした。

なので次回は、「コントローラー」から検索処理を呼び出す形にして、実際のアプリに近いスタイル(Web<>コントローラー<>エンティティ)にしてみます。

コメント

  1. […] 前回(No.11の記事)は、PostgreSQLのテーブルへアクセスする、簡単なプログラムを作成しました。 […]

タイトルとURLをコピーしました