有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在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构造的就是查询条件。
//构造查询条件 /** * root :Root接口,代表查询的根对象,可以通过root获取实体中的属性 * query :代表一个顶层查询对象,用来自定义查询 * cb :用来构建查询,此对象里有很多条件方法 **/ public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);对于Spring Data JPA中的分页查询,是其内部自动实现的封装过程,返回的是一个Spring Data JPA提供的pageBean对象。其中内部的方法说明如下:
//获取总页数 int getTotalPages(); //获取总记录数 long getTotalElements(); //获取列表数据 List<T> getContent();客户实体类:
/** * 客户的实体类 * 明确使用的注解都是JPA规范的 * 所以导包都要导入javax.persistence包下的 */ @Entity//表示当前类是一个实体类 @Table(name="cst_customer")//建立当前实体类和表之间的对应关系 public class Customer implements Serializable { @Id//表明当前私有属性是主键 @GeneratedValue(strategy=GenerationType.IDENTITY)//指定主键的生成策略 @Column(name="cust_id")//指定和数据库表中的cust_id列对应 private Long custId; @Column(name="cust_name")//指定和数据库表中的cust_name列对应 private String custName; @Column(name="cust_source")//指定和数据库表中的cust_source列对应 private String custSource; @Column(name="cust_industry")//指定和数据库表中的cust_industry列对应 private String custIndustry; @Column(name="cust_level")//指定和数据库表中的cust_level列对应 private String custLevel; @Column(name="cust_address")//指定和数据库表中的cust_address列对应 private String custAddress; @Column(name="cust_phone")//指定和数据库表中的cust_phone列对应 private String custPhone; //配置客户和联系人的一对多关系 @OneToMany(targetEntity=LinkMan.classtargetEntity = LinkMan.class,cascade = {CascadeType.ALL},orphanRemoval = true,mappedBy = "customer") private Set<LinkMan> linkmans = new HashSet<LinkMan>();联系人实体类:
/** * 联系人的实体类(数据模型) */ @Entity @Table(name="cst_linkman") public class LinkMan implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="lkm_id") private Long lkmId; @Column(name="lkm_name") private String lkmName; @Column(name="lkm_gender") private String lkmGender; @Column(name="lkm_phone") private String lkmPhone; @Column(name="lkm_mobile") private String lkmMobile; @Column(name="lkm_email") private String lkmEmail; @Column(name="lkm_position") private String lkmPosition; @Column(name="lkm_memo") private String lkmMemo; //多对一关系映射:多个联系人对应客户 @ManyToOne(targetEntity=Customer.class) @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer;//用它的主键,对应联系人表中的外键级联操作:指操作一个对象同时操作它的关联对象
使用方法:只需要在操作主体的注解上配置cascade
/** * cascade:配置级联操作 * CascadeType.MERGE 级联更新 * CascadeType.PERSIST 级联保存: * CascadeType.REFRESH 级联刷新: * CascadeType.REMOVE 级联删除: * CascadeType.ALL 包含所有 */ @OneToMany(mappedBy="customer",cascade=CascadeType.ALL) private Set<LinkMan> linkmans = new HashSet<LinkMan>();用户实体类:
/** * 用户的数据模型 */ @Entity @Table(name="sys_user") public class SysUser implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="user_id") private Long userId; @Column(name="user_code") private String userCode; @Column(name="user_name") private String userName; @Column(name="user_password") private String userPassword; @Column(name="user_state") private String userState; //多对多关系映射 @ManyToMany(mappedBy="users") private Set<SysRole> roles = new HashSet<SysRole>(0);一个角色可以赋予多个用户,所以在角色实体类中应该包含多个用户的信息,角色实体类如下:
/** * 角色的数据模型 */ @Entity @Table(name="sys_role") public class SysRole implements Serializable { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name="role_id") private Long roleId; @Column(name="role_name") private String roleName; @Column(name="role_memo") private String roleMemo; //多对多关系映射 @ManyToMany @JoinTable(name="user_role_rel",//中间表的名称 //中间表user_role_rel字段关联sys_role表的主键字段role_id joinColumns={@JoinColumn(name="role_id",referencedColumnName="role_id")}, //中间表user_role_rel的字段关联sys_user表的主键user_id inverseJoinColumns={@JoinColumn(name="user_id",referencedColumnName="user_id")} ) private Set<SysUser> users = new HashSet<SysUser>(0);对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。
例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。
查询一个客户,获取该客户下的所有联系人
@Autowired private CustomerDao customerDao; @Test //由于是在java代码中测试,为了解决no session问题,将操作配置到同一个事务中 @Transactional public void testFind() { Customer customer = customerDao.findOne(5l); Set<LinkMan> linkMans = customer.getLinkMans();//对象导航查询 for(LinkMan linkMan : linkMans) { System.out.println(linkMan); } }查询一个联系人,获取该联系人的所有客户
@Autowired private LinkManDao linkManDao; @Test public void testFind() { LinkMan linkMan = linkManDao.findOne(4l); Customer customer = linkMan.getCustomer(); //对象导航查询 System.out.println(customer); }Spring Data JPA在使用对象导航查询的时候,从一方查询多方,默认使用延迟加载的,从多方查询一方的时候,默认使用立即加载,也可自己配置是否使用延迟加载。
配置方式:
/** * 在客户对象的@OneToMany注解中添加fetch属性 * FetchType.EAGER :立即加载 * FetchType.LAZY :延迟加载 */ @OneToMany(mappedBy="customer",fetch=FetchType.EAGER) private Set<LinkMan> linkMans = new HashSet<>();Demo源码:https://gitee.com/xiaohuoban00/spring-data-jpa-demo.git