Spring Data JDBCの使い方 – 【Java】
前提
項目 | 値 |
---|---|
プロジェクト | Gradleプロジェクト |
Spring Boot | 3.1.3 |
Language | 17 |
Spring Data JDBCがサポートしているDBです。
- DB2
- H2
- HSQLDB
- MariaDB
- Microsoft SQL Server
- MySQL
- Oracle
- Postgres
build.gradle
build.gradleで1行追加します。
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
CrudRepository
org.springframework.data.repository.CrudRepository
をimplementしたインタフェースを定義します。
findAll()やfindById()、save()やcount()など基本的なCRUDのメソッドが用意されています。
saveAll(),findAll(),findAllById()がIterableを返します。
ListCrudRepository ※ver3以降
org.springframework.data.repository.ListCrudRepository
をimplementしたインタフェースを定義します。
findAll()やfindById()など基本的なCRUDのメソッドが用意されています。
saveAll(),findAll(),findAllById()がListを返します。
PagingAndSortingRepository
ページングをしたい場合はorg.springframework.data.repository.PagingAndSortingRepository
をimplementしたインタフェースを定義します。
findAll()で全検索します。
PagingAndSortingRepository<Employee, Long> repository; Page<Employee> employee = repository.findAll(PageRequest.of(1, 20));
引数にorg.springframework.data.domain.PageRequest
を指定してページングします。
PageRequest.of(ページ数,ページサイズ);
引数 | 値 |
---|---|
第一引数 | ページ数(0基底) |
第二引数 | ページサイズ |
@Query
@Queryアノテーション(org.springframework.data.jdbc.repository.query.Query)を使用してnative queryを使用します。
@Query("SELECT * FROM EMPLOYEE WHERE NAME = :name") List xxx(@Param("name") String name);
Springはorg.springframework.data.repository.query.Param
を省略しても良いようです。
Spring fully supports Java 8’s parameter name discovery based on the -parameters compiler flag. By using this flag in your build as an alternative to debug information, you can omit the @Param annotation for named parameters.
メソッド名からSQLを生成する ※Selectのみ
JPAだとfindByXXXのようにメソッド名からSQLを生成する機能があります。
同様の機能がSpring Data JDBCでもありますが、Select文だけサポートされているようです。
Deriving a query from the name of the method is is currently limited to simple properties, that means properties present in the aggregate root directly. Also, only select queries are supported by this approach.
save()メソッド
save()メソッドでインサートとアップデートを行います。@Idを付与したPKがnullの場合、インサートし、設定している場合、その値のデータをアップデートします。
テーブル側のDDLでPKはauto incrementされている必要があります。
ナチュラルキーにしたい場合はPersistableインタフェースをimplementしてisNew()メソッドをオーバーライドして判断する必要があります。
save()メソッド ※Persistable
エンティティにorg.springframework.data.domain.Persistableをimplementsさせて2つのメソッドをオーバーライドします。
- getId()
- isNew()
isNew()メソッドでsave()がインサートかアップデートかをコントロールします。
@Table("USER") @Getter @AllArgsConstructor public class XxxEntity implements Persistable { @PersistenceCreator public XxxEntity(Long id, String name, Integer age) { this.id = id; this.name= name; this.age= age; } @Id @Column("ID") Long id; @Column("NAME") String name; @Column("AGE") Integer age; @Transient boolean isNew; @Override @Nullable public Long getId() { return id; } @Override public boolean isNew() { return isNew; } }
isNewフィールドには@Transientアノテーションを付与することにより永続化対象外のフィールドにします。
コンストラクタに@PersistenceCreatorアノテーションを付与することにより、Spring Data JDBCに使用させるコンストラクタを指定します。※@PersistenceConstructorアノテーションは非推奨
データ生成は@AllArgsConstructorで自動生成したコンストラクタを使用し、isNewにtrue or falseを渡してインサート or アップデートをコントロールします。
XxxEntity entity = new XxxEntity(1L, "tani", 20, true); // trueなのでinsert repo.save(entity); // insertされる XxxEntity entity = new XxxEntity(1L, "tani", 20, false); // falseなのでupdate repo.save(entity); // updateされる
isNew()メソッドを使う場合はPKはauto incrementされている必要がありません。
SQLログ
SQLログをコンソール出力する場合は、application.ymlに以下追記します。
logging: level: org: springframework: jdbc: core: JdbcTemplate: TRACE
1対1のEntity
1対1のテーブルをEntityで表します。
H2のDDLです。
CREATE TABLE PARENT( id INTEGER AUTO_INCREMENT PRIMARY KEY, parent_name TEXT NOT NULL ); CREATE TABLE CHILD( id INTEGER AUTO_INCREMENT PRIMARY KEY, child_name TEXT NOT NULL, parent_id INTEGER NOT NULL, FOREIGN KEY (parent_id) REFERENCES PARENT(id) );
この親子テーブルをエンティティで表します。
Parentテーブルです。
package jp.co.confrage.demo.domain.entity; import org.springframework.data.annotation.Id; import org.springframework.data.relational.core.mapping.Column; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class Parent { @Id Long id; String parentName; @Column("PARENT_ID") Child child; }
Childテーブルです。PKやFKの定義は不要です。
package jp.co.confrage.demo.domain.entity; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class Child { String childName; }
Parentのリポジトリインタフェースです。
package jp.co.confrage.demo.infrastructure.repository; import org.springframework.data.repository.ListCrudRepository; import jp.co.confrage.demo.domain.entity.Parent; public interface ParentRepository extends ListCrudRepository<Parent, Long> { }
コントローラを作成してsaveメソッドでインサートします。
package jp.co.confrage.demo.presentation.controller; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import jp.co.confrage.demo.domain.entity.Child; import jp.co.confrage.demo.domain.entity.Parent; import jp.co.confrage.demo.infrastructure.repository.ParentRepository; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor public class DemoController { private final ParentRepository repo; @RequestMapping(path = "/test", method = RequestMethod.GET) public ResponseEntity<?> read(@RequestHeader HttpHeaders headers) throws Exception { Child child = new Child("takahashi"); Parent parent = new Parent(null, "yamada", child); repo.save(parent); return new ResponseEntity<>(null, headers, HttpStatus.OK); } }
Parentテーブル、Childテーブルに1レコードずつインサートされます。
監査ログ
登録者、登録日、更新者、更新日をアノテーションを使用してinsert/update時に自動で設定することができます。
以下のクラスを作成します。
package jp.co.confrage.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing; @EnableJdbcAuditing() @Configuration public class AuditLogConfig {}
DDLに登録日、更新日を追加します。
CREATE TABLE PARENT( id INTEGER AUTO_INCREMENT PRIMARY KEY, parent_name TEXT NOT NULL, created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE CHILD( id INTEGER AUTO_INCREMENT PRIMARY KEY, child_name TEXT NOT NULL, parent_id INTEGER NOT NULL, created_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (parent_id) REFERENCES PARENT(id) );
エンティティに日付を追加します。
Parentテーブルです。
package jp.co.confrage.demo.domain.entity; import java.time.LocalDateTime; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.relational.core.mapping.Column; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class Parent { @Id Long id; String parentName; @Column("PARENT_ID") Child child; @CreatedDate LocalDateTime createdDate; @LastModifiedDate LocalDateTime updatedDate; }
Childテーブルです。
package jp.co.confrage.demo.domain.entity; import java.time.LocalDateTime; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import lombok.AllArgsConstructor; import lombok.Data; @AllArgsConstructor @Data public class Child { String childName; @CreatedDate LocalDateTime createdDate; @LastModifiedDate LocalDateTime updatedDate; }
コントローラでsave()メソッドで登録・更新します。登録日、更新日はnull設定すれば自動で設定してくれます。
Child child = new Child("takahashi", null, null); Parent parent = new Parent(null, child, "yamada", null, null); repo.save(parent);
参考サイト
KHI入社して退社。今はCONFRAGEで正社員です。関西で140-170/80~120万から受け付けております^^
得意技はJS(ES6),Java,AWSの大体のリソースです
コメントはやさしくお願いいたします^^
座右の銘は、「狭き門より入れ」「願わくは、我に七難八苦を与えたまえ」です^^
コメント