實作一個基本資料庫存取功能的程式,需要寫大量的程式做資料庫的連接,甚至還需了解不同資料庫的語法,才能看到功能的雛形,而每多一個功能也需要做許多重複的事情。此篇介紹 Spring Data JPA 可以大幅降低資料庫存取功能的工作,讓開發人員更專注在商業邏輯上。
JPA(Java Persistence API) 是 SUN 針對 ORM 技術提出的規範,目的為簡化持久化的開發工作以及整合各家 ORM 技術(Hibernate、TopLink、OpenJpa…)。
Spring Data JPA 是 Spring 根據 ORM 框架和 JPA 規範而封裝的 JPA 應用框架,目的是降低存取資料層的工作量,讓開發人員只需寫出 repository 的介面,而 Spring 自動幫你實作其功能。
public interface JpaRepository<T,ID>
extends PagingAndSortingRepository<T,ID>, QueryByExampleExecutor
SimpleJpaRepository 中有實作 JpaRepository 介面,實作內容包含簡單的 crud,包含 count()
、existsById(ID id)
、findAll(Specification<T> spec, Pageable pageable)
等等,當繼承 JpaRepository 後不必實作上述方法也能使用。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.2.6.RELEASE'
建立 Entity 達成 orm
@Data
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
private Country country;
private int age;
@CreatedDate
private LocalDateTime createdDate;
}
可用的 Annotation
@Entity
是告訴 Spring 這是資料模型層的宣告。@Table
對應到資料庫中的名稱,也可指定要建立的 index。@Column
對應到 Table 的欄位中的欄位名稱。@Id
此資料表的 Primary Key。@GeneratedValue
告訴此Column的生成方式 ,如果設定成 GenerationType.AUTO
讓容器來自動產生。@CreatedDate
在 Entity 被建立或修改時會自動賦值。@ManyToOne
、@OneToMany
相當於資料表中 FOREIGN KEY 設定
CascadeType.PERSIST
在儲存時一併儲存被參考的物件。CascadeType.MERGE
在合併修改時一併合併修改被參考的物件。CascadeType.REMOVE
在移除時一併移除被參考的物件。CascadeType.REFRESH
在更新時一併更新被參考的物件。CascadeType.ALL
無論儲存、合併、更新或移除,一併對被參考物件作出對應動作。建立 UserDao 繼承 JpaRepository
@Repository
public interface UserDao extends JpaRepository<User, Integer> {
List<User> findByCountryAndAgeLessThan(Country country, int age);
}
Keyword | Sample | SQL |
---|---|---|
And | findByNameAndCountry | …WHERE name = ?1 AND country =?2 |
Between | findByAgeBetween | …WHERE age <= ?1 AND age >=?2 |
LessThan | findByAgeLessThan | …WHERE age < ?1 |
Like | findByNameLike | …WHERE name LIKE ?1 |
Page<User> findByName(String name, Pageable pageable);
List<User> findByName(String name, Sort sort);
public void findUser() {
int page = 0;
int size = 10;
Sort sort = new Sort(Direction.DESC, "age");
Pageable pageable = new PageRequest(page, size, sort);
Page<User> page = userDao.findAll(pageable);
Page<User> page = userDao.findByCountry(country, pageable);
}
@Query(value="select * from user where name like %?1", nativeQuery=true)
public List<User> findByName(String name);
@EnableJpaRepositories
import JpaRepositoriesRegistrar
JpaRepositoriesRegistrar
繼承於 RepositoryBeanDefinitionRegistrarSupport
RepositoryBeanDefinitionRegistrarSupport
的 registerBeanDefinitions
向 spring 註冊 JpaRepositoryFactoryBean
JpaRepositoryFactoryBean.afterPropertiesSet()
會調用 RepositoryFactorySupport.getRepository()
JpaRepositoryFactory
繼承 RepositoryFactorySupport
並且預設的 repository 為 SimpleJpaRepository
JpaRepository
的 class 不需要實作就可以完成 CRUD 的功能。ex findByName(String name)
),為什麼也能存取 DB。RepositoryFactorySupport.getRepository()
中會調用 QueryExecutorMethodInterceptor
,此攔截器就是拿來判斷 method 的類型。findByName()
的類型是自定義的查詢,所以會跑到 SingleEntityExecution()
CriteriaQueryImpl
來拼湊出 sql。使用 JpaRepository 的好處:
Spring Data JPA - Reference Documentation
SpringDataJpa: JpaRepository增删改查
Spring Data JPA 之 JpaRepository
CascadeType 與 FetchType
【spring boot 系列】spring data jpa 全面解析(实践 + 源码分析)
[Java] JPA 是什麼?