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));
}