SpringDataJpa

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表達式來實現方法。

  1. 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); //返回記錄數
}
  1. Specification源碼
public interface Specification<T> {
    //root:根,通過root的get方法設置字段
    //query:設置條件關鍵字,where,group by ,order by...
    //條件判斷(一個或者多個)
    Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}
  1. 使用案例
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的抽取

  1. 抽取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;
    }
}
  1. 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;
    }
}
  1. 測試代碼
@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));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章