背景・目的
以前、Sprint Bootの環境を構築したときのメモでは、Spring Bootの環境を構築しました。
今回は、Spring Bootからデータベースへのアクセスを試してみます。
まとめ
下記に特徴を記載いたします
特徴 | 説明 |
---|---|
Spring Data JPA | JPA(Java Persistence API)ベース リポジトリを簡単に開発できる データアクセステクノロジーを使ってSpring アプリケーションの構築が簡単にできる 目的は、データアクセスレイヤの実装を減らし改善すること 開発者は、様々な技術を使い、リポジトリインタフェイスを記述することで、Springは自動で接続できる |
JPA | Java Persistence APIの略。 オブジェクトリレーションマッピングのためのPOJO 永続モデルを提供している EJBソフトウェアコンポーネントに限定されない Webアプリケーション、アプリケーションクライアント、Java EE、Java SEなどでも利用できる |
JPAの主要コンポーネント | ・Object Releational Mapping ・Entity Manager API ・Transaction ・JPQL |
概要
Accessing Data with JPAと、gs-accessing-data-jpaを基に整理します。
Spring Data JPAとは?
そもそも、Spring Data JPAとは何でしょうか。Spring Data JPAを基に整理します。
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA-based (Java Persistence API) repositories. It makes it easier to build Spring-powered applications that use data access technologies.
- Spring Data familyの一部
- JPA(Java Persistence API)ベース リポジトリを簡単に開発できる
- データアクセステクノロジーを使ってSpring アプリケーションの構築が簡単にできる
Implementing a data access layer for an application can be quite cumbersome. Too much boilerplate code has to be written to execute the simplest queries. Add things like pagination, auditing, and other often-needed options, and you end up lost.
- アプリケーションのデータアクセスレイヤーを実装するのは面倒
- 単純なクエリを実行するために大量の定型コードを書く必要がある
- ページネイション、監査、その他必須のオプションなど、大変(途方に暮れる)
Spring Data JPA aims to significantly improve the implementation of data access layers by reducing the effort to the amount that’s actually needed. As a developer you write your repository interfaces using any number of techniques, and Spring will wire it up for you automatically. You can even use custom finders or query by example and Spring will write the query for you!
- Spring Data JPAの目的は、データアクセスレイヤの実装を減らし改善すること
- 開発者は、様々な技術を使い、リポジトリインタフェイスを記述することで、Springは自動で接続できる
- カスタムファインダーを使用したり、クエリ例により、Springがクエリを記述する
バージョン
バージョンページによると、2024/7/13現在では、3.3が最新のようです。
JPAとは?
Java Persistence APIをページを基に整理します。
The Java Persistence API provides a POJO persistence model for object-relational mapping. The Java Persistence API was developed by the EJB 3.0 software expert group as part of JSR 220, but its use is not limited to EJB software components. It can also be used directly by web applications and application clients, and even outside the Java EE platform, for example, in Java SE applications. See JSR 220.
- Java Persistence API(以降、JPAと書きます。)は、オブジェクトリレーションマッピングのためのPOJO 永続モデルを提供している
- JPAは、EJB3.0のエキスパートグループより、JSR220の一部として開発されたが、EJBソフトウェアコンポーネントに限定されない
- Webアプリケーション、アプリケーションクライアント、Java EE、Java SEなどでも利用できる
2011年頃の資料と思われ、若干古そうですが、Java Persistence API の詳細ついてという、Oracle社の資料が分かりやすかったので、以降は、こちらを基に整理します。
JPAの主要コンポーネント
- Object Releational Mapping
- ORM
- JavaオブジェクトとDBテーブルのマッピング
- Entity Manager API
- Entityのクラウド操作を実行
- Transaction
- 複数を処理を単一処理としてまとめる。いわゆるトランザクション
- JPQL
- Java Presistence Query Language
- オブジェクト指向のクエリ
実践
Accessing Data with JPAを基に試します。
前提
下記を前提としています。
- Mac
- VSCode
- Java 17
- Maven 3.9.8
プロジェクトの作成
- VSCodeを起動します
- 表示>コマンドパレットをクリックします
- 下記を選択します
- Spring Initializrを選択し、3.3.1
- Java
- GroupIdを指定します
- ArtifactIdを指定します
- Package TypeにJarを指定します
- Javaバージョンに17を指定します
- DependencyにSpring Webを指定します
- 作成後、Add Workspaceをクリックします
- 追加されました
pom.xmlの修正
- dependenciesに下記を追加します
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
エンティティを定義する
-
下記のとおり追加します
package com.example.data.jpa; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String firstName; private String lastName; protected Customer() {} public Customer(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
-
@Entity
- JPAエンティティとして認識される
-
@Id
- idプロパティに設定されており、これがオブジェクトIDとして認識される
-
@GeneratedValue
- IDが自動的に生成される
-
シンプルなクエリを作成する
Spring Data JPAは、RDBにデータを保存することに重点を置いている。
実行時に、リポジトリI/Fからリポジトリ実装を自動的に作成できる。
-
下記のコードを追加します
package com.example.data.jpa; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface CustomerRepository extends CrudRepository<Customer, Long>{ List<Customer> findByLastName(String lastName); Customer findById(long id); }
アプリケーションクラスを作成する
- 下記を追加します
package com.example.data.jpa; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @SpringBootApplication public class JpaApplication { private static final Logger log = LoggerFactory.getLogger(JpaApplication.class); public static void main(String[] args) { SpringApplication.run(JpaApplication.class, args); } @Bean public CommandLineRunner demo(CustomerRepository repository){ return(args)-> { // save a few customers repository.save(new Customer("Jack", "Bauer")); repository.save(new Customer("Chloe", "O'Brian")); repository.save(new Customer("Kim", "Bauer")); repository.save(new Customer("David", "Palmer")); repository.save(new Customer("Michelle", "Dessler")); // fetch all customers log.info("Customers found with findAll():"); log.info("-------------------------------"); repository.findAll().forEach(customer -> { log.info(customer.toString()); }); log.info(""); // fetch an individual customer by ID Customer customer = repository.findById(1L); log.info("Customer found with findById(1L):"); log.info("--------------------------------"); log.info(customer.toString()); log.info(""); // fetch customers by last name log.info("Customer found with findByLastName('Bauer'):"); log.info("--------------------------------------------"); repository.findByLastName("Bauer").forEach(bauer -> { log.info(bauer.toString()); }); log.info(""); }; } }
JARをビルドする
-
下記のコマンドによりビルドします
$ mvn clean install $ ls -l target/jpa-0.0.1-SNAPSHOT.jar -rw-r--r-- 1 XXXX XXX 47716176 7 13 17:01 target/jpa-0.0.1-SNAPSHOT.jar $
-
下記のコマンドで実行します
$ java -jar target/jpa-0.0.1-SNAPSHOT.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.3.1) 2024-07-13T17:03:27.197+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Starting JpaApplication v0.0.1-SNAPSHOT using Java 17.0.11 with PID 7825 (/Users/XXXXX/git/spring/jpa/target/jpa-0.0.1-SNAPSHOT.jar started by XXXX in /Users/XXXX/git/spring/jpa) 2024-07-13T17:03:27.202+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : No active profile set, falling back to 1 default profile: "default" 2024-07-13T17:03:28.063+09:00 INFO 7825 --- [jpa] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2024-07-13T17:03:28.142+09:00 INFO 7825 --- [jpa] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 66 ms. Found 1 JPA repository interface. 2024-07-13T17:03:28.776+09:00 INFO 7825 --- [jpa] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2024-07-13T17:03:28.790+09:00 INFO 7825 --- [jpa] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2024-07-13T17:03:28.790+09:00 INFO 7825 --- [jpa] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.25] 2024-07-13T17:03:28.854+09:00 INFO 7825 --- [jpa] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2024-07-13T17:03:28.856+09:00 INFO 7825 --- [jpa] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1536 ms 2024-07-13T17:03:29.004+09:00 INFO 7825 --- [jpa] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2024-07-13T17:03:29.332+09:00 INFO 7825 --- [jpa] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:b43ea3f2-01c5-457a-9ffd-20e2cab76853 user=SA 2024-07-13T17:03:29.333+09:00 INFO 7825 --- [jpa] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2024-07-13T17:03:29.408+09:00 INFO 7825 --- [jpa] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2024-07-13T17:03:29.507+09:00 INFO 7825 --- [jpa] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final 2024-07-13T17:03:29.564+09:00 INFO 7825 --- [jpa] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 2024-07-13T17:03:29.986+09:00 INFO 7825 --- [jpa] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2024-07-13T17:03:30.901+09:00 INFO 7825 --- [jpa] [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2024-07-13T17:03:30.946+09:00 INFO 7825 --- [jpa] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2024-07-13T17:03:31.306+09:00 WARN 7825 --- [jpa] [ 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 2024-07-13T17:03:31.647+09:00 INFO 7825 --- [jpa] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2024-07-13T17:03:31.667+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Started JpaApplication in 5.098 seconds (process running for 5.595) 2024-07-13T17:03:31.772+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customers found with findAll(): 2024-07-13T17:03:31.772+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : ------------------------------- 2024-07-13T17:03:31.883+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=1, firstName='Jack', lastName='Bauer'] 2024-07-13T17:03:31.884+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=2, firstName='Chloe', lastName='O'Brian'] 2024-07-13T17:03:31.884+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=3, firstName='Kim', lastName='Bauer'] 2024-07-13T17:03:31.884+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=4, firstName='David', lastName='Palmer'] 2024-07-13T17:03:31.884+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=5, firstName='Michelle', lastName='Dessler'] 2024-07-13T17:03:31.884+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer found with findById(1L): 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : -------------------------------- 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=1, firstName='Jack', lastName='Bauer'] 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer found with findByLastName('Bauer'): 2024-07-13T17:03:31.896+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : -------------------------------------------- 2024-07-13T17:03:31.951+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=1, firstName='Jack', lastName='Bauer'] 2024-07-13T17:03:31.951+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication : Customer[id=3, firstName='Kim', lastName='Bauer'] 2024-07-13T17:03:31.951+09:00 INFO 7825 --- [jpa] [ main] com.example.data.jpa.JpaApplication :
- findoAllで、saveにより登録したCustomerのすべてを見つける
- findByIdでは、CustomerRepositoryのメソッドを呼び出す
- findByLastNameでLastNameをキーに探し出す
考察
Spring Data JPA により、オブジェクトを簡単にデータベースに保存することができました。次回以降も継続して試してみます。
参考