JPA 学习总结
主键生成策略
通过annotation(注解)来映射hibernate实体的,基于annotation的hibernate主键标识为@Id, 其生成规则由**@GeneratedValue**设定的.这里的@id和@GeneratedValue都是JPA的标准用法。
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO。
- IDENTITY:主键由数据库自动生成(主要是自动增长型),前提是底层数据库支持主键递增(MYSQL)
- SEQUENCE:根据底层数据库的序列来生成主键,前提是底层数据库支持序列(Oracle)
- TABLE:使用一个特定的数据库表格来保存主键
- AUTO:主键由程序控制,由程序自动帮助我们选择主键生成策略
JPQL查询
Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造**查询条件**(JPQL的@Query中仅仅是构造查询条件),这时就可以使用@Query注解,结合JPQL的语句方式完成查询。
@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可
栗子:
public interface CustomerDao extends JpaRepository<Customer, Long>,JpaSpecificationExecutor<Customer> {
//@Query 使用jpql的方式查询。
@Query(value="from Customer")
public List<Customer> findAllCustomer();
//@Query 使用jpql的方式查询。
@Query(value="from Customer where custName = ?")
public Customer findCustomer(String custName);
//多个占位符,?1代表参数的占位符,其中1对应方法中的参数索引,参数顺序一致可以不写参数索引
@Query(value="from Customer where custName = ?1 and custId= ?2")
public Customer findCustomer(String custName,Integer custId);
}
也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询
@Query(value="update Customer set custName = ?1 where custId = ?2")
@Modifying
public void updateCustomer(String custName,Long custId);
使用SQL语句查询:
@Query(value="select * from cst_customer",nativeQuery=true)
public void findSql();
动态查询:Specifications
在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。
/**
* 接口JpaSpecificationExecutor中定义的方法
**/
public interface JpaSpecificationExecutor<T> {
//根据条件查询一个对象
T findOne(Specification<T> spec);
//根据条件查询集合
List<T> findAll(Specification<T> spec);
//根据条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//排序查询查询
List<T> findAll(Specification<T> spec, Sort sort);
//统计查询
long count(Specification<T> spec);
}
对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。
Specification接口中只定义了如下一个方法:
/**
* root :Root接口,代表查询的根对象,查询的任何属性都可以从root对象中获取
* query :代表一个顶层查询对象,用来自定义查询(一般不用)
* cb :用来构建查询,此对象封装了很多的条件方法
**/
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
使用Specifications完成条件查询:
@Autowired
private CustomerDao customerDao;
@Test
//自定义查询条件:
//1.实现Specification接口(提供泛型:查询的对象类型)
//2.实现toPredicate方法(构造查询条件)
//3.需要借助两个参数:
//root:获取需要查询的对象属性
//CriteriaBuilder:构造查询的条件,内部封装了很多查询条件(模糊匹配like,精准查询equal等)
public void testSpecifications() {
//使用匿名内部类的方式,创建一个Specification的实现类,并实现toPredicate方法
Specification <Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
//cb:构建查询,添加查询方式 like:模糊匹配
//root:从实体Customer对象中按照custName属性进行查询
Path<Object> custName=root.get("custName");//客户名
Predicate predicate= cb.like(custName.as(String.class), "师%");
//+++++++++++++++++++这里需要注意的是对于比较方法:gt、lt、ge、le、like,得到path对象后,要通过.as(String.class)指明比较的参数类型,再去做比较。+++++
return predicate
}
};
//查询出姓名以“师”字开头的客户
Customer customer = customerDao.findOne(spec);
System.out.println(customer);
}
使用Specifications完成多条件拼接查询:
案例:根据客户名(师耀昌)和客户所在行业(IT教育)查询
public void testSpecifications() {
Specification <Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path<Object> custName=root.get("custName");//客户名
Path<Object> custJob=root.get("custJob");//客户所属行业
//构造查询
//1.构造客户名的精准匹配查询
Predicate p1=cb.equal(custName,"师耀昌");
//2.构造所属行业的精准匹配查询
Predicate p2=cb.equal(custJob,"It教育");
//3.将多个查询条件组合到一起(多个条件同时满足,则为与关系用cb.and(),只要满足其中一个条件即可,则用或关系用cb.or())
Predicate and=cb.and(p1,p2);
return and;
}
};
//查询出姓名为“师耀昌”,且所在行业为“IT教育”的客户
Customer customer = customerDao.findOne(spec);//多条数据用findAll()
System.out.println(customer);
}
排序:
//排序很简单,只需要创建排序对象,将其传入方法即可
//Sort对象:
//第一个参数为排序的顺序 正序:Sort.Direction.ASC 倒序:Sort.Direction.DESC
//第二个参数为排序的属性名称
如:
Sort sort =new Sort(Sort.Direction.ASC,"custId");
List<Customer> list=customerDao.findAll(spec,sort);//根据spec的条件查询,并正序排列
分页:
@Test
public void testPage() {
//构造查询条件
Specification<Customer> spec = new Specification<Customer>() {
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.like(root.get("custName").as(String.class), "传智%");
}
};
/**
* 构造分页参数
* Pageable : 接口
* PageRequest实现了Pageable接口,调用构造方法的形式构造
* 第一个参数:页码(从0开始)
* 第二个参数:每页查询条数
*/
Pageable pageable = new PageRequest(0, 5);
/**
* 分页查询,封装为Spring Data Jpa 内部的page bean
* 此重载的findAll方法为分页方法需要两个参数
* 第一个参数:查询条件Specification
* 第二个参数:分页参数
*/
Page<Customer> page = customerDao.findAll(spec,pageable);
/** Page中还封装了如下方法:
* 获取总页数:
* int getTotalPages();
* 获取总记录数:
* long getTotalElements();
* 获取列表数据:
* List<T> getContent();
*/
System.out.println(page.getContent()) //获取分页后的数据集合
System.out.println(page.getTotalPages()) //获取总页数
System.out.println(page.getTotalElements()) //获取总条数
}
给个饭钱?
- Post link: http://sovzn.github.io/2021/07/13/JPA/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues