SpringData概述
SpringData :Spring的一個子項目。用於簡化數據庫訪問,支持NoSQL和關系數據存儲。其主要目標是使用數據庫的訪問變得方便快捷。
SpringData 項目所支持NoSQL存儲:
- MongoDB(文檔數據庫)
- Neo4j(圖形數據庫)
- Redis(鍵/值存儲)
- Hbase(列族數據庫)
SpringData 項目所支持的關系數據存儲技術:
- JDBC
- JPA
Spring Data : 致力於減少數據訪問層 (DAO) 的開發量. 開發者唯一要做的,就只是聲明持久層的接口,其他都交給 Spring Data JPA 來幫你完成!
框架怎么可能代替開發者實現業務邏輯呢?比如:當有一個 UserDao.findUserById()
這樣一個方法聲明,大致應該能判斷出這是根據給定條件的 ID 查詢出滿足條件的 User 對象。Spring Data JPA 做的便是規范方法的名字,根據符合規范的名字來確定方法需要實現什么樣的邏輯。
SpringBoot+SpringData 整合入門
第一步 添加依賴
springBoot相關
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
mysql驅動
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
springDataJPA相關
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
第二步 配置文件 (src/main/resources/application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot-springdata
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
jpa:
#指定數據庫
database: mysql
#打印sql
show-sql: true
hibernate:
#開啟數據庫更新表
ddl-auto: update
#指定命名策略
naming:
strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
#Hibernate方言
dialect: org.hibernate.dialect.MySQL5Dialect
第三步 創建Repository接口、Entity
Repository接口
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {}
Entity(啟動項目會自動創建表)
@DynamicInsert(true)
@DynamicUpdate(true)
@Table(name = "sys_user")
public class User implements Serializable{
private static final long serialVersionUID = 6425411731900579688L;
@Id
@GeneratedValue
@Column(columnDefinition = "bigint(20) comment '主鍵'", nullable = false)
private long id;
@Column(columnDefinition = "varchar(255) comment '用戶姓名'",nullable = false, unique = true)
private String username;
@Column(columnDefinition = "varchar(255) comment '密碼'", nullable = false)
private String password;
@Column(columnDefinition = "int(10) comment '年齡'", nullable = false)
private int age;
}
這樣就整合完成,可以創建接口進行單元測試了
Repository接口概述
Repository 接口是 Spring Data 的一個核心接口,它不提供任何方法,開發者需要在自己定義的接口中聲明需要的方法
public interface Repository<T, ID extends Serializable> { }
Spring Data可以讓我們只定義接口,只要遵循 Spring Data的規范,就無需寫實現類。
與繼承 Repository 等價的一種方式,就是在持久層接口上使用 @RepositoryDefinition
注解,並為其指定 domainClass
和idClass
屬性。如下兩種方式是完全等價的
/**
* 主鍵方式定義接口
*/
@RepositoryDefinition(domainClass=Person.class,idClass=Integer.class)
public interface PersonRepsotory{}
/**
* 繼承方式定義接口
* 常用
*/
public interface PersonRepsotory extends JpaRepository<Person, Integer>{}
Repository的子接口
如圖,基礎的 Repository 提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能。它們的繼承關系如下:
Repository
: 僅僅是一個標識,表明任何繼承它的均為倉庫接口類
CrudRepository
: 繼承 Repository,實現了一組 CRUD 相關的方法
PagingAndSortingRepository
: 繼承 CrudRepository,實現了一組分頁排序相關的方法
JpaRepository
: 繼承 PagingAndSortingRepository,實現一組 JPA 規范相關的方法
自定義的 XxxxRepository
: 需要繼承 JpaRepository,這樣的 XxxxRepository接口就具備了通用的數據訪問控制層的能力。
JpaSpecificationExecutor
: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法
SpringData 查詢方法命名規范
簡單條件查詢: 查詢某一個實體類或者集合
按照 Spring Data 的規范,查詢方法以 find | read | get
開頭, 涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性以首字母大寫。
例如:定義一個 Entity 實體類
public class User{
private String lastName;
private String firstName;
}
使用And條件連接時,應這樣寫: findByLastNameAndFirstName(String lastName,String firstName);
條件的屬性名稱與個數要與參數的位置與個數一一對應
支持的關鍵字
springData 查詢方法解析原理
假如創建如下的查詢:findByUserDeptUuid()
,框架在解析該方法時,首先剔除 findBy
,然后對剩下的屬性進行解析,假設查詢實體為UserInfo。
第一步:先判斷 userDeptUuid (根據 POJO 規范,首字母變為小寫)是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,繼續第二步;
第二步:從右往左截取第一個大寫字母開頭的字符串(此處為Uuid),然后檢查剩下的字符串是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,則重復第二步,繼續從右往左截取;最后假設 user 為查詢實體的一個屬性;
第三步:接着處理剩下部分(DepUuid),先判斷 user 所對應的類型是否有deptUuid屬性,如果有,則表示該方法最終是根據 “ UserInfo.user.deptUuid” 的取值進行查詢;否則繼續按照步驟 二的規則從右往左截取,最終表示根據 “UserInfo.user.dept.uuid” 的值進行查詢。可能會存在一種特殊情況,比如 UserInfo包含一個 user 的屬性,也有一個 userDep 屬性,此時會存在混淆。可以明確在屬性之間加上 "_" 以顯式表達意圖,比如 "findByUser_DepUuid()" 或者 "findByUserDept_Uuid()"
springData 實戰
命名方法查詢
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* 根據username來獲取對應的user
*/
User getByUsername(String username);
/**
* WHERE username LIKE %?
*/
List<User> findByUsernameStartingWith(String username);
/**
* WHERE username LIKE ?%
*/
List<User> findByUsernameEndingWith(String username);
/**
* WHERE username id < ?
*/
List<User> findByIdLessThan(Long id);
}
注解查詢
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* 查詢id 值最大的那個User 使用@Query 主鍵可以自定義JPQL語句以實現更靈活的查詢
*/
@Query("SELECT u FROM User u WHERE u.id = (SELECT MAX(p.id) FROM User p)")
User getMaxIdUser();
/**
* @Query 注解傳遞參數的方式一:占位符方式
*/
@Query("SELECT u FROM User u WHERE u.username = ?1 AND u.age = ?2")
List<User> testQueryAnnotationUser1(String username , Integer age);
/**
* @Query 注解傳遞參數的方式二:命名參數方式
*/
@Query("SELECT u FROM User u WHERE u.username = :username AND u.age = :age")
List<User> testQueryAnnotationUser2(@Param("username") String username , @Param("age")Integer age);
}
可以通過自定義的 JPQL 完成 UPDATE 和 DELETE 操作,注意
: JPQL 不支持使用 INSERT。
在 @Query 注解中編JPQL 語句, 但必須使用 @Modifying 進行修飾
. 以通知 SpringData, 這是一個 UPDATE 或 DELETE 操作。
UPDATE 或 DELETE 操作需要使用事務, 可以使用注解@Transactional聲明
,默認情況下, SpringData 的每個方法上有事務, 但都是一個只讀事務. 他們不能完成修改操作!
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* @Query 注解update、delete操作,不支持insert
*/
@Modifying
@Query("UPDATE User u SET u.age = :age")
@Transactional
void updateUserAge(@Param("age") Integer age);
}
還可以使用原生Sql查詢,只需配置nativeQuery = true
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
/**
* 設置 nativeQuery=true 即可以使用原生的 SQL 查詢
*/
@Query(value = "SELECT COUNT(id) FROM sys_user" , nativeQuery = true)
long getTotalCount();
}