首先,抛开Spring,先来谈谈JPA。
1.JPA是什么?
JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
说到底还是一个ORM框架,不过是Sun为了希望整合所有的ORM框架而推出的规范,总的来说没有什么大的区别。依旧是是开发者从复杂的SQL与JDBC中脱离出来。
2.实际中使用JPA
首先在数据库中建库与表,MySQL中脚本如下
1 CREATE DATABASE jpa; 2 USE jpa; 3 CREATE TABLE `user` ( 4 `id` bigint(20) NOT NULL AUTO_INCREMENT, 5 `firstName` varchar(45) NOT NULL, 6 `phoneNo` varchar(45) NOT NULL, 7 `lastName` varchar(45) NOT NULL, 8 `birthday` date NOT NULL, 9 PRIMARY KEY (`id`), 10 UNIQUE KEY `id_UNIQUE` (`id`), 11 UNIQUE KEY `phoneNo_UNIQUE` (`phoneNo`) 12 ) ENGINE=InnoDB DEFAULT CHARSET=UTF-8;
JPA标准配置文件,使用Hibernate实现 含每行注释(注意 一定要放置在src下的META-INF目录下,十分坑爹!!)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> 6 7 <!-- 8 Name属性用于定义持久化单元的名字 (name必选,空值也合法); 9 transaction-type 指定事务类型(可选) 10 --> 11 <persistence-unit name="TestJPAUnit" transaction-type="RESOURCE_LOCAL"> 12 13 <!-- 描述信息.(可选) 14 <description> </description> 15 --> 16 <!-- javax.persistence.PersistenceProvider接口的一个实现类(可选) 17 <provider> </provider> 18 --> 19 <!-- Jta-data-source和 non-jta-data-source用于分别指定持久化提供商使用的JTA和/或non-JTA数据源的全局JNDI名称(可选) 20 <jta-data-source>java:/MySqlDS</jta-data-source> 21 <non-jta-data-source> </non-jta-data-source> 22 --> 23 <!-- 声明orm.xml所在位置.(可选) 24 <mapping-file>product.xml</mapping-file> 25 --> 26 <!-- 以包含persistence.xml的jar文件为基准的相对路径,添加额外的jar文件.(可选) 27 <jar-file>../lib/model.jar</jar-file> 28 --> 29 <!-- 显式列出实体类,在Java SE 环境中应该显式列出.(可选) 30 <class>com.domain.User</class> 31 <class>com.domain.Product</class> 32 --> 33 <!-- 声明是否扫描jar文件中标注了@Enity类加入到上下文.若不扫描,则如下:(可选) 34 <exclude-unlisted-classes/> 35 --> 36 <!-- 厂商专有属性(可选) --> 37 <properties> 38 <!-- hibernate.hbm2ddl.auto= create-drop / create / update --> 39 <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" /> 40 <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> 41 <property name="hibernate.connection.username" value="remote" /> 42 <property name="hibernate.connection.password" value="feiyue" /> 43 <property name="hibernate.connection.url" value="jdbc:mysql://192.168.182.131:3306/jpa" /> 44 <property name="hibernate.max_fetch_depth" value="3" /> 45 <property name="hibernate.show_sql" value="true" /> 46 <property name="hibernate.hbm2ddl.auto" value="update"/> 47 </properties> 48 49 </persistence-unit> 50 51 </persistence>
使用Maven搭建一个测试环境,Maven的使用在这里不再赘述,这里显示所依赖的jar包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.2</version> </dependency> </dependencies>
搭建一个测试项目 如图
Entity 实体类 User.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.demo.bean; 2 3 // Generated 2016-3-22 17:50:11 by Hibernate Tools 4.0.0 4 5 import java.util.Date; 6 import javax.persistence.Column; 7 import javax.persistence.Entity; 8 import javax.persistence.GeneratedValue; 9 import static javax.persistence.GenerationType.IDENTITY; 10 import javax.persistence.Id; 11 import javax.persistence.Table; 12 import javax.persistence.Temporal; 13 import javax.persistence.TemporalType; 14 import javax.persistence.UniqueConstraint; 15 16 /** 17 * User generated by hbm2java 18 */ 19 @Entity 20 @Table(name = "user", catalog = "jpa", uniqueConstraints = @UniqueConstraint(columnNames = "phoneNo")) 21 public class User implements java.io.Serializable { 22 23 /** 24 * 25 */ 26 private static final long serialVersionUID = -2126972038126149900L; 27 private Long id; 28 private String firstName; 29 private String phoneNo; 30 private String lastName; 31 private Date birthday; 32 33 public User() { 34 } 35 36 public User(String firstName, String phoneNo, String lastName, Date birthday) { 37 this.firstName = firstName; 38 this.phoneNo = phoneNo; 39 this.lastName = lastName; 40 this.birthday = birthday; 41 } 42 43 @Id 44 @GeneratedValue(strategy = IDENTITY) 45 @Column(name = "id", unique = true, nullable = false) 46 public Long getId() { 47 return this.id; 48 } 49 50 public void setId(Long id) { 51 this.id = id; 52 } 53 54 @Column(name = "firstName", nullable = false, length = 45) 55 public String getFirstName() { 56 return this.firstName; 57 } 58 59 public void setFirstName(String firstName) { 60 this.firstName = firstName; 61 } 62 63 @Column(name = "phoneNo", unique = true, nullable = false, length = 45) 64 public String getPhoneNo() { 65 return this.phoneNo; 66 } 67 68 public void setPhoneNo(String phoneNo) { 69 this.phoneNo = phoneNo; 70 } 71 72 @Column(name = "lastName", nullable = false, length = 45) 73 public String getLastName() { 74 return this.lastName; 75 } 76 77 public void setLastName(String lastName) { 78 this.lastName = lastName; 79 } 80 81 @Temporal(TemporalType.DATE) 82 @Column(name = "birthday", nullable = false, length = 10) 83 public Date getBirthday() { 84 return this.birthday; 85 } 86 87 public void setBirthday(Date birthday) { 88 this.birthday = birthday; 89 } 90 91 @Override 92 public String toString(){ 93 return "User:"+ 94 "Id:"+id+" Name:"+firstName+" "+lastName+""; 95 } 96 97 }
Junit4 测试客户端,这里只简单的操作了保存与创建简单的HQL查询。JPATest.java
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.demo.client; 2 3 import java.util.Date; 4 import java.util.List; 5 6 import javax.persistence.EntityManager; 7 import javax.persistence.EntityManagerFactory; 8 import javax.persistence.Persistence; 9 10 import org.junit.Test; 11 12 import com.demo.bean.User; 13 14 public class JPATest { 15 16 @Test 17 public void testSave(){ 18 EntityManagerFactory factory = Persistence.createEntityManagerFactory("TestJPAUnit"); 19 EntityManager em = factory.createEntityManager(); 20 em.getTransaction().begin(); 21 User user = new User(); //user为new状态 22 user.setBirthday(new Date()); 23 user.setFirstName("Khalid"); 24 user.setLastName("Khalid"); 25 user.setPhoneNo("+8618888888889"); 26 em.persist(user);//持久化user实例 27 em.getTransaction().commit();//提交事务 28 em.close(); 29 factory.close(); 30 } 31 32 @Test 33 public void testQuery(){ 34 EntityManagerFactory factory = Persistence.createEntityManagerFactory("TestJPAUnit"); 35 EntityManager em = factory.createEntityManager(); 36 @SuppressWarnings("unchecked") 37 List<User> user=em.createQuery("from User").getResultList(); 38 System.out.println(user); 39 } 40 }
运行客户端就可以啦~~其实并没有什么特别的- -。
3.Spring对JPA的支持
前人总结的很好,我就不在赘述了
Spring 框架对 JPA 提供的支持主要体现在如下几个方面:
-
首先,它使得 JPA 配置变得更加灵活。JPA 规范要求,配置文件必须命名为 persistence.xml,并存在于类路径下的 META-INF 目录中。该文件通常包含了初始化 JPA 引擎所需的全部信息。Spring 提供的 LocalContainerEntityManagerFactoryBean 提供了非常灵活的配置,persistence.xml 中的信息都可以在此以属性注入的方式提供。
-
其次,Spring 实现了部分在 EJB 容器环境下才具有的功能,比如对 @PersistenceContext、@PersistenceUnit 的容器注入支持。
-
第三,也是最具意义的,Spring 将 EntityManager 的创建与销毁、事务管理等代码抽取出来,并由其统一管理,开发者不需要关心这些,业务方法中只剩下操作领域对象的代码,事务管理和 EntityManager 创建、销毁的代码都不再需要开发者关心了。
4.使用SpringDataJPA 让一切变得最为简单
使用Spring Data JPA框架,它会帮助你简化Spring唯一没有控制到的DAO层,甚至让你完全不用编写DAO层繁琐的业务逻辑,你仅仅需要做的只有声明接口。
不多说直接上代码!!!
首先依旧是建库与表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 CREATE DATABASE sdjpa; 2 USE sdjpa; 3 CREATE TABLE `user` ( 4 `id` bigint(20) NOT NULL AUTO_INCREMENT, 5 `firstName` varchar(45) NOT NULL, 6 `phoneNo` varchar(45) NOT NULL, 7 `lastName` varchar(45) NOT NULL, 8 `birthday` date NOT NULL, 9 PRIMARY KEY (`id`), 10 UNIQUE KEY `id_UNIQUE` (`id`), 11 UNIQUE KEY `phoneNo_UNIQUE` (`phoneNo`) 12 ) ENGINE=InnoDB DEFAULT CHARSET=UTF-8;
既然是Spring,applicationContext.xml内有关SpringDataJPA的相关配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:tx="http://www.springframework.org/schema/tx" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jpa="http://www.springframework.org/schema/data/jpa" 6 xmlns:task="http://www.springframework.org/schema/task" 7 xmlns:aop="http://www.springframework.org/schema/aop" 8 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 9 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 10 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 11 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd 12 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd 13 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" 14 default-lazy-init="true"> 15 16 17 <!-- 如果spring用了jpa,并且类型为LocalContainerEntityManagerFactoryBean,则组件注册在此配置文件出现即可,其余配置文件可忽略 18 使用component来替代annotation 自动注册bean, 并保证@Required、@Autowired的属性被注入 --> 19 <context:component-scan base-package="com.**.client,com.demo.dao"/> 20 21 <!-- 数据源--> 22 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 23 <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 24 <property name="url" value="jdbc:mysql://192.168.182.131/sdjpa"/> 25 <property name="username" value="root" /> 26 <property name="password" value="feiyue" /> 27 </bean> 28 29 <!-- Hibernate对Jpa的实现 --> 30 <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/> 31 32 <!-- 定义实体管理器工厂 33 Jpa配置 LocalContainerEntityManagerFactoryBean这个选项Spring扮演了容器的角色。完全掌管JPA --> 34 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 35 <!-- 指定数据源 --> 36 <property name="dataSource" ref="dataSource"/> 37 <!-- 指定Jpa持久化实现厂商类,这里以Hibernate为例 --> 38 <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter"/> 39 <!-- 指定Entity实体类包路径 --> 40 <property name="packagesToScan" > 41 <array> 42 <value>com.**.bean</value> 43 </array> 44 </property> 45 <!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 --> 46 <property name="jpaProperties"> 47 <props> 48 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop> 49 <!-- EJB3规范的命名实现 例如 firstName 会被映射为 first_name 建议取消 50 <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> 51 --> 52 <prop key="hibernate.show_sql">true</prop> 53 <prop key="hibernate.format_sql">true</prop> 54 <!-- 也可以配置为update 自动生成表 我这里在数据库已经建好了 就设置为validate--> 55 <prop key="hibernate.hbm2ddl.auto">update</prop> 56 </props> 57 </property> 58 </bean> 59 60 <!-- 重要配置:启用扫描并自动创建代理的功能 --> 61 <jpa:repositories base-package="com.**.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager"/> 62 63 64 65 <!-- Jpa 事务管理器 --> 66 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 67 <property name="entityManagerFactory" ref="entityManagerFactory"/> 68 </bean> 69 70 <!-- 开启注解事务 --> 71 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> 72 73 74 <!-- 启动对@AspectJ(面向切面)注解的支持 --> 75 <aop:aspectj-autoproxy /> 76 77 </beans>
依旧是Maven,这次的依赖库
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <dependencies> 2 <dependency> 3 <groupId>log4j</groupId> 4 <artifactId>log4j</artifactId> 5 <version>1.2.17</version> 6 </dependency> 7 8 <dependency> 9 <groupId>com.fasterxml.jackson.core</groupId> 10 <artifactId>jackson-databind</artifactId> 11 <version>2.4.5</version> 12 </dependency> 13 14 <dependency> 15 <groupId>com.fasterxml.jackson.core</groupId> 16 <artifactId>jackson-annotations</artifactId> 17 <version>2.4.5</version> 18 </dependency> 19 20 <dependency> 21 <groupId>com.fasterxml.jackson.core</groupId> 22 <artifactId>jackson-core</artifactId> 23 <version>2.4.5</version> 24 </dependency> 25 26 27 <dependency> 28 <groupId>aspectj</groupId> 29 <artifactId>aspectjweaver</artifactId> 30 <version>1.5.4</version> 31 </dependency> 32 <dependency> 33 <groupId>org.springframework</groupId> 34 <artifactId>spring-core</artifactId> 35 <version>4.1.6.RELEASE</version> 36 </dependency> 37 38 <dependency> 39 <groupId>org.springframework</groupId> 40 <artifactId>spring-aop</artifactId> 41 <version>4.1.6.RELEASE</version> 42 </dependency> 43 44 <dependency> 45 <groupId>org.springframework.data</groupId> 46 <artifactId>spring-data-jpa</artifactId> 47 <version>1.7.0.RELEASE</version> 48 </dependency> 49 50 <dependency> 51 <groupId>org.springframework</groupId> 52 <artifactId>spring-beans</artifactId> 53 <version>4.1.6.RELEASE</version> 54 </dependency> 55 56 <dependency> 57 <groupId>org.springframework</groupId> 58 <artifactId>spring-context</artifactId> 59 <version>4.1.6.RELEASE</version> 60 </dependency> 61 62 <dependency> 63 <groupId>org.springframework</groupId> 64 <artifactId>spring-context-support</artifactId> 65 <version>4.1.6.RELEASE</version> 66 </dependency> 67 68 <dependency> 69 <groupId>org.springframework</groupId> 70 <artifactId>spring-jdbc</artifactId> 71 <version>4.1.6.RELEASE</version> 72 </dependency> 73 74 <dependency> 75 <groupId>org.springframework</groupId> 76 <artifactId>spring-expression</artifactId> 77 <version>4.1.6.RELEASE</version> 78 </dependency> 79 80 <dependency> 81 <groupId>org.springframework</groupId> 82 <artifactId>spring-tx</artifactId> 83 <version>4.1.6.RELEASE</version> 84 </dependency> 85 86 <dependency> 87 <groupId>org.springframework</groupId> 88 <artifactId>spring-test</artifactId> 89 <version>4.1.6.RELEASE</version> 90 </dependency> 91 92 <dependency> 93 <groupId>org.springframework</groupId> 94 <artifactId>spring-orm</artifactId> 95 <version>4.1.6.RELEASE</version> 96 </dependency> 97 <dependency> 98 <groupId>org.springframework</groupId> 99 <artifactId>spring-webmvc</artifactId> 100 <version>4.1.6.RELEASE</version> 101 </dependency> 102 <dependency> 103 <groupId>org.hibernate</groupId> 104 <artifactId>hibernate-core</artifactId> 105 <version>4.1.9.Final</version> 106 </dependency> 107 108 <dependency> 109 <groupId>org.hibernate</groupId> 110 <artifactId>hibernate-entitymanager</artifactId> 111 <version>4.1.9.Final</version> 112 </dependency> 113 <dependency> 114 <groupId>org.springframework</groupId> 115 <artifactId>spring-web</artifactId> 116 <version>4.1.6.RELEASE</version> 117 </dependency> 118 <dependency> 119 <groupId>commons-dbcp</groupId> 120 <artifactId>commons-dbcp</artifactId> 121 <version>1.4</version> 122 </dependency> 123 124 <dependency> 125 <groupId>commons-codec</groupId> 126 <artifactId>commons-codec</artifactId> 127 <version>1.10</version> 128 </dependency> 129 130 <dependency> 131 <groupId>commons-io</groupId> 132 <artifactId>commons-io</artifactId> 133 <version>1.3.2</version> 134 </dependency> 135 136 <dependency> 137 <groupId>commons-pool</groupId> 138 <artifactId>commons-pool</artifactId> 139 <version>1.6</version> 140 </dependency> 141 142 <dependency> 143 <groupId>mysql</groupId> 144 <artifactId>mysql-connector-java</artifactId> 145 <version>5.1.25</version> 146 </dependency> 147 148 <dependency> 149 <groupId>org.slf4j</groupId> 150 <artifactId>slf4j-log4j12</artifactId> 151 <version>1.7.2</version> 152 </dependency> 153 </dependencies>
搭建环境~
核心DAO层代码 IUserRepository.java 是不是非常简洁,剩下的工作Spring Data JPA框架帮你代理完成
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.demo.dao; 2 3 4 5 import java.util.List; 6 7 import org.springframework.data.jpa.repository.JpaRepository; 8 import org.springframework.data.jpa.repository.Modifying; 9 import org.springframework.data.jpa.repository.Query; 10 import org.springframework.data.repository.CrudRepository; 11 import org.springframework.data.repository.PagingAndSortingRepository; 12 import org.springframework.data.repository.query.Param; 13 import org.springframework.stereotype.Repository; 14 15 import com.demo.bean.User; 16 17 18 19 @Repository 20 public interface IUserRepository extends JpaRepository<User, Long>,CrudRepository<User, Long>, 21 PagingAndSortingRepository<User, Long> 22 { 23 /** 24 * 可使用的接口有: ********** 25 * Repository:是 Spring Data的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法。** 26 * CrudRepository:继承Repository,提供增删改查方法,可以直接调用。 ** 27 * PagingAndSortingRepository:继承CrudRepository,具有分页查询和排序功能(本类实例) ** 28 * JpaRepository: 继承PagingAndSortingRepository,针对JPA技术提供的接口 ** 29 * JpaSpecificationExecutor: 可以执行原生SQL查询 ** 30 * 继承不同的接口,有两个不同的泛型参数,他们是该持久层操作的类对象和主键类型。 ** 31 ********************************************************************************* 32 * */ 33 34 //更新操作 需要注解Modifying 35 @Modifying 36 @Query("update User u set u.firstName=:firstName where u.id=:id") 37 public void updateFirstName(@Param("firstName") String firstName,@Param("id") long id); 38 39 @Query("select u from User u where u.phoneNo=?1") 40 public User findByPhoneno(String phoneNo); 41 42 //nativeQuery 使用SQL查询 自动进行转换 43 @Query(value="select * from user u where u.firstName=?1",nativeQuery=true) 44 public List<User> findByFirstname(String firstName); 45 }
Entity实体类 User.java 注意catalog的变化
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.demo.bean; 2 3 // Generated 2016-3-22 17:50:11 by Hibernate Tools 4.0.0 4 5 import java.util.Date; 6 import javax.persistence.Column; 7 import javax.persistence.Entity; 8 import javax.persistence.GeneratedValue; 9 import static javax.persistence.GenerationType.IDENTITY; 10 import javax.persistence.Id; 11 import javax.persistence.Table; 12 import javax.persistence.Temporal; 13 import javax.persistence.TemporalType; 14 import javax.persistence.UniqueConstraint; 15 16 /** 17 * User generated by hbm2java 18 */ 19 @Entity 20 @Table(name = "user", catalog = "sdjpa", uniqueConstraints = @UniqueConstraint(columnNames = "phoneNo")) 21 public class User implements java.io.Serializable { 22 23 /** 24 * 25 */ 26 private static final long serialVersionUID = -2126972038126149900L; 27 private Long id; 28 private String firstName; 29 private String phoneNo; 30 private String lastName; 31 private Date birthday; 32 33 public User() { 34 } 35 36 public User(String firstName, String phoneNo, String lastName, Date birthday) { 37 this.firstName = firstName; 38 this.phoneNo = phoneNo; 39 this.lastName = lastName; 40 this.birthday = birthday; 41 } 42 43 @Id 44 @GeneratedValue(strategy = IDENTITY) 45 @Column(name = "id", unique = true, nullable = false) 46 public Long getId() { 47 return this.id; 48 } 49 50 public void setId(Long id) { 51 this.id = id; 52 } 53 54 @Column(name = "firstName", nullable = false, length = 45) 55 public String getFirstName() { 56 return this.firstName; 57 } 58 59 public void setFirstName(String firstName) { 60 this.firstName = firstName; 61 } 62 63 @Column(name = "phoneNo", unique = true, nullable = false, length = 45) 64 public String getPhoneNo() { 65 return this.phoneNo; 66 } 67 68 public void setPhoneNo(String phoneNo) { 69 this.phoneNo = phoneNo; 70 } 71 72 @Column(name = "lastName", nullable = false, length = 45) 73 public String getLastName() { 74 return this.lastName; 75 } 76 77 public void setLastName(String lastName) { 78 this.lastName = lastName; 79 } 80 81 @Temporal(TemporalType.DATE) 82 @Column(name = "birthday", nullable = false, length = 10) 83 public Date getBirthday() { 84 return this.birthday; 85 } 86 87 public void setBirthday(Date birthday) { 88 this.birthday = birthday; 89 } 90 91 @Override 92 public String toString(){ 93 return "User:"+ 94 "Id:"+id+" Name:"+firstName+" "+lastName+""; 95 } 96 97 }
为了简单 我通过Spring-test框架以及Junit整合编写了一个测试客户端,没有编写Service层,Spring-test与Junit整合的教程可以点这里,讲的十分详细。使用Spring-test非常大的好处是支持数据库的自动回滚,以至于测试不会破坏数据库的现场,而且可以频繁的测试多组数据。代码如下 TestJPADemoClient.java,基本包括了常用的查询Demo
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package com.demo.client; 2 3 import static org.junit.Assert.*; 4 5 import java.util.Date; 6 7 import javax.annotation.Resource; 8 9 import org.junit.*; 10 import org.junit.runner.RunWith; 11 import org.springframework.data.domain.Page; 12 import org.springframework.data.domain.PageRequest; 13 import org.springframework.data.domain.Sort; 14 import org.springframework.data.domain.Sort.Direction; 15 import org.springframework.test.annotation.Rollback; 16 import org.springframework.test.context.ContextConfiguration; 17 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 18 import org.springframework.transaction.annotation.Transactional; 19 20 import com.demo.bean.User; 21 import com.demo.dao.IUserRepository; 22 23 @RunWith(SpringJUnit4ClassRunner.class) 24 @ContextConfiguration(locations = { "classpath:applicationContext.xml" }) 25 @Transactional 26 public class TestJpaDemoClient { 27 28 @Resource 29 private IUserRepository userRepository; 30 31 // private ApplicationContext applicationContext; 32 33 private static long userId; 34 35 private static User user = null; 36 37 @Before 38 // 插入一些测试数据,与测试方法处于同一事务,自动回滚 39 40 public void setUp() { 41 System.out.println("***************插入测试数据****************"); 42 user = new User(); 43 user.setBirthday(new Date()); 44 user.setFirstName("Fei"); 45 user.setLastName("Yue"); 46 user.setPhoneNo("+8618888888889"); 47 user = userRepository.save(user); 48 userId = user.getId(); 49 User userForInsert = new User(); 50 userForInsert.setBirthday(new Date()); 51 userForInsert.setFirstName("Khalid"); 52 userForInsert.setLastName("Khalid"); 53 userForInsert.setPhoneNo("+86188888888888"); 54 userRepository.save(userForInsert); 55 } 56 57 @Test 58 public void testInsert() { 59 System.out.println("********测试插入*********"); 60 System.out.println("********插入前*********"); 61 System.out.println(userRepository.findAll()); 62 User userForInsert = new User(); 63 userForInsert.setBirthday(new Date()); 64 userForInsert.setFirstName("Khalid"); 65 userForInsert.setLastName("Khalid"); 66 userForInsert.setPhoneNo("+861888888888899"); 67 userRepository.save(userForInsert); 68 System.out.println("********插入后*********"); 69 System.out.println(userRepository.findAll()); 70 71 } 72 73 @Test 74 public void testUpdate() { 75 System.out.println("********测试更新*********"); 76 System.out.println("*******更新前************"); 77 System.out.println(userRepository.findOne(userId)); 78 // userRepository.updateFirstName("FY", userId); 79 user.setFirstName("Fy"); 80 userRepository.save(user); 81 System.out.println("*******更新后************"); 82 System.out.println(userRepository.findOne(userId)); 83 } 84 85 @Test 86 public void testQuery() { 87 System.out.println("********测试条件查询*********"); 88 User findUser = userRepository.findByPhoneno(user.getPhoneNo()); 89 assertTrue(findUser.getPhoneNo().equals(user.getPhoneNo())); 90 System.out.println("********查询结果:" + findUser); 91 } 92 93 @Test 94 public void testFindAll() { 95 System.out.println("********测试查询所有*********"); 96 System.out.println(userRepository.findAll()); 97 } 98 99 @Test 100 public void testDelById() { 101 System.out.println("********以ID删除*********"); 102 userRepository.delete(userId); 103 System.out.println("********以ID删除*********"); 104 } 105 106 @Test 107 public void testDel() { 108 System.out.println("********以对象删除********"); 109 System.out.println("*******删除前*********"); 110 System.out.println(userRepository.findOne(userId)); 111 userRepository.delete(user); 112 System.out.println("********删除后*********"); 113 assertNull(userRepository.findOne(userId)); 114 } 115 116 @Test 117 public void testDelAll() { 118 System.out.println("********批量删除*********"); 119 System.out.println("********删除前*********"); 120 System.out.println(userRepository.findAll()); 121 userRepository.deleteAllInBatch(); 122 System.out.println("********删除后*********"); 123 assertTrue(userRepository.findAll().isEmpty()); 124 } 125 126 @Test 127 public void testPageQuery() { 128 System.out.println("********测试分页查询********"); 129 Page<User> page = userRepository.findAll(new PageRequest(0, 1)); 130 System.out.println("总页数:" + page.getTotalPages()); 131 for (int i = 0; i < page.getTotalPages(); i++) { 132 System.out.println("第" + (i + 1) + "页"); 133 for (User user : page) { 134 System.out.println(user); 135 } 136 if (page.hasNext()) { 137 page = userRepository.findAll(page.nextPageable()); 138 } else 139 break; 140 } 141 142 } 143 144 @Test 145 public void testCount() { 146 System.out.println("********测试查询条数********"); 147 System.out.println("条数为:" + userRepository.count()); 148 } 149 150 @Test 151 public void testSortQuery(){ 152 System.out.println("********测试排序查询********"); 153 System.out.println(userRepository.findAll(new Sort(Direction.DESC,"firstName"))); 154 155 } 156 157 @Test 158 public void testSQL(){ 159 System.out.println("********使用原生SQL查询********"); 160 System.out.println(userRepository.findByFirstname("Fei")); 161 } 162 163 }
4.1 Spring Data JPA的查询机制
引用前辈的话,写的非常详细清楚~~原文链接
通过解析方法名创建查询
通过前面的例子,读者基本上对解析方法名创建查询的方式有了一个大致的了解,这也是 Spring Data JPA 吸引开发者的一个很重要的因素。该功能其实并非 Spring Data JPA 首创,而是源自一个开源的 JPA 框架 Hades,该框架的作者 Oliver Gierke 本身又是 Spring Data JPA 项目的 Leader,所以把 Hades 的优势引入到 Spring Data JPA 也就是顺理成章的了。
框架在进行方法名解析时,会先把方法名多余的前缀截取掉,比如 find、findBy、read、readBy、get、getBy,然后对剩下部分进行解析。并且如果方法的最后一个参数是 Sort 或者 Pageable 类型,也会提取相关的信息,以便按规则进行排序或者分页查询。
在创建查询时,我们通过在方法名中使用属性名称来表达,比如 findByUserAddressZip ()。框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,详细规则如下(此处假设该方法针对的域对象为 AccountInfo 类型):
- 先判断 userAddressZip (根据 POJO 规范,首字母变为小写,下同)是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
- 从右往左截取第一个大写字母开头的字符串(此处为 Zip),然后检查剩下的字符串是否为 AccountInfo 的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为 AccountInfo 的一个属性;
- 接着处理剩下部分( AddressZip ),先判断 user 所对应的类型是否有 addressZip 属性,如果有,则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。
可能会存在一种特殊情况,比如 AccountInfo 包含一个 user 的属性,也有一个 userAddress 属性,此时会存在混淆。读者可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_AddressZip()" 或者 "findByUserAddress_Zip()"。
在查询时,通常需要同时根据多个属性进行查询,且查询的条件也格式各样(大于某个值、在某个范围等等),Spring Data JPA 为此提供了一些表达条件查询的关键字,大致如下:
- And --- 等价于 SQL 中的 and 关键字,比如 findByUsernameAndPassword(String user, Striang pwd);
- Or --- 等价于 SQL 中的 or 关键字,比如 findByUsernameOrAddress(String user, String addr);
- Between --- 等价于 SQL 中的 between 关键字,比如 findBySalaryBetween(int max, int min);
- LessThan --- 等价于 SQL 中的 "<",比如 findBySalaryLessThan(int max);
- GreaterThan --- 等价于 SQL 中的">",比如 findBySalaryGreaterThan(int min);
- IsNull --- 等价于 SQL 中的 "is null",比如 findByUsernameIsNull();
- IsNotNull --- 等价于 SQL 中的 "is not null",比如 findByUsernameIsNotNull();
- NotNull --- 与 IsNotNull 等价;
- Like --- 等价于 SQL 中的 "like",比如 findByUsernameLike(String user);
- NotLike --- 等价于 SQL 中的 "not like",比如 findByUsernameNotLike(String user);
- OrderBy --- 等价于 SQL 中的 "order by",比如 findByUsernameOrderBySalaryAsc(String user);
- Not --- 等价于 SQL 中的 "! =",比如 findByUsernameNot(String user);
- In --- 等价于 SQL 中的 "in",比如 findByUsernameIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数;
- NotIn --- 等价于 SQL 中的 "not in",比如 findByUsernameNotIn(Collection<String> userList) ,方法的参数可以是 Collection 类型,也可以是数组或者不定长参数
使用 @Query 创建查询
@Query 注解的使用非常简单,只需在声明的方法上面标注该注解,同时提供一个 JP QL 查询语句即可,如下所示:
清单 16. 使用 @Query 提供自定义查询语句示例
public interface UserDao extends Repository<AccountInfo, Long> { @Query("select a from AccountInfo a where a.accountId = ?1") public AccountInfo findByAccountId(Long accountId); @Query("select a from AccountInfo a where a.balance > ?1") public Page<AccountInfo> findByBalanceGreaterThan( Integer balance,Pageable pageable); }很多开发者在创建 JP QL 时喜欢使用命名参数来代替位置编号,@Query 也对此提供了支持。JP QL 语句中通过": 变量"的格式来指定参数,同时在方法的参数前面使用 @Param 将方法参数与 JP QL 中的命名参数对应,示例如下:
清单 17. @Query 支持命名参数示例
public interface UserDao extends Repository<AccountInfo, Long> { public AccountInfo save(AccountInfo accountInfo); @Query("from AccountInfo a where a.accountId = :id") public AccountInfo findByAccountId(@Param("id")Long accountId); @Query("from AccountInfo a where a.balance > :balance") public Page<AccountInfo> findByBalanceGreaterThan( @Param("balance")Integer balance,Pageable pageable); }此外,开发者也可以通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询。如下所示:
清单 18. 使用 @Modifying 将查询标识为修改查询
@Modifying @Query("update AccountInfo a set a.salary = ?1 where a.salary < ?2") public int increaseSalary(int after, int before);通过调用 JPA 命名查询语句创建查询
命名查询是 JPA 提供的一种将查询语句从方法体中独立出来,以供多个方法共用的功能。Spring Data JPA 对命名查询也提供了很好的支持。用户只需要按照 JPA 规范在 orm.xml 文件或者在代码中使用 @NamedQuery(或 @NamedNativeQuery)定义好查询语句,唯一要做的就是为该语句命名时,需要满足”DomainClass.methodName()”的命名规则。假设定义了如下接口:
清单 19. 使用 JPA 命名查询时,声明接口及方法时不需要什么特殊处理
public interface UserDao extends Repository<AccountInfo, Long> { ...... public List<AccountInfo> findTop5(); }如果希望为 findTop5() 创建命名查询,并与之关联,我们只需要在适当的位置定义命名查询语句,并将其命名为 "AccountInfo.findTop5",框架在创建代理类的过程中,解析到该方法时,优先查找名为 "AccountInfo.findTop5" 的命名查询定义,如果没有找到,则尝试解析方法名,根据方法名字创建查询。
创建查询的顺序
Spring Data JPA 在为接口创建代理对象时,如果发现同时存在多种上述情况可用,它该优先采用哪种策略呢?为此,<jpa:repositories> 提供了 query-lookup-strategy 属性,用以指定查找的顺序。它有如下三个取值:
- create --- 通过解析方法名字来创建查询。即使有符合的命名查询,或者方法通过 @Query 指定的查询语句,都将会被忽略。
- create-if-not-found --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则通过解析方法名字来创建查询。这是 query-lookup-strategy 属性的默认值。
- use-declared-query --- 如果方法通过 @Query 指定了查询语句,则使用该语句实现查询;如果没有,则查找是否定义了符合条件的命名查询,如果找到,则使用该命名查询;如果两者都没有找到,则抛出异常。
5.总结
这篇文章结合了网上大多SpringDataJPA的讲解,加上一些自己的理解,希望可以对需要学习的程序员带来帮助。
2016-03-27
Khalid