zoukankan      html  css  js  c++  java
  • 学习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

  • 相关阅读:
    8月3日云栖精选夜读:LSF-SCNN:一种基于CNN的短文本表达模型及相似度计算的全新优化模型
    linux(centos)下安装PHP的PDO扩展
    Linux中find常见用法示例
    01 编译原理概述
    20145221 《信息安全系统设计基础》第5周学习总结
    20145221 《信息安全系统设计基础》第4周学习总结
    爱春秋之戏说春秋 Writeup
    20145221 《信息安全系统设计基础》第3周学习总结
    20145221 《信息安全系统设计基础》第2周学习总结
    20145221 《信息安全系统设计基础》第1周学习总结
  • 原文地址:https://www.cnblogs.com/KhalidBlog/p/5325468.html
Copyright © 2011-2022 走看看