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