實作一個基本資料庫存取功能的程式,需要寫大量的程式做資料庫的連接,甚至還需了解不同資料庫的語法,才能看到功能的雛形,而每多一個功能也需要做許多重複的事情。此篇介紹 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 JpaRepositoriesRegistrarJpaRepositoriesRegistrar 繼承於 RepositoryBeanDefinitionRegistrarSupportRepositoryBeanDefinitionRegistrarSupport 的 registerBeanDefinitions 向 spring 註冊 JpaRepositoryFactoryBeanJpaRepositoryFactoryBean.afterPropertiesSet() 會調用 RepositoryFactorySupport.getRepository()JpaRepositoryFactory 繼承 RepositoryFactorySupport 並且預設的 repository 為 SimpleJpaRepositoryJpaRepository 的 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 是什麼?
When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.
評論