第1章 Spring Data JPA的概述
1.1 Spring Data JPA概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。
1.2 Spring Data JPA的特性
SpringData Jpa 极大简化了数据库访问层代码。 如何简化的呢? 使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。
1.3 Spring Data JPA 与 JPA和hibernate之间的关系
JPA是一套规范,内部是由接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。
第2章 Spring Data JPA的快速入门
2.1 需求说明
Spring Data JPA完成客户的基本CRUD操作
2.2 搭建Spring Data JPA的开发环境
2.2.1 引入Spring Data JPA的坐标
使用Spring Data JPA,需要整合Spring与Spring Data JPA,并且需要提供JPA的服务提供者hibernate,所以需要导入spring相关坐标,hibernate坐标,数据库驱动坐标等
1 <properties> 2 <spring.version>5.0.2.RELEASE</spring.version> 3 <hibernate.version>5.0.7.Final</hibernate.version> 4 <slf4j.version>1.6.6</slf4j.version> 5 <log4j.version>1.2.12</log4j.version> 6 <c3p0.version>0.9.1.2</c3p0.version> 7 <mysql.version>5.1.6</mysql.version> 8 </properties> 9 10 <dependencies> 11 <!-- junit单元测试 --> 12 <dependency> 13 <groupId>junit</groupId> 14 <artifactId>junit</artifactId> 15 <version>4.12</version> 16 <scope>test</scope> 17 </dependency> 18 19 <!-- spring beg --> 20 <dependency> 21 <groupId>org.aspectj</groupId> 22 <artifactId>aspectjweaver</artifactId> 23 <version>1.6.8</version> 24 </dependency> 25 26 <dependency> 27 <groupId>org.springframework</groupId> 28 <artifactId>spring-aop</artifactId> 29 <version>${spring.version}</version> 30 </dependency> 31 32 <dependency> 33 <groupId>org.springframework</groupId> 34 <artifactId>spring-context</artifactId> 35 <version>${spring.version}</version> 36 </dependency> 37 38 <dependency> 39 <groupId>org.springframework</groupId> 40 <artifactId>spring-context-support</artifactId> 41 <version>${spring.version}</version> 42 </dependency> 43 44 <!-- spring对orm框架的支持包--> 45 <dependency> 46 <groupId>org.springframework</groupId> 47 <artifactId>spring-orm</artifactId> 48 <version>${spring.version}</version> 49 </dependency> 50 51 <dependency> 52 <groupId>org.springframework</groupId> 53 <artifactId>spring-beans</artifactId> 54 <version>${spring.version}</version> 55 </dependency> 56 57 <dependency> 58 <groupId>org.springframework</groupId> 59 <artifactId>spring-core</artifactId> 60 <version>${spring.version}</version> 61 </dependency> 62 63 <!-- spring end --> 64 65 <!-- hibernate beg --> 66 <!--hiberate核心包--> 67 <dependency> 68 <groupId>org.hibernate</groupId> 69 <artifactId>hibernate-core</artifactId> 70 <version>${hibernate.version}</version> 71 </dependency> 72 <!--hibernate对jpa的实现--> 73 <dependency> 74 <groupId>org.hibernate</groupId> 75 <artifactId>hibernate-entitymanager</artifactId> 76 <version>${hibernate.version}</version> 77 </dependency> 78 <!--验证包--> 79 <dependency> 80 <groupId>org.hibernate</groupId> 81 <artifactId>hibernate-validator</artifactId> 82 <version>5.2.1.Final</version> 83 </dependency> 84 <!-- hibernate end --> 85 86 <!-- c3p0 beg --> 87 <dependency> 88 <groupId>c3p0</groupId> 89 <artifactId>c3p0</artifactId> 90 <version>${c3p0.version}</version> 91 </dependency> 92 <!-- c3p0 end --> 93 94 <!-- log end --> 95 <dependency> 96 <groupId>log4j</groupId> 97 <artifactId>log4j</artifactId> 98 <version>${log4j.version}</version> 99 </dependency> 100 101 <dependency> 102 <groupId>org.slf4j</groupId> 103 <artifactId>slf4j-api</artifactId> 104 <version>${slf4j.version}</version> 105 </dependency> 106 107 <dependency> 108 <groupId>org.slf4j</groupId> 109 <artifactId>slf4j-log4j12</artifactId> 110 <version>${slf4j.version}</version> 111 </dependency> 112 <!-- log end --> 113 114 115 <dependency> 116 <groupId>mysql</groupId> 117 <artifactId>mysql-connector-java</artifactId> 118 <version>${mysql.version}</version> 119 </dependency> 120 121 <!-- spring data jpa 的坐标--> 122 <dependency> 123 <groupId>org.springframework.data</groupId> 124 <artifactId>spring-data-jpa</artifactId> 125 <version>1.9.0.RELEASE</version> 126 </dependency> 127 128 <dependency> 129 <groupId>org.springframework</groupId> 130 <artifactId>spring-test</artifactId> 131 <version>${spring.version}</version> 132 </dependency> 133 134 <!-- el beg 使用spring data jpa 必须引入 --> 135 <dependency> 136 <groupId>javax.el</groupId> 137 <artifactId>javax.el-api</artifactId> 138 <version>2.2.4</version> 139 </dependency> 140 141 <dependency> 142 <groupId>org.glassfish.web</groupId> 143 <artifactId>javax.el</artifactId> 144 <version>2.2.4</version> 145 </dependency> 146 <!-- el end --> 147 148 <dependency> 149 <groupId>org.projectlombok</groupId> 150 <artifactId>lombok</artifactId> 151 <version>1.18.8</version> 152 <scope>provided</scope> 153 </dependency> 154 </dependencies>
2.2.2 整合Spring Data JPA与Spring
applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 10 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 12 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 13 http://www.springframework.org/schema/data/jpa 14 http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> 15 16 <!--spring 和 spring data jpa的配置--> 17 18 <!-- 1.创建entityManagerFactory对象交给spring容器管理--> 19 <bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 20 <property name="dataSource" ref="dataSource" /> 21 <!--配置的扫描的包(实体类所在的包) --> 22 <property name="packagesToScan" value="cn.itcast.domain" /> 23 <!-- jpa的实现厂家 --> 24 <property name="persistenceProvider"> 25 <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/> 26 </property> 27 28 <!--jpa的供应商适配器 --> 29 <property name="jpaVendorAdapter"> 30 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 31 <!--配置是否自动创建数据库表 (false:不自动创建) --> 32 <property name="generateDdl" value="false" /> 33 <!--指定数据库类型(MYSQL必须大写) --> 34 <property name="database" value="MYSQL" /> 35 <!--数据库方言:支持的特有语法 --> 36 <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /> 37 <!--是否显示sql --> 38 <property name="showSql" value="true" /> 39 </bean> 40 </property> 41 42 <!--jpa的方言 :高级的特性 --> 43 <property name="jpaDialect" > 44 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> 45 </property> 46 47 </bean> 48 49 <!--2.创建数据库连接池 --> 50 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 51 <property name="user" value="root"></property> 52 <property name="password" value="root"></property> 53 <property name="jdbcUrl" value="jdbc:mysql:///jpa" ></property> 54 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 55 </bean> 56 57 <!--3.整合spring dataJpa--> 58 <jpa:repositories base-package="cn.itcast.dao" transaction-manager-ref="transactionManager" 59 entity-manager-factory-ref="entityManagerFactoty" ></jpa:repositories> 60 61 <!--4.配置事务管理器 --> 62 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 63 <property name="entityManagerFactory" ref="entityManagerFactoty"></property> 64 </bean> 65 66 <!-- 4.txAdvice--> 67 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 68 <tx:attributes> 69 <tx:method name="save*" propagation="REQUIRED"/> 70 <tx:method name="insert*" propagation="REQUIRED"/> 71 <tx:method name="update*" propagation="REQUIRED"/> 72 <tx:method name="delete*" propagation="REQUIRED"/> 73 <tx:method name="get*" read-only="true"/> 74 <tx:method name="find*" read-only="true"/> 75 <tx:method name="*" propagation="REQUIRED"/> 76 </tx:attributes> 77 </tx:advice> 78 79 <!-- 5.aop--> 80 <aop:config> 81 <aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" /> 82 <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /> 83 </aop:config> 84 85 86 <!--5.声明式事务 --> 87 88 <!-- 6. 配置包扫描--> 89 <context:component-scan base-package="cn.itcast" ></context:component-scan> 90 </beans>
带有声明式事务的配置:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd 10 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd 12 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd 13 http://www.springframework.org/schema/data/jpa 14 http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> 15 16 <!-- 1.dataSource 配置数据库连接池--> 17 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 18 <property name="driverClass" value="com.mysql.jdbc.Driver" /> 19 <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa" /> 20 <property name="user" value="root" /> 21 <property name="password" value="111111" /> 22 </bean> 23 24 <!-- 2.配置entityManagerFactory --> 25 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 26 <property name="dataSource" ref="dataSource" /> 27 <property name="packagesToScan" value="cn.itcast.entity" /> 28 <property name="persistenceProvider"> 29 <bean class="org.hibernate.jpa.HibernatePersistenceProvider" /> 30 </property> 31 <!--JPA的供应商适配器--> 32 <property name="jpaVendorAdapter"> 33 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 34 <property name="generateDdl" value="false" /> 35 <property name="database" value="MYSQL" /> 36 <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /> 37 <property name="showSql" value="true" /> 38 </bean> 39 </property> 40 <property name="jpaDialect"> 41 <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> 42 </property> 43 </bean> 44 45 46 <!-- 3.事务管理器--> 47 <!-- JPA事务管理器 --> 48 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 49 <property name="entityManagerFactory" ref="entityManagerFactory" /> 50 </bean> 51 52 <!-- 整合spring data jpa--> 53 <jpa:repositories base-package="cn.itcast.dao" 54 transaction-manager-ref="transactionManager" 55 entity-manager-factory-ref="entityManagerFactory"></jpa:repositories> 56 57 <!-- 4.txAdvice--> 58 <tx:advice id="txAdvice" transaction-manager="transactionManager"> 59 <tx:attributes> 60 <tx:method name="save*" propagation="REQUIRED"/> 61 <tx:method name="insert*" propagation="REQUIRED"/> 62 <tx:method name="update*" propagation="REQUIRED"/> 63 <tx:method name="delete*" propagation="REQUIRED"/> 64 <tx:method name="get*" read-only="true"/> 65 <tx:method name="find*" read-only="true"/> 66 <tx:method name="*" propagation="REQUIRED"/> 67 </tx:attributes> 68 </tx:advice> 69 70 <!-- 5.aop--> 71 <aop:config> 72 <aop:pointcut id="pointcut" expression="execution(* cn.itcast.service.*.*(..))" /> 73 <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /> 74 </aop:config> 75 76 <context:component-scan base-package="cn.itcast"></context:component-scan> 77 78 <!--组装其它 配置文件--> 79 80 </beans>
2.2.3 使用JPA注解配置映射关系
我们使用案例中的Customer实体类对象,已经配置好了映射关系
Customer.java
1 package cn.itcast.domain; 2 3 import lombok.Getter; 4 import lombok.Setter; 5 import lombok.ToString; 6 7 import javax.persistence.*; 8 9 /** 10 * 1.实体类和表的映射关系 11 * @Eitity 12 * @Table 13 * 2.类中属性和表中字段的映射关系 14 * @Id 15 * @GeneratedValue 16 * @Column 17 */ 18 /** 19 * 20 * * 所有的注解都是使用JPA的规范提供的注解, 21 * * 所以在导入注解包的时候,一定要导入javax.persistence下的 22 */ 23 @Entity 24 @Table(name="cst_customer") 25 @Getter 26 @Setter 27 @ToString 28 public class Customer { 29 30 @Id 31 @GeneratedValue(strategy = GenerationType.IDENTITY) 32 @Column(name = "cust_id") 33 private Long custId; 34 @Column(name = "cust_address") 35 private String custAddress; 36 @Column(name = "cust_industry") 37 private String custIndustry; 38 @Column(name = "cust_level") 39 private String custLevel; 40 @Column(name = "cust_name") 41 private String custName; 42 @Column(name = "cust_phone") 43 private String custPhone; 44 @Column(name = "cust_source") 45 private String custSource; 46 47 }
2.3 使用Spring Data JPA完成需求
2.3.1 编写符合Spring Data JPA规范的Dao层接口
Spring Data JPA是spring提供的一款对于数据访问层(Dao层)的框架,使用Spring Data JPA,只需要按照框架的规范提供dao接口,不需要实现类就可以完成数据库的增删改查、分页查询等方法的定义,极大的简化了我们的开发过程。
在Spring Data JPA中,对于定义符合规范的Dao层接口,我们只需要遵循以下几点就可以了:
1.创建一个Dao层接口,并实现JpaRepository和JpaSpecificationExecutor
2.提供相应的泛型
CustomerDao.java
1 package cn.itcast.dao; 2 3 import cn.itcast.domain.Customer; 4 import org.springframework.data.jpa.repository.JpaRepository; 5 import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 6 import org.springframework.data.jpa.repository.Modifying; 7 import org.springframework.data.jpa.repository.Query; 8 import org.springframework.data.repository.query.Param; 9 10 import java.util.List; 11 12 /** 13 * 符合SpringDataJpa的dao层接口规范 14 * JpaRepository<操作的实体类类型,实体类中主键属性的类型> 15 * * 封装了基本CRUD操作 16 * JpaSpecificationExecutor<操作的实体类类型> 17 * * 封装了复杂查询(如分页) 18 */ 19 //该写法已经具备了基本的增删改查 20 public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { 21 22 /** 23 * 案例:根据客户名称查询客户 24 * 使用jpql的形式查询 25 * jpql:from Customer where custName = ? 26 * 27 * 配置jpql语句,使用的@Query注解 28 */ 29 @Query(value="from Customer where custName = ?") 30 public Customer findJpql(String custName); 31 32 //返回多条 33 @Query(value="from Customer where custName = ?") 34 public List<Customer> findAllByCustName(String custName); 35 36 /** 37 * 案例:根据客户名称和客户id查询客户 38 * jpql: from Customer where custName = ? and custId = ? 39 * 40 * 对于多个占位符参数 41 * 赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致 42 * 43 * 可以指定占位符参数的位置 44 * ? 索引的方式,指定此占位的取值来源 45 */ 46 @Query(value = "from Customer where custName = ?2 and custId = ?1") 47 public Customer findCustNameAndId(Long id,String name); 48 49 //@Query(value = "from Customer where custName = ? and custId = ?") 50 //public Customer findCustNameAndId(String name,Long id); 51 52 //@Query(value = "from Customer where custName = :name and custId = :id") 53 //public Customer findCustNameAndId(@Param("id") Long id, @Param("name") String name); 54 55 /** 56 * 使用jpql完成更新操作 57 * 案例 : 根据id更新,客户的名称 58 * 更新4号客户的名称,将名称改为“黑马程序员” 59 * 60 * sql :update cst_customer set cust_name = ? where cust_id = ? 61 * jpql : update Customer set custName = ? where custId = ? 62 * 63 * @Query : 代表的是进行查询 64 * * 声明此方法是用来进行更新操作 65 * @Modifying 66 * * 当前执行的是一个更新操作 67 * 68 */ 69 @Query(value = " update Customer set custName = ?2 where custId = ?1 ") 70 @Modifying 71 public void updateCustomer(long custId,String custName); 72 73 /** 74 * 使用sql的形式查询: 75 * 查询全部的客户 76 * sql : select * from cst_customer; 77 * Query : 配置sql查询 78 * value : sql语句 79 * nativeQuery : 查询方式 80 * true : 本地sql查询 81 * false:jpql查询 82 * 83 */ 84 //@Query(value = " select * from cst_customer" ,nativeQuery = true) 85 @Query(value="select * from cst_customer where cust_name like ?1",nativeQuery = true) 86 public List<Object [] > findSql(String name); 87 //@Query(value="select * from cst_customer where cust_name like :name",nativeQuery = true) 88 //public List<Object [] > findSql(@Param("name") String name); 89 90 91 /** 92 * 方法名的约定: 93 * findBy : 查询 94 * 对象中的属性名(首字母大写) : 查询的条件 95 * CustName 96 * * 默认情况 : 使用 等于的方式查询 97 * 特殊的查询方式 98 * 99 * findByCustName -- 根据客户名称查询 100 * 101 * 再springdataJpa的运行阶段 102 * 会根据方法名称进行解析 findBy from xxx(实体类) 103 * 属性名称 where custName = 104 * 105 * 1.findBy + 属性名称 (根据属性名称进行完成匹配的查询=) 106 * 2.findBy + 属性名称 + “查询方式(Like | isnull)” 107 * findByCustNameLike 108 * 3.多条件查询 109 * findBy + 属性名 + “查询方式” + “多条件的连接符(and|or)” + 属性名 + “查询方式” 110 */ 111 //public Customer findByCustName(String custName); 112 public List<Customer> findByCustName(String custName); 113 114 public List<Customer> findByCustNameLike(String custName); 115 116 //使用客户名称模糊匹配和客户所属行业精准匹配的查询(参数顺序必须正确) 117 public Customer findByCustNameLikeAndCustIndustry(String custName,String custIndustry); 118 }
2.3.2 完成基本CRUD操作
完成了Spring Data JPA的环境搭建,并且编写了符合Spring Data JPA 规范的Dao层接口之后,就可以使用定义好的Dao层接口进行客户的基本CRUD操作
CustomerDaoTest.java
1 package cn.itcast.test; 2 3 import cn.itcast.dao.CustomerDao; 4 import cn.itcast.domain.Customer; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.test.context.ContextConfiguration; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 import org.springframework.transaction.annotation.Transactional; 11 12 import java.util.List; 13 14 @RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境 15 @ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息 16 public class CustomerDaoTest { 17 18 @Autowired 19 private CustomerDao customerDao; 20 /** 21 * 根据id查询 22 */ 23 @Test 24 public void testFindOne() { 25 Customer customer = customerDao.findOne(4l); 26 System.out.println(customer); 27 } 28 29 /** 30 * save : 保存或者更新 31 * 根据传递的对象是否存在主键id, 32 * 如果没有id主键属性:保存 33 * 存在id主键属性,根据id查询数据,(查到数据后)更新数据 34 */ 35 @Test 36 public void testSave() { 37 Customer customer = new Customer(); 38 customer.setCustName("黑马程序员"); 39 customer.setCustLevel("vip"); 40 customer.setCustIndustry("it教育"); 41 customerDao.save(customer); 42 } 43 44 @Test 45 public void testUpdate() { 46 Customer customer = new Customer(); 47 customer.setCustId(4l); 48 customer.setCustName("黑马程序员很厉害"); 49 customerDao.save(customer); 50 } 51 52 /** 53 * 删除 54 */ 55 @Test 56 public void testDelete () { 57 customerDao.delete(3l); 58 } 59 60 /** 61 * 查询所有 62 */ 63 @Test 64 public void testFindAll() { 65 List<Customer> list = customerDao.findAll(); 66 for(Customer customer : list) { 67 System.out.println(customer); 68 } 69 } 70 71 /** 72 * 测试统计查询:查询客户的总数量 73 * count:统计总条数 74 */ 75 @Test 76 public void testCount() { 77 long count = customerDao.count();//查询全部的客户数量 78 System.out.println(count); 79 } 80 81 /** 82 * 测试:判断id为4的客户是否存在 83 * 1. 可以查询以下id为4的客户 84 * 如果值为空,代表不存在,如果不为空,代表存在 85 * 2. 判断数据库中id为4的客户的数量 86 * 如果数量为0,代表不存在,如果大于0,代表存在 87 */ 88 @Test 89 public void testExists() { 90 boolean exists = customerDao.exists(4l); 91 System.out.println("id为4的客户 是否存在:"+exists); 92 } 93 94 95 /** 96 * 根据id从数据库查询 97 * @Transactional : 保证getOne正常运行 98 * 99 * findOne: 100 * em.find() :立即加载 101 * getOne: 102 * em.getReference :延迟加载 103 * * 返回的是一个客户的动态代理对象 104 * * 什么时候用,什么时候查询 105 */ 106 @Test 107 @Transactional 108 public void testGetOne() { 109 Customer customer = customerDao.getOne(4l); 110 System.out.println(customer); 111 } 112 }
第3章 Spring Data JPA的内部原理剖析
3.1 Spring Data JPA的常用接口分析
在客户的案例中,我们发现在自定义的CustomerDao中,并没有提供任何方法就可以使用其中的很多方法,那么这些方法究竟是怎么来的呢?答案很简单,对于我们自定义的Dao接口,由于继承了JpaRepository和JpaSpecificationExecutor,所以我们可以使用这两个接口的所有方法。
在使用Spring Data JPA时,一般实现JpaRepository和JpaSpecificationExecutor接口,这样就可以使用这些接口中定义的方法,但是这些方法都只是一些声明,没有具体的实现方式,那么在 Spring Data JPA中它又是怎么实现的呢?
3.2 Spring Data JPA的实现过程
通过对客户案例,以debug断点调试的方式,通过分析Spring Data JPA的原来来分析程序的执行过程
我们以findOne方法为例进行分析
- 代理子类的实现过程
断点执行到方法上时,我们可以发现注入的customerDao对象,本质上是通过JdkDynamicAopProxy生成的一个代理对象
- 代理对象中方法调用的分析
当程序执行的时候,会通过JdkDynamicAopProxy的invoke方法,对customerDao对象生成动态代理对象。根据对Spring Data JPA介绍而知,要想进行findOne查询方法,最终还是会出现JPA规范的API完成操作,那么这些底层代码存在于何处呢?答案很简单,都隐藏在通过JdkDynamicAopProxy生成的动态代理对象当中,而这个动态代理对象就是SimpleJpaRepository
通过SimpleJpaRepository的源码分析,定位到了findOne方法,在此方法中,返回em.find()的返回结果,那么em又是什么呢?
带着问题继续查找em对象,我们发现em就是EntityManager对象,而他是JPA原生的实现方式,所以我们得到结论Spring Data JPA只是对标准JPA操作进行了进一步封装,简化了Dao层代码的开发
3.3 Spring Data JPA完整的调用过程分析
第4章 Spring Data JPA的查询方式
4.1 使用Spring Data JPA中接口定义的方法进行查询
在继承JpaRepository,和JpaRepository接口后,我们就可以使用接口中定义的方法进行查询
- 继承JpaRepository后的方法列表
- 继承JpaSpecificationExecutor的方法列表
4.2 使用JPQL的方式查询
使用Spring Data JPA提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来说,我们还需要灵活的构造查询条件,这时就可以使用@Query注解,结合JPQL的语句方式完成查询
@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个JPQL查询语句即可
JpqlTest.java
1 package cn.itcast.test; 2 3 import cn.itcast.dao.CustomerDao; 4 import cn.itcast.domain.Customer; 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.test.annotation.Rollback; 9 import org.springframework.test.context.ContextConfiguration; 10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 import org.springframework.transaction.annotation.Transactional; 12 13 import java.util.Arrays; 14 import java.util.List; 15 16 @RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境 17 @ContextConfiguration(locations = "classpath:applicationContext.xml")//指定spring容器的配置信息 18 public class JpqlTest { 19 20 @Autowired 21 private CustomerDao customerDao; 22 23 @Test 24 public void testFindJPQL() { 25 Customer customer = customerDao.findJpql("传智播客"); 26 System.out.println(customer); 27 } 28 29 @Test 30 public void testFindAllJPQL() { 31 List<Customer> customerList = customerDao.findAllByCustName("传智播客"); 32 for(Customer c : customerList){ 33 System.out.println(c); 34 } 35 //System.out.println(customer); 36 } 37 38 @Test 39 public void testFindCustNameAndId() { 40 // Customer customer = customerDao.findCustNameAndId("传智播客",1l); 41 Customer customer = customerDao.findCustNameAndId(1l,"传智播客"); 42 System.out.println(customer); 43 } 44 45 /** 46 * 测试jpql的更新操作 47 * * springDataJpa中使用jpql完成 更新/删除操作 48 * * 需要手动添加事务的支持 49 * * 默认会执行结束之后,回滚事务 50 * @Rollback : 设置是否自动回滚 51 * false | true 52 */ 53 @Test 54 @Transactional //添加事务的支持 55 @Rollback(value = false) 56 public void testUpdateCustomer() { 57 customerDao.updateCustomer(4l,"黑马程序员"); 58 } 59 60 //测试sql查询 61 @Test 62 public void testFindSql() { 63 List<Object[]> list = customerDao.findSql("传智播客%"); 64 for(Object [] obj : list) { 65 System.out.println(Arrays.toString(obj)); 66 } 67 } 68 69 //测试方法命名规则的查询 70 @Test 71 public void testNaming() { 72 //Customer customer = customerDao.findByCustName("传智播客"); 73 //System.out.println(customer); 74 List<Customer> list = customerDao.findByCustName("传智播客"); 75 System.out.println(list); 76 } 77 78 //测试方法命名规则的查询 79 @Test 80 public void testFindByCustNameLike() { 81 List<Customer> list = customerDao.findByCustNameLike("传智播客%"); 82 for (Customer customer : list) { 83 System.out.println(customer); 84 } 85 } 86 87 //测试方法命名规则的查询 88 @Test 89 public void testFindByCustNameLikeAndCustIndustry() { 90 Customer customer = customerDao.findByCustNameLikeAndCustIndustry("传智播客1%", "it教育"); 91 System.out.println(customer); 92 } 93 94 95 }
4.3 使用SQL语句查询
Spring Data JPA同样也支持sql语句的查询,如下:
1 /** 2 * 使用sql的形式查询: 3 * 查询全部的客户 4 * sql : select * from cst_customer; 5 * Query : 配置sql查询 6 * value : sql语句 7 * nativeQuery : 查询方式 8 * true : 本地sql查询 9 * false:jpql查询 10 * 11 */ 12 @Query(value="select * from cst_customer",nativeQuery=true) 13 public void findSql();
4.4 方法命名规则查询
顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询
按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。
//方法命名方式查询(根据客户名称查询客户) public Customer findByCustName(String custName);
具体的关键字,使用方法和生产成SQL如下表所示