jpa
- 简介
- JPA 是 Java Persistence API 的简称,是一套 Sun 公司 Java 官方制定的 ORM(Object Relational Mapping) 方案,是规范、是标准,Sun 公司自己并没有实现;
- 市场上的主流的 JPA 框架有:Hibernate (JBoos)、EclipseTop(Eclipse社区)、OpenJPA (Apache基金会),Hibernate 是众多实现者之中,性能最好的,我们引入的 JPA 框架,采用 Hibernate 实现,需要注意的是,引用是 javax.persistence 这个“标准”包,和 org.hibernate 实现包;
- JPA 想要做的就是尽量让你少写 Sql,甚至不写 Sql,基于这种思想,JPA 实现了它自己的一套语法、注解规则,JPA 要用各种注解配合来实现数据实体间一对多、多对多等关联关系,正因为这样,个人觉得实体变得不那么单纯,掺杂了逻辑在里面,这增加了实体的复杂度,对于比较复杂的业务来说,很容易造成实体间直接或间接的循环引用;
- 在此,我们引入将引入 Spring Data Jpa,在 JPA 之上添加另一层抽象(Repository 层的实现),极大地简化持久层开发;
- Hibernate、JPA、Spring Data Jpa 之间的关系如下:
- 引入 Jar
- spring-context
- spring-orm
- mysql-connector-java
- c3p0
- hibernate-core
- spring-data-jpa
- ---------------- 已经引入的部分忽略 ----------------
- 1
<wiz_code_mirror>1
<!-- Spring data jpa -->2<dependency>3<groupId>org.springframework.data</groupId>4<artifactId>spring-data-jpa</artifactId>5<version>2.3.4.RELEASE</version>6</dependency> - 2
- 配置
- 配置结构
- applicationContext.xml ---- 全局配置,为了避免该文件杂乱,我们按功能拆分为子配置文件;
- 引入 jdbcParms.properties ---- Jdbc 配置信息;
- 引入 hibernateParms.properties ---- Hibernate 配置属性信息;
- 引入 springJpa.xml ---- JPA 注入 Spring 配置;
- applicationContext.xml
- 1
<wiz_code_mirror>1
<!-- 加载属性文件 -->2<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">3<property name="locations">4<list>5<value>classpath:config/properties/jdbcParms.properties</value>6<value>classpath:config/properties/hibernateParms.properties</value>7</list>8</property>9</bean>1011<!-- 引入别的配置文件 -->12<import resource="springJpa.xml"/> - 2
- hibernateParms.properties
- 参见 Hibernate 相关配置;
- springJpa.xml
- 配置链
- dataSource ---- entityManagerFactory ---- transactionManager ---- jpa:repositories;
- 1
<wiz_code_mirror>1
<?xml version="1.0" encoding="UTF-8"?>2<beans xmlns="http://www.springframework.org/schema/beans"3xmlns:context="http://www.springframework.org/schema/context"4xmlns:aop="http://www.springframework.org/schema/aop"5xmlns:tx="http://www.springframework.org/schema/tx"6xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"7xmlns:jpa="http://www.springframework.org/schema/data/jpa"8xsi:schemaLocation="http://www.springframework.org/schema/beans9http://www.springframework.org/schema/beans/spring-beans-3.0.xsd10http://www.springframework.org/schema/context11http://www.springframework.org/schema/context/spring-context-3.0.xsd12http://www.springframework.org/schema/mvc13http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd14http://www.springframework.org/schema/aop15http://www.springframework.org/schema/aop/spring-aop-3.0.xsd16http://www.springframework.org/schema/tx17http://www.springframework.org/schema/tx/spring-tx-3.0.xsd18http://www.springframework.org/schema/data/jpa19http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" >2021<!-- 数据源,spring jdbc || c3p0 jdbc -->22<!-- <bean id="dataSourceForJpa" class="org.springframework.jdbc.datasource.DriverManagerDataSource">23<property name="driverClassName" value="${jdbc.driverClassName}"/>24<property name="url" value="${jdbc.url}"/>25<property name="username" value="${jdbc.username}"/>26<property name="password" value="${jdbc.password}"/>27</bean> -->28<!-- 配置 c3p0 数据源 -->29<bean id="dataSourceForJpa" class="com.mchange.v2.c3p0.ComboPooledDataSource">30<property name="driverClass" value="${jdbc.driverClassName}" />31<property name="jdbcUrl" value="${jdbc.url}" />32<property name="user" value="${jdbc.username}" />33<property name="password" value="${jdbc.password}" />3435<!-- 连接池中连接用完时,c3p0 一次性创建的连接数 -->36<property name="acquireIncrement" value="${pool.acquireIncrement}" />37<!-- 初始化连接数,在 minPoolSize 和 maxPoolSize 之间 -->38<property name="initialPoolSize" value="${pool.initialPoolSize}" />39<property name="minPoolSize" value="${pool.minPoolSize}" />40<property name="maxPoolSize" value="${pool.maxPoolSize}" />41<!-- 连接关闭时默认将所有未提交的操作回滚,默认为 false -->42<property name="autoCommitOnClose" value="${pool.autoCommitOnClose}"/>43</bean>4445<!-- 配置 entityManagerFactory -->46<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">47<!-- 设置数据源 -->48<property name="dataSource" ref="dataSourceForJpa" />49<!-- 扫描 Entity 包 -->50<property name="packagesToScan" value="com.sfac.springMvc.module.*.entity" />51<!-- 指定 JPA 实现厂商 -->52<property name="persistenceProvider">53<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />54</property>55<!-- 指定 JPA 供应商适配器 -->56<property name="jpaVendorAdapter">57<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />58</property>59<!-- 配置 JPA 属性 -->60<property name="jpaProperties">61<props>62<!-- 动态生成表策略 -->63<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>64<!-- 命名规则隐式策略 -->65<prop key="hibernate.implicit_naming_strategy">${hibernate.implicit_naming_strategy}</prop>66<!-- 数据库方言 -->67<prop key="hibernate.dialect">${hibernate.dialect}</prop>68<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>69<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>70</props>71</property>72</bean>7374<!-- 配置事务管理器 -->75<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">76<property name="entityManagerFactory" ref="entityManagerFactory" />77</bean>7879<!-- 开启事务注解 -->80<tx:annotation-driven transaction-manager="jpaTransactionManager"/>8182<!-- 配置 repository -->83<jpa:repositories base-package="com.sfac.springMvc.module.*.repository"84transaction-manager-ref="jpaTransactionManager"85entity-manager-factory-ref="entityManagerFactory" />86</beans> - 2
- 应用
- 主要接口和实现类
- Repository
- 属性查询
- 接口方法命名:
- findBy + 属性 + 关键字 + 属性(option);
- find + Top || First(option) + By + 属性 + 关键字 + 属性(option);
- 查询关键字列表
- And ---- findByLastnameAndFirstname ---- … where x.lastname = ?1 and x.firstname = ?2;
- Or ---- findByLastnameOrFirstname ---- … where x.lastname = ?1 or x.firstname = ?2;
- Is,Equals ---- findByFirstname,findByFirstnameIs,findByFirstnameEquals ---- … where x.firstname = ?1;
- Between ---- findByStartDateBetween ---- … where x.startDate between ?1 and ?2;
- LessThan ---- findByAgeLessThan ---- … where x.age < ?1;
- LessThanEqual ---- 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<Age> ages) ---- … where x.age in ?1;
- NotIn ---- findByAgeNotIn(Collection<Age> ages) ---- … 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);
- @Query 注解查询;
- 查询语言
- SQL 语言
- 关系型数据库查询语言;
- select name, age, user_id from t_user;
- JPA 中使用 Sql 语句,只需注解中添加参数 nativeQuery = true 即可;
- HQL 语言
- Hibernate 造出来的对象语言,只有 Hibernate 框架能够解析,并将其通过一系列的映射转换,拼凑成 SQL 语言;
- 使用 “类名” 取代 “表名”,用 “类名.属性名” 取代 “表名.列名”,类名建议不要省略;
- 没有 * 查询,使用 from 类名 代替;
- 查询操作使用 @Query 注解
- 删除修改操作使用 @Modifying + @Transactional + @Query 注解,@Transactional 可加在 service 层;
- JPQL 语言
- EJB3.0 中的 JPA 造出来的对象查询语言,其原型类似于 HQL,Sun 看到 Hibernate 做的 ORM 技术做的非常好,成为行业内领导 ORM 框架的主流产品,故 Sun 将其吸收进 EJB3 中,制定了一套 ORM 的 JAVA API 标准;
- 传参方式
- select name,age,userId from User where userName = ?1; ---- ?加数字表示占位符,从 1 开始;
- select name,age,userId from User where userName=:userName; ---- :加上变量名,这里是与方法参数中有 @PARAM 的值匹配;
- CrudRepository
- 添加了对数据的增删改查的方法;
- PagingAndSortingRepository
- 添加了对数据的分页和排序方法;
- QueryByExampleExecutor
- Example 条件查询;
- JpaRepository
- 对继承父接口中方法的返回值进行了适配,在父类接口中通常都返回迭代器,需要我们自己进行强制类型转化,而在 JpaRepository 中,直接返回了 List;
- JpaSpecificationExecutor
- 主要提供了多条件查询的支持,并且可以在查询中添加分页和排序,因为这个接口单独存在,因此需要配合以上说的接口使用;
- QueryDslPredicateExecutor
- SimpleJpaRepository ---- 实现类;
- QueryDslJpaRepository ---- 实现类;
- 新增接口
- 针对每个 Bean 书写一个持久层接口,继承 JpaRepository<T, ID> 接口(其中 T 是实体 bean, ID 是主键),添加 @Repository 注解,到此,我们一行代码都不用写,即可调用父接口实现功能;
- StudentRepository.java
- 1
<wiz_code_mirror>12
public interface StudentRepository extends JpaRepository<Student, Integer> {3} - 2
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
private StudentRepository studentRepository;3456public ResultEntity<Student> insertStudentForJpa(Student student) {7studentRepository.saveAndFlush(student);8return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Insert success.", student);9} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student ---- post3* {"studentName":"HymanHu"}4* {"studentName":"HymanHu", "studentCard":{"cardNo":"studentCard001"}}5*/67public ResultEntity<Student> insertStudentForJpa(8return studentService.insertStudentForJpa(student);9} - 2
- 修改接口
- StudentServiceImpl.java
- 1
<wiz_code_mirror>123
public ResultEntity<Student> updateStudentForJpa(Student student) {4studentRepository.saveAndFlush(student);5// int i = 1 / 0;6return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Update success.", student);7} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student ---- put3* {"id":"2","studentName":"HymanHu1"}4* {"id":"2","studentName":"HymanHu1","studentCard":{"id":"1","cardNo":"studentCard002"}}5*/67public ResultEntity<Student> updateStudentForJpa(8return studentService.updateStudentForJpa(student);9} - 2
- 删除接口
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
public ResultEntity<Object> deleteStudentForJpa(Integer id) {3studentRepository.deleteById(id);4return new ResultEntity<Object>(ResultStatus.SUCCESS.status, "Delete success.");5} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student/2 ---- delete3*/45public ResultEntity<Object> deleteStudentForJpa(6return studentService.deleteStudentForJpa(id);7} - 2
- 父接口查询
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
public Student getStudentByIdForJpa(Integer id) {3return studentRepository.findById(id).orElse(null);4}56public List<Student> getStudentsForJpa() {7return studentRepository.findAll();8} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student/9 ---- get3*/45public Student getStudentByIdForJpa(6return studentService.getStudentByIdForJpa(id);7}8/**9* 127.0.0.1/api/jpa/students ---- get10*/1112public List<Student> getStudentsForJpa(13return studentService.getStudentsForJpa();14} - 2
- Example && Criteria 查询
- JpaRepository:<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
- Example:动态条件查询样本 ----static <T> Example<T> of(T probe, ExampleMatcher matcher);
- T:实体 bean;
- ExampleMatcher:查询匹配器;
- ExampleMatcher.matchingAny() ---- 匹配条件是 or 的关系;
- ExampleMatcher.matching() ---- 匹配条件是 and 的关系;
- withMatcher("studentName", match -> match.contains()) ---- 模糊查询,即 %studentName%;
- withIgnorePaths("studentId", "createDate"); ---- 忽略字段,可以自动忽略空值的字段,但 id 是基本数据类型,默认值为 0,必须忽略;
- Pageable:分页组件 ---- public static PageRequest of(int page, int size, Sort sort);
- page:当前页,从 0 开始;
- size:每页大小;
- sort:排序对象 ---- new Sort(direction, orderBy);
- direction:排序方向,Sort.Direction.ASC;
- orderBy:排序字段;
- Page:返回分页对象;
- JpaSpecificationExecutor:Page findAll(@Nullable Specification<T> spec, Pageable pageable);
- Specification:Criteria 多条件查询,支持时间比较、数字比较等;
- Pageable
- Page
- StudentRepository.java
- 1
<wiz_code_mirror>12
public interface StudentRepository extends JpaRepository<Student, Integer>, JpaSpecificationExecutor<Student> {3} - 2
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
public Page<Student> getStudentsBySearchBeanForJpa(SearchBean searchBean) {3searchBean.initSearchBean();4// Page<Student> page = exampleAndPage(searchBean);5Page<Student> page = criteriaAndPage(searchBean);6return page;7}89/**10* -实现方式一:Example + Page 查询11*/12public Page<Student> exampleAndPage(SearchBean searchBean) {13// 创建 Pageable 对象14String orderBy = StringUtils.isBlank(searchBean.getOrderBy()) ? "id" : searchBean.getOrderBy();15Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) ||16searchBean.getDirection().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC;17Sort sort = new Sort(direction, orderBy);18// 当前页起始为 019Pageable pageable = PageRequest.of(searchBean.getCurrentPage() - 1, searchBean.getPageSize(), sort);2021// 创建 Example 对象22Student student = new Student();23student.setStudentName(searchBean.getKeyWord());24student.setCreateDate(LocalDateTime.of(2021, 1, 19, 0, 0));25// matchingAny 相当于 or 连接查询条件,matching 相当于 and 连接查询条件26ExampleMatcher exampleMatcher = ExampleMatcher.matchingAny()27// 模糊查询,即 %{studentName} %28.withMatcher("studentName", match -> match.contains())29// 时间类型不支持模糊查询,生成的语句为createDate=?,同时也不支持 id > startId && id < endId 这样的操作30.withMatcher("createDate", match -> match.contains())31// 忽略基本数据类型字段,如果使用包装类则无需忽略32.withIgnorePaths("id");33Example<Student> example = Example.of(student, exampleMatcher);3435return studentRepository.findAll(example, pageable);36}3738/**39* -实现方式:Criteria + Page40*/41public Page<Student> criteriaAndPage(SearchBean searchBean) {42// 创建 Pageable 对象43String orderBy = StringUtils.isBlank(searchBean.getOrderBy()) ? "id" : searchBean.getOrderBy();44Sort.Direction direction = StringUtils.isBlank(searchBean.getDirection()) ||45searchBean.getDirection().equalsIgnoreCase("asc") ? Sort.Direction.ASC : Sort.Direction.DESC;46Sort sort = new Sort(direction, orderBy);47// 当前页起始为 048Pageable pageable = PageRequest.of(searchBean.getCurrentPage() - 1, searchBean.getPageSize(), sort);4950// 创建 Specification 对象51Specification<Student> specification = new Specification<Student>() {52private static final long serialVersionUID = 1L;5354/**55* -构造语句 select * from test_student where createDate>=? and56* (studentName like ? or id between 10 and 20) order by id desc limit ?57*/5859public Predicate toPredicate(Root<Student> root, CriteriaQuery<?> query,60CriteriaBuilder criteriaBuilder) {6162return criteriaBuilder.and(63criteriaBuilder.greaterThanOrEqualTo(64root.get("createDate").as(LocalDateTime.class),65LocalDateTime.of(2021, 1, 20, 0, 0)),66criteriaBuilder.or(67criteriaBuilder.like(root.get("studentName").as(String.class),68String.format("%%%s%%", searchBean.getKeyWord())),69criteriaBuilder.between(root.get("id"), 10, 20)70)71);72}73};7475return studentRepository.findAll(specification, pageable);76} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/students ---- post3* {"currentPage":1, "pageSize":5, "keyWord":"hy", "orderBy":"id", "direction":"desc"}4* {"currentPage":1, "pageSize":5, "keyWord":"hu", "orderBy":"studentName", "direction":"asc"}5* -注意:排序字段不能使用下划线6*7*/89public Page<Student> getStudentsBySearchBeanForJpa(10return studentService.getStudentsBySearchBeanForJpa(searchBean);11} - 2
- 属性查询
- StudentRepository.java
- 1
<wiz_code_mirror>1
List<Student> findByStudentName(String studentName); - 2
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
public Student getStudentByNameForJpa(String studentName) {3List<Student> students = Optional.ofNullable(studentRepository.findByStudentName(studentName))4.orElse(Collections.emptyList());5return students.isEmpty() ? null : students.get(0);6} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student?studentName=HymanHu ---- get3*/45public Student getStudentByNameForJpa(6return studentService.getStudentByNameForJpa(studentName);7} - 2
- Sql && Hql 查询
- StudentRepository.java
- 1
<wiz_code_mirror>1
//@Query(nativeQuery = true, value = "select * from test_student where id = :id")23Student getStudentById(456//@Query(nativeQuery = true, value = "update test_student set student_name = :studentName where id = :id")78void updateStudentName(9101112"set student_name = :#{#student.studentName} where id = :#{#student.id}")13void updateStudent( - 2
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
public Student getStudentByIdV2ForJpa(Integer id) {3return studentRepository.getStudentById(id);4}5678public ResultEntity<Student> updateStudentNameForJpa(Student student) {9studentRepository.updateStudentName(student.getStudentName(), student.getId());10return new ResultEntity<Student>(ResultStatus.SUCCESS.status, "Update success.", student);11} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/student/2/v2 ---- get3*/45public Student getStudentByIdV2ForJpa(6return studentService.getStudentByIdV2ForJpa(id);7}89/**10* 127.0.0.1/api/jpa/student/v2 ---- put11* {"id":"1","studentName":"HymanHu1"}12*/1314public ResultEntity<Student> updateStudentNameForJpa(15return studentService.updateStudentNameForJpa(student);16} - 2
- 批量接口
- 思路
- 方式一:父接口中有 saveAll、deleteInBatch 方法,可完成批量增删改操作,在此不做赘述;
- 方式二:引入 @persistencecontext 注解和 EntityManager 类,属于 Jpa 包,用途是将 Entity 在内存操作,处理完成之后再一次性同步到数据库,效率要比第一种方式高;
- StudentServiceImpl.java
- 1
<wiz_code_mirror>12
private EntityManager entityManager;3456public ResultEntity<List<Student>> batchInsertStudentsForJpa(List<Student> students) {7// 方式一,调用父接口完成批量操作8// studentRepository.saveAll(students);9// studentRepository.flush();1011// 方式二,引入 entityManager 进行批量操作,效率高于第一种方式12students.stream().forEach(item -> {13entityManager.persist(item);14});15entityManager.flush();16entityManager.clear();17return new ResultEntity<List<Student>>(ResultStatus.SUCCESS.status, "Update success.", students);18} - 2
- StudentController.java
- 1
<wiz_code_mirror>1
/**2* 127.0.0.1/api/jpa/students/v2 ---- post3* [{"studentName":"aa1"},{"studentName":"aa2"}]4*/56public ResultEntity<List<Student>> batchInsertStudentsForJpa(7return studentService.batchInsertStudentsForJpa(students);8} - 2
- 练习
- 创建 User、Role、UserRole、Resource、RoleResource 五个 Bean,使用 Jpa 自动生成表(中间表不要外键关系),使用 Mybatis Or Jpa 实现 User、 Role、Resource 的增删改查接口;

浙公网安备 33010602011771号