学习Spring Data Jpa

  首先,抛开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目录下,十分坑爹!!)

 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>
View Code

  使用Maven搭建一个测试环境,Maven的使用在这里不再赘述,这里显示所依赖的jar包

<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>
View Code

  搭建一个测试项目 如图

  

  Entity 实体类 User.java

 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 }
View Code

  Junit4 测试客户端,这里只简单的操作了保存与创建简单的HQL查询。JPATest.java

 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 }
View Code

   运行客户端就可以啦~~其实并没有什么特别的- -。

       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层繁琐的业务逻辑,你仅仅需要做的只有声明接口。

    不多说直接上代码!!!

    首先依旧是建库与表    

 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;
View Code

   既然是Spring,applicationContext.xml内有关SpringDataJPA的相关配置    

 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>
View Code

  依旧是Maven,这次的依赖库  

  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>
View Code

  搭建环境~

  

  核心DAO层代码 IUserRepository.java  是不是非常简洁,剩下的工作Spring Data JPA框架帮你代理完成   

 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 }
View Code

   Entity实体类 User.java 注意catalog的变化

 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 }
View Code

  为了简单 我通过Spring-test框架以及Junit整合编写了一个测试客户端,没有编写Service层,Spring-test与Junit整合的教程可以点这里,讲的十分详细。使用Spring-test非常大的好处是支持数据库的自动回滚,以至于测试不会破坏数据库的现场,而且可以频繁的测试多组数据。代码如下 TestJPADemoClient.java,基本包括了常用的查询Demo

  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 }
View Code

  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

posted on 2016-03-27 16:57  Khalid  阅读(302)  评论(0编辑  收藏  举报

导航