Spring Data JPA 笔记

1、前言

最近项目需要用到Spring Data JPA,经过一段时间的学习和整理,做如下备忘笔记,也供读者了解和使用该框架提供指导。

2、Spring Data JPA简介

介绍:针对关系型数据库,KV数据库,Document数据库,Graph数据库,Map-Reduce等主流数据库,采用统一技术进行访问,并且尽可能简化访问手段,让数据的访问变得更加方便。Spring Data由多个子项目组成,支持CouchDB、MongoDB、Neo4J、Hadoop、HBase、Cassandra、JPA等。


学习资料:SpringData主页

SpringData:http://www.springsource.org/spring-data
SpringDataJPA:http://www.springsource.org/spring-data/jpa

SpringDataJPA 指南文档:http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/

3、实践示例

本实践示例代码基于 Hibernate EntityManager 开发,但是读者几乎不用修改任何代码,便可以非常容易地切换到其他JPA 框架,因为代码中使用到的都是JPA 规范提供的接口/ 类。


示例用到数据源为Oracle,包依赖用Maven管理,在前段时间构建的基础Maven项目『Maven笔记(2)』上加以实现:


Spring Data JPA 极大简化了数据库访问层代码,只要3步:

1. 编写Entity类,依照JPA规范,定义实体
2. 编写Repository接口,依靠SpringData规范,定义数据访问接口(只要接口,不需要任何实现)
3. 编写一小陀配置文件(Spring极大地简化了配置方式)


另加3步实现业务层及测试类:

4. 编写业务层接口

5. 编写业务层接口实现类

6. 编写测试代码


示例主要涉及六个文件:业务层包含一个接口和一个实现;持久层包含一个接口、一个实体类;另外加上一个JPA 配置文件和一个测试类。相关类/接口/配置如下:


1. Entity类

Java代码   收藏代码
  1. package com.esom.tech.springjpa.domain;  

  2. import javax.persistence.Column;  

  3. import javax.persistence.Entity;  

  4. import javax.persistence.GeneratedValue;  

  5. import javax.persistence.GenerationType;  

  6. import javax.persistence.Id;  

  7. import javax.persistence.SequenceGenerator;  

  8. import javax.persistence.Table;  

  9. import org.hibernate.annotations.DynamicInsert;  

  10. import org.hibernate.annotations.DynamicUpdate;  

  11. @Entity

  12. @DynamicInsert

  13. @DynamicUpdate//生成的SQL中涉及的字段只包含User类中修改的属性所对应的表字段

  14. @Table(name="MA_USER")  

  15. publicclass User {  

  16. @Id

  17. @Column(name = "ID")  

  18. @SequenceGenerator(name="USER_ID_GENERATOR", sequenceName="SEQ_USER_ID",allocationSize=1)  

  19. @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_ID_GENERATOR")  

  20. private Long id;  

  21. /**

  22.     * 如果不指定表字段,会自动映射为USERNAME

  23.     * 并且在加载运行时,发现没有该表字段,会自动添加创建(如果表,Sequence没有也会创建)。

  24.     * 对应ORACLE,创建的的类型对应关系:

  25.     *      String  VARCHAR2(255 CHAR)

  26.     *      Long    NUMBER(19)

  27.     *      Integer NUMBER(10)

  28.     *      java.sql.Date   DATE

  29.     *      java.sql.Time   DATE

  30.     *      java.util.Date  TIMESTAMP(6)

  31.     *      java.sql.Timestamp  TIMESTAMP(6)    

  32.     */

  33. @Column(name = "USER_NAME", unique = true)  

  34. private String userName;  

  35. @Column(name = "FIRST_NAME")  

  36. private String firstName;  

  37. @Column(name = "LAST_NAME")  

  38. private String lastName;  

  39. @Column(name = "AGE")  

  40. private Integer age;  

  41. @Override

  42. public String toString() {  

  43. return String.format("Entity of type %s with id: %s", this.getClass()  

  44.                .getName(), getId());  

  45.    }  

  46. @Override

  47. publicboolean equals(Object obj) {  

  48. if (null == obj) {  

  49. returnfalse;  

  50.        }  

  51. if (this == obj) {  

  52. returntrue;  

  53.        }  

  54. if (!getClass().equals(obj.getClass())) {  

  55. returnfalse;  

  56.        }  

  57. returnnull == this.getId() ? false : this.getId().equals(((User)obj).getId());  

  58.    }  

  59. // 忽略所有get、set方法

  60. }  


2. Repository接口

Java代码   收藏代码
  1. package com.esom.tech.springjpa.repository;  

  2. import java.util.List;  

  3. import org.springframework.data.jpa.repository.JpaSpecificationExecutor;  

  4. import org.springframework.data.jpa.repository.Query;  

  5. import org.springframework.data.repository.CrudRepository;  

  6. import org.springframework.data.repository.query.Param;  

  7. import com.esom.tech.springjpa.domain.User;  

  8. publicinterface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User>{  

  9. /**

  10.     * 根据方法名解析

  11.     * @param lastname

  12.     * @return

  13.     */

  14.    List<User> findByLastName(String ln);  

  15. /**

  16.     * 根据@Query和命名参数解析

  17.     * 注意这里是HSQL,所以用User(而非ma_user), lastName(而非first_name)

  18.     * @param name

  19.     * @return

  20.     */

  21. @Query(" from User u where u.firstName = :name or u.lastName = :name ")  

  22. public List<User> findByFirstNameOrLastName(@Param("name") String name);  

  23. /**

  24.     * 根据@Query和占位符解析

  25.     * @param firstname

  26.     * @return

  27.     */

  28. @Query(" from User u where u.firstName = ?1 and lastName = ?2 ")  

  29.    List<User> findByFirstNameAndLastName(String fb, String ln);  

  30. }  


3. 一小陀配置


配置repository和服务bean,demo-repository-context.xml

Xml代码   收藏代码
  1. <?xmlversion="1.0"encoding="UTF8"?>

  2. <beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  3. xmlns:context="http://www.springframework.org/schema/context"xmlns:jpa="http://www.springframework.org/schema/data/jpa"

  4. xsi:schemaLocation="  

  5.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  

  6.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd  

  7.        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

  8. <importresource="classpath*:/META-INF/spring/application-context-root.xml"/>

  9. <!--配置jpa repository,核心部分-->

  10. <jpa:repositoriesbase-package="com.esom.tech.springjpa.repository"entity-manager-factory-ref="entityManagerFactory"

  11. transaction-manager-ref="transactionManager"/>

  12. <!--配置服务bean-->

  13. <context:component-scanbase-package="com.esom.tech.springjpa.service"/>

  14. </beans>


配置数据源及EntityManager,application-context-root.xml

Xml代码   收藏代码
  1. <?xmlversion="1.0"encoding="UTF8"?>

  2. <beansxmlns="http://www.springframework.org/schema/beans"

  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jdbc="http://www.springframework.org/schema/jdbc"

  4. xmlns:tx="http://www.springframework.org/schema/tx"xmlns:util="http://www.springframework.org/schema/util"

  5. xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"

  6. xsi:schemaLocation="  

  7.        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd  

  8.        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  

  9.        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd  

  10.        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd  

  11.        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd  

  12.        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  13. <!--Data Source-->

  14. <beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

  15. <propertyname="driverClassName"value="oracle.jdbc.driver.OracleDriver"/>

  16. <propertyname="url"value="jdbc:oracle:thin:@171.22.70.28:1521:gbst"/>

  17. <propertyname="username"value="username"/>

  18. <propertyname="password"value="password"/>

  19. <propertyname="initialSize"value="1"/>

  20. <propertyname="maxActive"value="2"/>

  21. </bean>

  22. <!-- Parent Entity Manager : Hibernate 实现 -->

  23. <beanid="parentEntityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"

  24. abstract="true">

  25. <propertyname="jpaVendorAdapter">

  26. <beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

  27. <propertyname="database"value="ORACLE"/>

  28. <propertyname="showSql"value="true"/>

  29. <propertyname="generateDdl"value="true"/>

  30. </bean>

  31. </property>

  32. </bean>

  33. <!-- Entity Manager -->

  34. <beanid="entityManagerFactory"parent="parentEntityManagerFactory">

  35. <propertyname="dataSource"ref="dataSource"/>

  36. <propertyname="packagesToScan">

  37. <array>

  38. <!--entity扫描目录,可多个value节点-->

  39. <value>com.esom.tech.springjpa.domain</value>

  40. </array>

  41. </property>

  42. </bean>

  43. <!-- Transaction Manager -->

  44. <beanid="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager">

  45. <propertyname="entityManagerFactory"ref="entityManagerFactory"/>

  46. </bean>

  47. <tx:adviceid="businessTxAdvise"transaction-manager="transactionManager"/>

  48. <aop:config>

  49. <aop:pointcutid="businessPointcut"

  50. expression="execution(* com.esom.tech..*.*(..))"/>

  51. <aop:advisoradvice-ref="businessTxAdvise"pointcut-ref="businessPointcut"/>

  52. </aop:config>

  53. </beans>

根据项目具体情况配置,不一定是Hibernate EntityManager及ORACLE


4. 业务层接口

Java代码   收藏代码
  1. package com.esom.tech.springjpa.service;  

  2. import com.esom.tech.springjpa.domain.User;  

  3. publicinterface UserService {  

  4. //保存User

  5. public User saveUser(User user);  

  6. //是否存在客户

  7. boolean hasUser(Long id);  

  8. }  


5. 业务层接口实现类

Java代码   收藏代码
  1. package com.esom.tech.springjpa.service.impl;  

  2. import org.springframework.beans.factory.annotation.Autowired;  

  3. import org.springframework.stereotype.Service;  

  4. import com.esom.tech.springjpa.domain.User;  

  5. import com.esom.tech.springjpa.repository.UserRepository;  

  6. import com.esom.tech.springjpa.service.UserService;  

  7. @Service

  8. publicclass UserServiceImpl implements UserService{  

  9. @Autowired

  10. private UserRepository userRepository;  

  11. publicboolean hasUser(Long id) {  

  12.        User user = userRepository.findOne(id);  

  13. return user != null ? true:false;  

  14.    }  

  15. public User saveUser(User user) {  

  16. return userRepository.save(user);  

  17.    }  

  18. }  


6. 测试代码

Java代码   收藏代码
  1. package com.esom.tech.springjpademo;  

  2. importstatic org.junit.Assert.*;  

  3. import java.util.List;  

  4. import javax.persistence.criteria.CriteriaBuilder;  

  5. import javax.persistence.criteria.CriteriaQuery;  

  6. import javax.persistence.criteria.Predicate;  

  7. import javax.persistence.criteria.Root;  

  8. import org.junit.Before;  

  9. import org.junit.Test;  

  10. import org.junit.runner.RunWith;  

  11. import org.springframework.beans.factory.annotation.Autowired;  

  12. import org.springframework.data.jpa.domain.Specification;  

  13. import org.springframework.test.context.ContextConfiguration;  

  14. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  

  15. //import org.springframework.test.context.transaction.TransactionConfiguration;

  16. //import org.springframework.transaction.annotation.Transactional;

  17. import com.esom.tech.springjpa.domain.User;  

  18. import com.esom.tech.springjpa.repository.UserRepository;  

  19. import com.esom.tech.springjpa.service.UserService;  

  20. @RunWith(SpringJUnit4ClassRunner.class)  

  21. @ContextConfiguration(locations = {"classpath*:/META-INF/spring/springjpa/demo-repository-context.xml" })  

  22. //@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

  23. //@Transactional

  24. publicclass SpringJpaTest {  

  25. @Autowired

  26.    UserRepository repository;  

  27. @Autowired

  28.    UserService userService;  

  29.    User user;  

  30. //每个@Test方法都会先执行init()一遍

  31. @Before

  32. publicvoid init(){  

  33.        user = new User();  

  34.        user.setAge(28);  

  35.        user.setFirstName("Lios");  

  36.        user.setLastName("Lin");  

  37.        user.setUserName("Lios Lin");  

  38.    }  

  39. // crud方法测试

  40. @Test

  41. publicvoid testCrud(){  

  42. //第一次,新增一条记录,新增后user.id会给赋值

  43.        repository.save(user);    

  44. //第二次,不做保存,因为user.id有值,会根据user.id查询数据库是否存在记录,对有记录并且字段值没变动的忽略保存操作

  45.        repository.save(user);  

  46. //第三次,字段值有变动(包括置为null),做更新保存

  47.        user.setAge(68);  

  48.        repository.save(user);  

  49.        User user2 = repository.findOne(user.getId());  

  50.        assertEquals(user.getAge(),user2.getAge());  

  51.        assertEquals(user,user2);  

  52.    }  

  53. // method query测试

  54. @Test

  55. publicvoid testMethodQuery() throws Exception {  

  56.        repository.save(user);  

  57.        List<User> users = repository.findByLastName("Lin");  

  58.        assertNotNull(users);  

  59.        assertTrue(users.contains(user));  

  60.    }  

  61. // named query测试

  62. @Test

  63. publicvoid testNameQuery() throws Exception {  

  64.        repository.save(user);  

  65.        List<User> users = repository.findByFirstNameOrLastName("Lin");  

  66.        assertTrue(users.contains(user));  

  67.    }  

  68. // criteria query测试

  69. @Test

  70. publicvoid testCriteriaQuery() throws Exception {  

  71.        repository.save(user);  

  72.        List<User> users = repository.findAll(new Specification<User>() {  

  73. public Predicate toPredicate(Root<User> root,  

  74.                    CriteriaQuery<?> query,CriteriaBuilder cb) {  

  75. return cb.equal(root.get("lastName"), user.getLastName());  

  76.            }  

  77.        });  

  78.        assertTrue(users.contains(user));  

  79.    }  

  80. // 其他 query测试

  81. @Test

  82. publicvoid testOtherQuery() throws Exception {  

  83.        repository.save(user);  

  84. //占位符查询

  85.        List<User> users = repository.findByFirstNameAndLastName(user.getFirstName(), user.getLastName());  

  86.        assertTrue(users.contains(user));  

  87.    }  

  88. // service测试

  89. @Test

  90. publicvoid testService() throws Exception {  

  91.        userService.saveUser(user);  

  92.        assertTrue(userService.hasUser(user.getId()));  

  93.    }  

  94. }  


这个项目结构如下(附件一并带上源码):



跑JUnit测试,绿了,要使生活过的去,哪怕测试有点绿,,,

至于pom.xml为什么是红呢,这是因为maven引用oracle jdbc驱动引起的,下面章节5会说到。


4、Repository核心接口

上面代码可以看到持久层UserRepository继承了CrudRepository和JpaSpecificationExecutor接口(注意这些接口都不需要实现),而CrudRepository又继承了顶级接口Repository。


我们看下接口CrudRepository中的办法:

Java代码   收藏代码
  1. publicinterface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {  

  2.    <S extends T> S save(S entity);  

  3.    <S extends T> Iterable<S> save(Iterable<S> entities);  

  4.    T findOne(ID id);  

  5. boolean exists(ID id);  

  6.    Iterable<T> findAll();  

  7.    Iterable<T> findAll(Iterable<ID> ids);  

  8. long count();  

  9. void (ID id);  

  10. void (T entity);  

  11. void (Iterable<? extends T> entities);  

  12. void All();  

  13. }  

继承Repository,实现了一组CRUD相关的方法


其他核心接口说明:

PagingAndSortingRepository: 继承CrudRepository,实现了一组分页排序相关的方法

JpaRepository: 继承PagingAndSortingRepository,实现一组JPA规范相关的方法

JpaSpecificationExecutor: 比较特殊,不属于Repository体系,实现一组JPA Criteria查询相关的

这些接口都不需要写任何实现类,Spring Data Jpa框架帮你搞定这一切。


另外说下UserRepository的一些query的其他用法:

1)Method Query: 方法级别的查询,针对 findBy, find, readBy, read, getBy等

前缀的方法,解析方法字符串,生成查询语句,如下图(图来自于互联网):



2)Named Query: 针对一些复杂的SQL,支持原生SQL方式,进行查询,保证性能
3)Criteria Query: 支持JPA标准中的Criteria Query

4)@Modifying 将查询标识为修改查询

Java代码   收藏代码
  1. @Modifying

  2. @Query("update User a set a.age = ?1 where a.id = ?2")  

  3. publicint updateUserAge(int age, int id);  



5、示例构建过程遇到的一些问题

1、使用maven管理项目包的依赖,如果项目没用到maven,根据pom.xml的配置引用对应包到项目

Xml代码   收藏代码
  1. <project>

  2. ...  

  3. <properties>

  4. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  5. <spring.version>3.2.0.RELEASE</spring.version>

  6. <slf4j.version>1.6.6</slf4j.version>

  7. </properties>

  8. <dependencies>

  9. <!-- JUnit -->

  10. <dependency>

  11. <groupId>junit</groupId>

  12. <artifactId>junit</artifactId>

  13. <version>4.7</version>

  14. <scope>test</scope>

  15. </dependency>

  16. <!-- J2EE -->

  17. <dependency>

  18. <groupId>javax.servlet</groupId>

  19. <artifactId>servlet-api</artifactId>

  20. <version>2.5</version>

  21. <scope>provided</scope>

  22. </dependency>

  23. <dependency>

  24. <groupId>javax.transaction</groupId>

  25. <artifactId>jta</artifactId>

  26. <version>1.1</version>

  27. <scope>provided</scope>

  28. </dependency>

  29. <!-- spring -->

  30. <dependency>

  31. <groupId>org.springframework</groupId>

  32. <artifactId>spring-orm</artifactId>

  33. <version>${spring.version}</version>

  34. </dependency>

  35. <dependency>

  36. <groupId>org.springframework</groupId>

  37. <artifactId>spring-tx</artifactId>

  38. <version>${spring.version}</version>

  39. </dependency>

  40. <dependency>

  41. <groupId>org.springframework</groupId>

  42. <artifactId>spring-context</artifactId>

  43. <version>${spring.version}</version>

  44. </dependency>

  45. <dependency>

  46. <groupId>org.springframework</groupId>

  47. <artifactId>spring-core</artifactId>

  48. <version>${spring.version}</version>

  49. </dependency>

  50. <dependency>

  51. <groupId>org.springframework.data</groupId>

  52. <artifactId>spring-data-jpa</artifactId>

  53. <version>1.2.0.RELEASE</version>

  54. </dependency>

  55. <dependency>

  56. <groupId>org.springframework</groupId>

  57. <artifactId>spring-test</artifactId>

  58. <version>${spring.version}</version>

  59. <scope>test</scope>

  60. </dependency>

  61. <dependency>

  62. <groupId>org.springframework</groupId>

  63. <artifactId>spring-aspects</artifactId>

  64. <version>${spring.version}</version>

  65. </dependency>

  66. <!--dbcp-->

  67. <dependency>

  68. <groupId>commons-dbcp</groupId>

  69. <artifactId>commons-dbcp</artifactId>

  70. <version>1.4</version>

  71. </dependency>

  72.    <!-- HSQL  

  73. <dependency>

  74. <groupId>org.hsqldb</groupId>

  75. <artifactId>hsqldb</artifactId>

  76. <version>2.0.0</version>

  77. <scope>test</scope>

  78. </dependency>

  79.     -->

  80. <!-- hibernate -->

  81. <dependency>

  82. <groupId>org.hibernate.javax.persistence</groupId>

  83. <artifactId>hibernate-jpa-2.0-api</artifactId>

  84. <version>1.0.0.Final</version>

  85. </dependency>

  86. <dependency>

  87. <groupId>org.hibernate</groupId>

  88. <artifactId>hibernate-entitymanager</artifactId>

  89. <version>4.1.6.Final</version>

  90. </dependency>

  91. <dependency>

  92. <groupId>org.slf4j</groupId>

  93. <artifactId>slf4j-log4j12</artifactId>

  94. <version>${slf4j.version}</version>

  95. <scope>runtime</scope>

  96. </dependency>

  97. <dependency>

  98. <groupId>org.aspectj</groupId>

  99. <artifactId>aspectjweaver</artifactId>

  100. <version>1.7.1</version>

  101. </dependency>

  102. <!--oracle-->

  103. <dependency>

  104. <groupId>com.oracle</groupId>

  105. <artifactId>ojdbc6</artifactId>

  106. <version>11.2.0.3.0</version>

  107. </dependency>

  108. </dependencies>

  109. ...  

  110. </project>



2、用Maven管理Oracle  JDBC驱动包有点特殊。直接声明依赖,pom.xml文件会提示找不到依赖,这是因为Oracle JDBC驱动包是需要Oracle官方授权才能从Maven中央库下载,我们可以通过2种方法解决:


第一种:首先,下载Oracle的jdbc驱动包ojdbc6.jar,

下载地址:http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html,

这里下载的版本是11.2.0.1.0。

然后,通过命令行执行命令将包安装本地库中去:mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.1.0 -Dpackaging=jar -Dfile=D:\ojdbc6.jar。

最后,在pom.xml声明依赖

Xml代码   收藏代码
  1. <project>

  2. ...  

  3. <repositories>

  4. <repository>

  5. <id>my-repo</id>

  6. <name>my-repo</name>

  7. <url>http://localhost:8082/nexus/content/groups/public</url>

  8. </repository>

  9. </repositories>

  10. <dependencies>

  11.    ...  

  12. <!--oracle-->

  13. <dependency>

  14. <groupId>com.oracle</groupId>

  15. <artifactId>ojdbc6</artifactId>

  16. <version>11.2.0.3.0</version>

  17. </dependency>

  18.    ...  

  19. </dependencies>

  20. ...  

  21. </project>


JDBC驱动就添加到工程中了。


第二种:通过构建自己的私人仓库实现,后续介绍。


你可能感兴趣的:(数据库,技术,项目,资料,读者)