SpringDataJpa
文章目錄
SpringData -> SpringDataJpa:操作數據庫變得更好簡單
1. pom.xml配置
1.基本的SSJ的導入SpringDataJpa
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
2. SpringDataJpa基本配置
2.1 配置對應的Spring-data-jpa
注意要講ssj中原來對dao層的掃描刪除掉,同時需要將jpa配置中的建表語句generateDdl修改爲false。
<jpa:repositories base-package="cn.itsource.aisell.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"
></jpa:repositories>
2.2 實體的抽取
因爲每個類都有id這個字段,且類型都爲Long類型,爲了簡化代碼,可以將id字段抽取出來形成一個父類,然後讓每個實體類都繼承它即可。
@MappedSuperclass //此註解是告訴jpa這個類不是一個實體類
public class BaseDomain {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
2.3 持久層repository
jpa建議將原來的到層命名爲repository,並且這裏針對實體也只需要寫接口,不需要寫實現,但是接口必須繼承JpaRepository,如果要支持高級的規則執行者,還需要繼承JpaSpecificationExecutor。
//JpaRepository<Employee,Long>:jpa通過代理自定義的接口,實現了基本的增刪改查的方法。
//泛型第一個參數是描述操作的哪一個實體,第二個參數是此實體對應的主鍵的類型。
//JpaSpecificationExecutor<Employee> :JPA規則執行者,用於動態生成Query來適應業務需求
public interface EmployeeRepository extends JpaRepository<Employee,Long>, JpaSpecificationExecutor<Employee>
2.4 完成相應的CRUD
employeeRepository.findAll(); //查詢所有
employeeRepository.findOne(Long id); //根據id查詢對象
employeeRepository.save(對象); //添加或者修改
employeeRepository.delete(id/對象); //刪除對象
employeeRepository.findAll(Pageable) -> 分頁 //Pageable需要new PageRequest對象
Pageable pageable = new PageRequest(0, 5); //0就是第一頁
employeeRepository.findAll(Sort) -> 排序
Sort sort = new Sort(Sort.Direction.DESC,"username"); //第一參數是排序規則,第二個是排序的字段
如果要把分頁和排序結合起來:new PageRequest(0, 5,sort);
3. 規則名稱查詢
在repository接口中,按照一定的規則命名的方法,也不需要自己實現(jpa做了實現),而通過直接調用即可使用。
1.名稱規則
findByUsername(String username) // 根據名稱查詢 username = ?
findByUsernameLike(String username) // username like ?
findByUsernameLikeAndEmailLike(String username,String email)
//username like ? and email like ?
2.Query註解
@Query("select o from Employee o where o.name like ?1") //jpql
//@Query("select o from Employee o where o.name like :name")
@Query(nativeQuery = true,value="select * from employee") //原生sql
4. 規則查詢彙總
表達式 | 例子 | hql查詢語句 |
---|---|---|
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is,Equals | findByFirstname,findByFirstnameIs,findByFirstnameEqual | … where x.firstname = 1? |
Between | findByStartDateBetween | … where x.startDate between 1? and ?2 |
LessThan(lt) | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual(le) | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull | findByAgeIsNull | … where x.age is null |
IsNotNull,NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection age) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstame) = UPPER(?1) |
5. 高級查詢
5.1 JpaSpecificationExecutor
JpaSpecificationExecutor(Jpa規則執行者),一種更加面向對象的方式對數據庫進行操作。在使用這個執行器的時候,需要傳入一個Specification,而Specification是一個接口,裏面有一個toPredicate方法需要實現,所以這離可以通過匿名內部類的方式進行傳參並實現方法,也可以通過lambda表達式來實現方法。
JpaSpecificationExecutor
源碼
public interface JpaSpecificationExecutor<T> {
T findOne(Specification<T> var1); //返回一個對象
List<T> findAll(Specification<T> var1); //返回對象集合
Page<T> findAll(Specification<T> var1, Pageable var2); //分頁查詢
List<T> findAll(Specification<T> var1, Sort var2); //查詢後排序
long count(Specification<T> var1); //返回記錄數
}
Specification
源碼
public interface Specification<T> {
//root:根,通過root的get方法設置字段
//query:設置條件關鍵字,where,group by ,order by...
//條件判斷(一個或者多個)
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
- 使用案例
1.我們的Repository繼承JpaSpecificationExecutor
EmployeeRepository extends JpaRepository<Employee,Long>,JpaSpecificationExecutor<Employee>
2.findAll的查詢方法
employeeRepository.findAll(Specification<T> spec)
employeeRepository.findAll(Specification<T> spec,Pageable pageable)
new Specification<Employee>{
//方法的實現1:username模糊查詢
@Override
public Predicate toPredicate(Root<Employee> root,
CriteriaQuery<?> query,
CriteriaBuilder cb) {
Path path = root.get("username");//拿到要做查詢的字段
Predicate p = cb.like(path, "%1%");//like代表做模糊查詢,後面就是它的條件值
return p;
}
}
//方法的實現2:排序+分頁+lambda表達式
Sort sort = new Sort(Sort.Direction.ASC,"age");
Pageable pageable = new PageRequest(0,10,sort);
Page<Employee> page = employeeRepository.findAll((root, cq, cb) -> {
Path username = root.get("username");
Predicate predicate = cb.like(username, "%ad%");
return predicate;
}, pageable);
page.forEach(e-> System.out.println(e));
5.2 jpa-spec插件
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.0</version>
</dependency>
//第一個參數: true -> 條件過濾啓用
//第二個參數: 需要過濾的屬性
//第三個參數: 過濾條件的 ?對應的值
Specification<Person> specification = Specifications.<Person>and()
.eq(StringUtils.isNotBlank(request.getName()), "name", name)
.gt(Objects.nonNull(request.getAge()), "age", 18)
.build();
5.3 Query的抽取
- 抽取BaseQuery
public abstract class BaseQuery {
private Integer currentPage = 1;//jpa分頁查詢是從0開始的,提供get方法的時候需要減1
private Integer pageSize = 10;
private String orderName;//根據哪個字段進行排序
private String orderType = "ASC"; //指定排序的規則,默認爲增長
//...提供getter,setter方法...注意:getCurrentPage方法返回:return currentPage-1;
//創建一個抽象方法,規範子類創建對應的方法,同時規定子類必須提供一個拿到條件的方法
public abstract Specification createSpec();
//創建排序的對象,這裏根據orderName是否爲空來判斷是否需要排序
//如果orderName沒有值,就返回null【不排序】
//如果orderType是DESC,就升序(反之就降序)
public Sort createSort(){
//StringUtils類是apache.commons.lang3下的工具類包
if(StringUtils.isNotBlank(orderName)){
return new Sort(Sort.Direction.valueOf(orderType.toUpperCase()),orderName);
}
return null;
}
}
- EmployeeQuery實現
public class EmployeeQuery extends BaseQuery {
//查詢條件需要的字段
private String username;
private String email;
private Integer age;
//創建query查詢條件
@Override
public Specification createSpec() {
Specification<Employee> spec = Specifications.<Employee>and()
.like(StringUtils.isNotBlank(username), "username", "%" + username + "%")
.like(StringUtils.isNotBlank(email), "email", "%" + email + "%")
.gt(age != null, "age", age)//gt代表大於
.build();
return spec;
}
}
- 測試代碼
@Test
public void testCreateQuery(){
EmployeeQuery query = new EmployeeQuery(); //創建查詢對象
query.setUsername("ad");
query.setOrderName("age");
Specification spec = query.createSpec(); //創建規則執行者
Sort sort = query.createSort(); //創建排序對象
//創建分頁對象
Pageable pageable = new PageRequest(query.getCurrentPage(),query.getPageSize(),sort);
//查詢結果
Page<Employee> page = employeeRepository.findAll(spec, pageable);
page.forEach(e-> System.out.println(e));
}