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类
package com.esom.tech.springjpa.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.hibernate.annotations.DynamicInsert; import org.hibernate.annotations.DynamicUpdate; @Entity @DynamicInsert @DynamicUpdate //生成的SQL中涉及的字段只包含User类中修改的属性所对应的表字段 @Table(name="MA_USER") public class User { @Id @Column(name = "ID") @SequenceGenerator(name="USER_ID_GENERATOR", sequenceName="SEQ_USER_ID",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="USER_ID_GENERATOR") private Long id; /** * 如果不指定表字段,会自动映射为USERNAME * 并且在加载运行时,发现没有该表字段,会自动添加创建(如果表,Sequence没有也会创建)。 * 对应ORACLE,创建的的类型对应关系: * String VARCHAR2(255 CHAR) * Long NUMBER(19) * Integer NUMBER(10) * java.sql.Date DATE * java.sql.Time DATE * java.util.Date TIMESTAMP(6) * java.sql.Timestamp TIMESTAMP(6) */ @Column(name = "USER_NAME", unique = true) private String userName; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "AGE") private Integer age; @Override public String toString() { return String.format("Entity of type %s with id: %s", this.getClass() .getName(), getId()); } @Override public boolean equals(Object obj) { if (null == obj) { return false; } if (this == obj) { return true; } if (!getClass().equals(obj.getClass())) { return false; } return null == this.getId() ? false : this.getId().equals(((User)obj).getId()); } // 忽略所有get、set方法 }
2. Repository接口
package com.esom.tech.springjpa.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import com.esom.tech.springjpa.domain.User; public interface UserRepository extends CrudRepository<User, Long>, JpaSpecificationExecutor<User>{ /** * 根据方法名解析 * @param lastname * @return */ List<User> findByLastName(String ln); /** * 根据@Query和命名参数解析 * 注意这里是HSQL,所以用User(而非ma_user), lastName(而非first_name) * @param name * @return */ @Query(" from User u where u.firstName = :name or u.lastName = :name ") public List<User> findByFirstNameOrLastName(@Param("name") String name); /** * 根据@Query和占位符解析 * @param firstname * @return */ @Query(" from User u where u.firstName = ?1 and lastName = ?2 ") List<User> findByFirstNameAndLastName(String fb, String ln); }
3. 一小陀配置
配置repository和服务bean,demo-repository-context.xml
<?xml version="1.0" encoding="UTF8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <import resource="classpath*:/META-INF/spring/application-context-root.xml" /> <!--配置jpa repository,核心部分--> <jpa:repositories base-package="com.esom.tech.springjpa.repository" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" /> <!--配置服务bean--> <context:component-scan base-package="com.esom.tech.springjpa.service"/> </beans>
配置数据源及EntityManager,application-context-root.xml
<?xml version="1.0" encoding="UTF8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--Data Source--> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@171.22.70.28:1521:gbst" /> <property name="username" value="username" /> <property name="password" value="password" /> <property name="initialSize" value="1"/> <property name="maxActive" value="2"/> </bean> <!-- Parent Entity Manager : Hibernate 实现 --> <bean id="parentEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" abstract="true"> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database" value="ORACLE" /> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> </bean> </property> </bean> <!-- Entity Manager --> <bean id="entityManagerFactory" parent="parentEntityManagerFactory"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan"> <array> <!--entity扫描目录,可多个value节点--> <value>com.esom.tech.springjpa.domain</value> </array> </property> </bean> <!-- Transaction Manager --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <tx:advice id="businessTxAdvise" transaction-manager="transactionManager" /> <aop:config> <aop:pointcut id="businessPointcut" expression="execution(* com.esom.tech..*.*(..))" /> <aop:advisor advice-ref="businessTxAdvise" pointcut-ref="businessPointcut" /> </aop:config> </beans>
根据项目具体情况配置,不一定是Hibernate EntityManager及ORACLE
4. 业务层接口
package com.esom.tech.springjpa.service; import com.esom.tech.springjpa.domain.User; public interface UserService { //保存User public User saveUser(User user); //是否存在客户 boolean hasUser(Long id); }
5. 业务层接口实现类
package com.esom.tech.springjpa.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.esom.tech.springjpa.domain.User; import com.esom.tech.springjpa.repository.UserRepository; import com.esom.tech.springjpa.service.UserService; @Service public class UserServiceImpl implements UserService{ @Autowired private UserRepository userRepository; public boolean hasUser(Long id) { User user = userRepository.findOne(id); return user != null ? true:false; } public User saveUser(User user) { return userRepository.save(user); } }
6. 测试代码
package com.esom.tech.springjpademo; import static org.junit.Assert.*; import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //import org.springframework.test.context.transaction.TransactionConfiguration; //import org.springframework.transaction.annotation.Transactional; import com.esom.tech.springjpa.domain.User; import com.esom.tech.springjpa.repository.UserRepository; import com.esom.tech.springjpa.service.UserService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath*:/META-INF/spring/springjpa/demo-repository-context.xml" }) //@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) //@Transactional public class SpringJpaTest { @Autowired UserRepository repository; @Autowired UserService userService; User user; //每个@Test方法都会先执行init()一遍 @Before public void init(){ user = new User(); user.setAge(28); user.setFirstName("Lios"); user.setLastName("Lin"); user.setUserName("Lios Lin"); } // crud方法测试 @Test public void testCrud(){ //第一次,新增一条记录,新增后user.id会给赋值 repository.save(user); //第二次,不做保存,因为user.id有值,会根据user.id查询数据库是否存在记录,对有记录并且字段值没变动的忽略保存操作 repository.save(user); //第三次,字段值有变动(包括置为null),做更新保存 user.setAge(68); repository.save(user); User user2 = repository.findOne(user.getId()); assertEquals(user.getAge(),user2.getAge()); assertEquals(user,user2); } // method query测试 @Test public void testMethodQuery() throws Exception { repository.save(user); List<User> users = repository.findByLastName("Lin"); assertNotNull(users); assertTrue(users.contains(user)); } // named query测试 @Test public void testNameQuery() throws Exception { repository.save(user); List<User> users = repository.findByFirstNameOrLastName("Lin"); assertTrue(users.contains(user)); } // criteria query测试 @Test public void testCriteriaQuery() throws Exception { repository.save(user); List<User> users = repository.findAll(new Specification<User>() { public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query,CriteriaBuilder cb) { return cb.equal(root.get("lastName"), user.getLastName()); } }); assertTrue(users.contains(user)); } // 其他 query测试 @Test public void testOtherQuery() throws Exception { repository.save(user); //占位符查询 List<User> users = repository.findByFirstNameAndLastName(user.getFirstName(), user.getLastName()); assertTrue(users.contains(user)); } // service测试 @Test public void testService() throws Exception { userService.saveUser(user); assertTrue(userService.hasUser(user.getId())); } }
这个项目结构如下(附件一并带上源码):
跑JUnit测试,绿了,要使生活过的去,哪怕测试有点绿,,,
至于pom.xml为什么是红呢,这是因为maven引用oracle jdbc驱动引起的,下面章节5会说到。
4、Repository核心接口
上面代码可以看到持久层UserRepository继承了CrudRepository和JpaSpecificationExecutor接口(注意这些接口都不需要实现),而CrudRepository又继承了顶级接口Repository。
我们看下接口CrudRepository中的办法:
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> { <S extends T> S save(S entity); <S extends T> Iterable<S> save(Iterable<S> entities); T findOne(ID id); boolean exists(ID id); Iterable<T> findAll(); Iterable<T> findAll(Iterable<ID> ids); long count(); void (ID id); void (T entity); void (Iterable<? extends T> entities); void All(); }
继承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 将查询标识为修改查询
@Modifying @Query("update User a set a.age = ?1 where a.id = ?2") public int updateUserAge(int age, int id);
5、示例构建过程遇到的一些问题
1、使用maven管理项目包的依赖,如果项目没用到maven,根据pom.xml的配置引用对应包到项目
<project> ... <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>3.2.0.RELEASE</spring.version> <slf4j.version>1.6.6</slf4j.version> </properties> <dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency> <!-- J2EE --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.transaction</groupId> <artifactId>jta</artifactId> <version>1.1</version> <scope>provided</scope> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>1.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!--dbcp--> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.4</version> </dependency> <!-- HSQL <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency> --> <!-- hibernate --> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>1.0.0.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.1.6.Final</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.1</version> </dependency> <!--oracle--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3.0</version> </dependency> </dependencies> ... </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声明依赖
<project> ... <repositories> <repository> <id>my-repo</id> <name>my-repo</name> <url>http://localhost:8082/nexus/content/groups/public</url> </repository> </repositories> <dependencies> ... <!--oracle--> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3.0</version> </dependency> ... </dependencies> ... </project>
JDBC驱动就添加到工程中了。
第二种:通过构建自己的私人仓库实现,后续介绍。
相关推荐
SpringData JPA笔记
JPA 的全称是 Java Persistence API, 即 Java 持久化 API,是 SUN 公司推出的一套基于 ORM 的规范,内部是由一系列的接口和抽象类构成。JPA 的查询语言是面向对象而非面向... 本文档包含了JPA的课件笔记与学习标注。
综合多家视频,超全面综合笔记!Spring Data Jpa 学习
Spring Data JPA 是一个强大的框架,它简化了与Java持久层的交互,允许开发者以声明式的方式处理数据库操作。这个框架是Spring生态的一部分,专门用于简化JPA(Java Persistence API)的使用。下面将详细解释Spring ...
SpringDataJPA学习笔记
本文是介绍Spring-data-jpa...SpringData JPA详解 http://shensuqiao.iteye.com/blog/2096114 W3Cschool JPA概述 https://www.w3cschool.cn/java/jpa-field-mapping.html 轻松愉快之玩转SpringData-慕课网 (免费视频) ...
Spring Data JPA与Hibernate是Java领域中用于数据库操作的重要框架,它们在企业级应用开发中扮演着关键角色。本文将深入探讨这两个框架的核心概念、功能以及它们如何协同工作。 Spring Data JPA是Spring Framework...
#### 三、SpringData方法定义规范 Spring Data JPA 通过定义方法名的约定来推断方法的行为。以下是一些常用的方法名前缀和示例: - **查询所有数据**:`findAll()` 或 `findUsers()`(假设实体类名为 User)。 - *...
在"spring-data-JPA学习笔记"中,我们主要关注的是如何使用JPA进行CRUD操作以及一些常用的查询方法。 首先,Spring Data JPA 提供了一种基于方法名的自动查询映射机制。例如,`findByUsernameAndPassword(String ...
1. **JPA模块**:Spring Data JPA(Java Persistence API)是Spring Data的一个重要组成部分,它基于Java EE的JPA规范,允许开发者以声明式的方式进行数据操作。通过Repository接口,你可以轻松实现CRUD(创建、读取...
- eclipselink:展示了如何在Spring Boot和Eclipselink中使用Spring Data JPA的示例项目。 - example:包含了各种示例包,展示了使用Spring Data JPA的不同级别。可以查看simple包以获取最基本的设置。 - ...
**Spring Data JPA精讲** 在Java开发领域,Spring Data JPA是一个不可或缺的工具,它为开发者提供了方便快捷的方式来操作数据库。Spring Data JPA是Spring框架的一个模块,它简化了Java Persistence API(JPA)的...
在本文中,我们将深入探讨Spring Data JPA中的JpaRepository查询功能。Spring Data JPA是Spring框架的一个模块,它简化了与Java Persistence API (JPA)的交互,提供了强大的数据访问抽象,使得数据库操作变得更加...
在本实训项目中,我们将深入探讨“Spring Data JPA”这一强大的Java持久层框架,它为开发者提供了方便快捷的方式来操作数据库。"sxau-山西农大21级 实训"显然是一个面向山西农业大学21级学生的实践教学活动,旨在...
**JPA笔记** Java Persistence API(JPA)是Java平台上的一个标准,用于管理关系数据库中的数据。它为Java开发者提供了一种对象/关系映射(ORM)工具,简化了数据库操作,使得开发者可以使用面向对象的方式来处理...
在本篇《深入理解Spring Cloud与微服务构建》的学习笔记中,我们将重点探讨Spring Boot如何与Java Persistence API (JPA)进行整合,从而实现高效、便捷的数据库操作。Spring Boot以其快速开发特性,大大简化了应用...
### SpringData知识点详解 #### SpringData简介 SpringData是Spring框架下的一个重要子项目,它的主要目标是为开发者提供一种统一的方式来访问不同的数据存储系统。通过SpringData,开发者能够以一致的方式处理不同...
5. **数据访问/集成**:Spring 提供了对 JDBC、JPA、Hibernate 等多种数据访问技术的支持。学习如何使用 Spring JdbcTemplate 或者 MyBatis 进行数据库操作,以及事务管理(@Transactional)是非常重要的。 6. **...
JPA + Hibernate + Spring + Data JPA-快速入门模板 模板/范例 Hibernate独立 Hibernate+ JPA Hibernate + Spring XML Hibernate + JPA + Spring XML Hibernate + JPA + Spring批注 Hibernate + JPA + Spring批注...