net码农刚开始接触java项目,开发过程中对Spring Data Jpa做了一些了解,整理一套文档供项目组其他同事参考,所以也写个博文,对这两天的工作学习做个归纳,希望能对其他新入门的同学有点帮助!如果有写的不对的地方,望大家不吝指正,我也好做出调整,避免误导他人。
Entity层(实体类,与数据库表、字段的映射):
@Entity
@DynamicInsert
@DynamicUpdate
@Table(name = "USERS")
public class Users {
@Id
@Column(name = "ID")
private String id;
@Column(name = "LOGIN_NAME")
private String loginName;
@Column(name = "REAL_NAME")
private String realName;
@OneToMany(mappedBy = "users")
private List<DeptUserRelation> deptUserRelations;
/*
* 部门名称
* */
@Transient
private String deptName;
}
- @Entity注解:标注该类 为一个实体类,可以直接为此注解的name 赋值,值为对应的数据库表名,也可以通过@Table注解实现,详细配置查看相关资料
- @Table注解:标注此实体类映射的数据库表 ,详细配置查看相关资料
-
@DynamicInsert,@DynamicUpdate注解:用于动态生成insert,update语句,主要作用是调用JPA提供的新增,修改操作接口时不生成传入对象属性的值为null的字段
-
@ID注解:标注此属性为主键列,详细配置查看相关资料
-
@Column注解: 标注实体类的属性映射的数据库列,详细配置查看相关资料
-
@Transient注解: 标注该属性不与数据库映射
更多配置映射关系的注解不一一介绍,重点说一下@OneToMany @ManyToOne,@ManyToMany,@OneToOne实体关系映射(用于join查询,主外键约束和主从表的数据同步不作介绍)。
以多对多关系Users(人员表)与Department(部门表)表之间的关系(DeptUserRelation表)举例:
-
分别通过@OneToMany,@OneToMany在Users与DeptUserRelation,Department与DeptUserRelation类建立一对多的关系
/* * Users类: DeptUserRelation类映射的表为Users与Department的关联表,mappedBy的值为 * DeptUserRelation类中与Users类中建立关联关系的属性名users **/ @OneToMany 一对多关系 @OneToMany(mappedBy = "users") private List<DeptUserRelation> deptUserRelations; /* * Department类:同Users类,设置mappedBy值与DeptUserRelation中的属性department建立关联关系 **/ @OneToMany(mappedBy = "department") private List<DeptUserRelation> deptUserRelations; /* * DeptUserRelation类:与Users类建立关联关系的属性users,USER_ID为Users类映射的数据表的外键 **/ @ManyToOne @JoinColumn(name = "USER_ID", insertable = false, updatable = false) private Users users; /* * DeptUserRelation类:与Department类建立关联关系的属性department,DEPARTMENT_ID为Department类 * 映射的数据表的外键 **/ @ManyToOne @JoinColumn(name = "DEPARTMENT_ID", insertable = false, updatable = false) private Department department;
-
通过@ManyToMany 建立多对多关系,直接与另一张表建立映射关系,不需要关注中间表,如Users与Department类为多对多关系:
/* * Uses实体类中添加属性departments * @JoinTable注解:name= 关联表的名字(如果没有指定name,则由框架在库中生成中间表) * joinColumns: * @JoinColumn注解:中间表中Uses表的外键 * inverseJoinColumns * @JoinColumn注解:中间表中另一张表Department的外键 **/ @ManyToMany @JoinTable(name = "DEPT_REL_USER", joinColumns={@JoinColumn(name = "USER_ID",insertable = false,updatable = false)}, inverseJoinColumns = { @JoinColumn(name="DEPARTMENT_ID",insertable=false,updatable = false) }) private Set<Department> departments; /* * Department实体类中添加属性users,departments为Users类中的关联属性departments **/ @ManyToMany(mappedBy = "departments") private List<Users> users;
insertable ,updatable 主从表的数据同步用,还没有深入了解,不作介绍;建立关联关系的这些属性还用于写动态查询Join语句时使用,具体对照下方Service层的代码
Dao层(数据库操作层):
@Repository
public interface UsersDao extends JpaRepository<Users,String>, JpaSpecificationExecutor<Users> {
@Query(value = "select t2.LOGIN_NAME,t2.REAL_NAME from (select * from DEPARTMENT T start with T.ID= ?1 connect by T.PID=prior T.id) t\n" +
" left join dept_rel_user t1 on t.id=t1.department_id\n" +
" left join users t2 on t1.user_id=t2.id \n" +
" where 1=1",nativeQuery = true)
Page<Object> findAll(String deptId, Specification<Users> specification, Pageable pageable);
}
-
@Repository注解:可以标记在任何的类上,用来表明该类是用来执行与数据库相关的操作(即dao对象),并支持自动处理数据库操作产生的异常
-
@Query注解:标记在自定义查询方法上,提供编写本地化sql(JPQL语法可以查看相关资料)的功能,传参方式有多种,建议使用索引的方式?1(意思是方法的第一 个参数,以此类推)
-
JpaRepository:JpaRepository支持接口规范方法名查询。意思是如果在接口中定义的查询方法符合它的命名规则,就可以不用写实现,提供简单的增删改查操作
-
JpaSpecificationExecutor:JpaSpecificationExecutor接口提供了复杂的动态查询,具体参考后面的Service代码
Service层(业务逻辑层):
@Service
public class UsersService {
@Autowired
private UsersDao usersDao;
@PersistenceContext
private EntityManager entityManager;
}
-
@Service注解:这个注解是写在类上面的,标注将这个类交给Spring容器管理,spring容器要为他创建对象
-
@Autowired注解:这个注解是用来修饰变量的,写在变量上面,并且由系统底层代理创建这个变量的实例,并注入到这个类中,就不用自己手动去创建对象了。
-
@PersistenceContext注解:是注入保存实体类状态的数据结构,针对实体类的不同状态(四种,managedh或detached等)可以做出不同的反应(merge,persist等等),其实就是把数据从数据库里提出,然后在内存里处理的,再返回数据库的法则。
-
findAllByUsers方法:按照Uses对象分页查询,因为业务中可能遇到很多查询条件的方法,也可以查询条件是动态的,比如暂定只有username的查询条件,后期可能添加usersex之类的查询,所以我们的查询条件不能是写死的,灵活的方式就是根据实体对象查询,在方法内部根据实体类的每个属性是否有值去判断是否增加此查询条件,就需要用到Dao层继承的JpaSpecificationExecutor接口提供的动态查询,此处使用的findAll(Specification,Pageable)。
-
/* * 1,Specification:动态创建查询语句,重写Specification类的 toPredicate方法 * Root<Users>:查询的表 * CriteriaQuery<?>:select and where 等查询操作 * CriteriaBuilder:字段之间的关系,如何生成一个查询条件,每一个查询条件都是什么方式 * 2,Pageable:分页对象 * 3,list.add(cb.equal(root.get("id"), users.getId())); 将查询条件添加到容器中,equel “=”操 * 作(not、in、exists等等操作);root.get("id"),指向此root对象对应的表中的字段ID,此处的 * get("id")为实体类中的属性id,参考实体类的字段与数据库列的映射 * 4,Subquery<DeptUserRelation> deptUserRelationSubquery = * cq.subquery(DeptUserRelation.class); 多表查询,此处用于exists操作,对应查询的子表 **/ public Page<Users> findAllByUsers(Users users, Pageable pageable) { Page<Users> usersPage = (Page<Users>) usersDao.findAll(new Specification<Users>() { @Override public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { //部门人员关联表 Subquery<DeptUserRelation> deptUserRelationSubquery = cq.subquery(DeptUserRelation.class); Root<DeptUserRelation> deptUserRelationRoot = deptUserRelationSubquery.from(DeptUserRelation.class); deptUserRelationSubquery.select(deptUserRelationRoot); List<Predicate> list = new ArrayList<Predicate>(); //按id查询 if (!Strings.isNullOrEmpty(users.getId())) { list.add(cb.equal(root.get("id"), users.getId())); } //未分配部门 if (!Objects.isNull(users.getnDept()) && users.getnDept() == 1) { deptUserRelationSubquery.where(cb.equal(deptUserRelationRoot.get("userId"), root.get("id"))); list.add(cb.not(cb.exists(deptUserRelationSubquery))); } Predicate[] arr = new Predicate[list.size()]; cq.where(list.toArray(arr)); return null; } }, pageable); return usersPage; }
-
findUsersByDeptID2,多表关联分页查询:@ManyToMany关系,这种方式无法取到中间表的数据
public Page<Users> findUsersByDeptID2(Users users, Pageable pageable) { return (Page<Users>) usersDao.findAll(new Specification<Users>() { @Override public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); // 1,这种方式建立JOIN需要通过下列代码关联右表属性 // Join<Users,Department> usersDepartmentJoin = root.join("departments", JoinType.INNER); // Metamodel metamodel = entityManager.getMetamodel(); // //获取Department表属性 ID // EntityType<Department> departmentEntityType = metamodel.entity(Department.class); // Path deptId=usersDepartmentJoin.get(departmentEntityType.getSingularAttribute("id", String.class)); // 2,root.getModel().getSet("departments",Department.class) 可以直接关联右表属性 Join<Users, Department> usersDepartmentJoin = root.join(root.getModel().getSet("departments", Department.class), JoinType.INNER); if (!Strings.isNullOrEmpty(users.getdID())) { List<Department> departments = departmentDao.findChildsById(users.getdID()); CriteriaBuilder.In<Object> stringIn = cb.in(usersDepartmentJoin.get("id")); for (Department department : departments) { stringIn.value(department.getId()); } predicates.add(cb.and(stringIn)); } predicates.add(cb.isNotNull(root.get("id"))); Predicate[] arr = new Predicate[predicates.size()]; List<Selection<?>> selections = new ArrayList<Selection<?>>(); selections.add(root); cq.multiselect(selections); cq.where(predicates.toArray(arr)); // cq.orderBy(cb.asc(deptId)); return null; } }, pageable); }
-
findUsersByDeptID,多表关联分页查询,使用@OneToMany建立多对多关系,这种方式可以取到中间表的数据
public Page<Users> findUsersByDeptID(Users users, Pageable pageable) { return (Page<Users>) usersDao.findAll(new Specification<Users>() { @Override public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { List<Predicate> predicates = new ArrayList<>(); Join<Users, DeptUserRelation> userJoin = root.join("deptUserRelations", JoinType.LEFT); Join<DeptUserRelation, Department> departmentJoin = userJoin.join("department", JoinType.LEFT); if (!Strings.isNullOrEmpty(users.getdID())) { List<Department> departments = departmentDao.findChildsById(users.getdID()); CriteriaBuilder.In<Object> stringIn = cb.in(departmentJoin.get("id")); for (Department department : departments) { stringIn.value(department.getId()); } predicates.add(cb.and(stringIn)); } Predicate[] arr = new Predicate[predicates.size()]; cq.where(predicates.toArray(arr)); return null; } }, pageable); }