1.Spring Data JPA简介
1.1.Spring Data JPA概述
Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!
Spring Data JPA 让我们解脱了DAO层的操作,基本上所有CRUD都可以依赖于它来实现,在实际的工作工程中,推荐使用Spring Data JPA + ORM(如:hibernate)完成操作,这样在切换不同的ORM框架时提供了极大的方便,同时也使数据库层操作更加简单,方便解耦。
1.2.Spring Data JPA的特性
SpringData Jpa 极大简化了数据库访问层代码。 如何简化的呢? 使用了SpringDataJpa,我们的dao层中只需要写接口,就自动具有了增删改查、分页查询等方法。
1.3.Spring Data JPA 与 JPA和hibernate之间的关系
JPA是一套规范,内部是有接口和抽象类组成的。hibernate是一套成熟的ORM框架,而且Hibernate实现了JPA规范,所以也可以称hibernate为JPA的一种实现方式,我们使用JPA的API编程,意味着站在更高的角度上看待问题(面向接口编程)
Spring Data JPA是Spring提供的一套对JPA操作更加高级的封装,是在JPA规范下的专门用来进行数据持久化的解决方案。
2.Spring Data JPA的快速入门
2.1.需求说明
Spring Data JPA完成客户的基本CRUD操作
2.2.搭建Spring Data JPA的开发环境
2.2.1.引入Spring Data JPA的坐标
使用Spring Data JPA,需要整合Spring与Spring Data JPA,并且需要提供JPA的服务提供者hibernate,所以需要导入spring相关坐标,hibernate坐标,数据库驱动坐标等.
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<!-- spring beg -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</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-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring end -->
<!-- hibernate beg -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!-- hibernate end -->
<!-- c3p0 beg -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!-- c3p0 end -->
<!-- log end -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- el beg 使用spring data jpa 必须引入 -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
<!-- el end -->
</dependencies>
2.2.2.db.properties
首先添加一个和数据库相关的properties文件;新建db.properties文件,内容如下
jdbcUrl=jdbc:mysql://localhost:3306/jpadb?useUnicode=true&characterEncoding=utf8
driverClass=com.mysql.jdbc.Driver
user=root
password=123
initialPoolSize=10
maxPoolSize=30
2.2.3.整合Spring Data JPA与Spring
需要新建一个Spring的配置文件,因此新建一个applicationContext.xml文件。里面大致配置的东西有:
- 开启包扫描,扫描service层,让service层的对象交给Spring容器管理
- 读取properties文件
- 配置数据源dataSource
- 配置JPA的EntityManagerFactory, 这里面有个包扫描,是扫描实体类那一层的
- 配置事务管理器transactionManager
- 配置支持注解的事务
- 配置SpringData这里包扫描是扫描dao层,扫描那些定义的接口
<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:tx="http://www.springframework.org/schema/tx" 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-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 配置自动扫描的包,扫描service层,service上面有注解就直接被spring容器实例化 -->
<context:component-scan base-package="com.bruceliu.service" />
<!-- 1. 配置数据源 -->
<context:property-placeholder location="classpath:db.properties" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbcUrl}" />
<property name="driverClass" value="${driverClass}" />
<property name="user" value="${user}" />
<property name="password" value="${password}" />
<property name="initialPoolSize" value="${initialPoolSize}" />
<property name="maxPoolSize" value="${maxPoolSize}" />
</bean>
<!-- 2. 配置 JPA 的 EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
</property>
<!-- 配置包扫描,扫描实体 -->
<property name="packagesToScan" value="com.bruceliu.entity" />
<property name="jpaProperties">
<props>
<!-- 生成的数据表的列的映射策略 -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<!-- hibernate 基本属性 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- 3. 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 4. 配置支持注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 5. 配置 SpringData,需要加入 jpa 的命名空间 -->
<!-- base-package: 扫描 Repository Bean 所在的 package -->
<jpa:repositories base-package="com.bruceliu.dao"
entity-manager-factory-ref="entityManagerFactory">
</jpa:repositories>
</beans>
2.2.4.测试整合
测试Spring容器是否整合成功
我们在com.bruceliu.test包中新建一个TestConfig的类,在类里面我们写单元测试的代码。主要内容有:
通过静态代码块创建 ClassPathXmlApplicationContext对象,让它读取applicationContext.xml,这样就启动了Spring容器,通过Spring容器获取dataSource对象,如果成功获取,说明整合成功了。
/**
* @author bruceliu
* @create 2019-07-06 17:49
* @description
*/
(SpringJUnit4ClassRunner.class)
(locations="classpath:applicationContext.xml")
public class TestDs {
private DataSource ds;
public void testDS(){
System.out.println(ds);
}
}
成功后控制台输出结果如下:
com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1hge22pa31j4oyjndv1lmv|378542de, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1hge22pa31j4oyjndv1lmv|378542de, idleConnectionTestPeriod -> 0, initialPoolSize -> 10, jdbcUrl -> jdbc:mysql://localhost:3306/jpadb?useUnicode=true&characterEncoding=utf8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 30, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
2.2.5.测试JPA是否整合成功
JPA是否整合成功主要是看entityManagerFactory这个对象是否起作用,这个对象起作用了就会去扫描com.jpa.bean下面的实体类。测试方法如下:
有一个前提,数据库必须先创建。这里springdata这个数据库创建了
给实体类加上注解,然后开启Hibernate自动建表功能,启动Spring容器;如果数据库自动建表成功,那就整合成功。
实体类的代码如下:
/**
* * 所有的注解都是使用JPA的规范提供的注解,
* * 所以在导入注解包的时候,一定要导入javax.persistence下的
*/
//声明实体类
(name="cst_customer") //建立实体类和表的映射关系
public class Customer {
//声明当前私有属性为主键
(strategy= GenerationType.IDENTITY) //配置主键的生成策略
(name="cust_id") //指定和表中cust_id字段的映射关系
private Long custId;
(name="cust_name") //指定和表中cust_name字段的映射关系
private String custName;
(name="cust_source")//指定和表中cust_source字段的映射关系
private String custSource;
(name="cust_industry")//指定和表中cust_industry字段的映射关系
private String custIndustry;
(name="cust_level")//指定和表中cust_level字段的映射关系
private String custLevel;
(name="cust_address")//指定和表中cust_address字段的映射关系
private String custAddress;
(name="cust_phone")//指定和表中cust_phone字段的映射关系
private String custPhone;
public Long getCustId() {
return custId;
}
public void setCustId(Long custId) {
this.custId = custId;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustSource() {
return custSource;
}
public void setCustSource(String custSource) {
this.custSource = custSource;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}
public String getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String getCustPhone() {
return custPhone;
}
public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
}
2.2.6.在dao层声明接口
在框架整合完成后,就可以开始使用SpringData了,我们新建了一个Customer实体类,利用这个Person类来展开讲解。
Spring Data JPA是spring提供的一款对于数据访问层(Dao层)的框架,使用Spring Data JPA,只需要按照框架的规范提供dao接口,不需要实现类就可以完成数据库的增删改查、分页查询等方法的定义,极大的简化了我们的开发过程。
在Spring Data JPA中,对于定义符合规范的Dao层接口,我们只需要遵循以下几点就可以了:
1.创建一个Dao层接口,并实现JpaRepository和JpaSpecificationExecutor
2.提供相应的泛型
/**
* JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作
* JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作)
*/
public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> {
}
这样我们就定义好了一个符合Spring Data JPA规范的Dao层接口
2.2.7.完成基本CRUD操作
完成了Spring Data JPA的环境搭建,并且编写了符合Spring Data JPA 规范的Dao层接口之后,就可以使用定义好的Dao层接口进行客户的基本CRUD操作
/**
* @author bruceliu
* @create 2019-07-06 17:29
* @description
*/
(SpringJUnit4ClassRunner.class)
(locations="classpath:applicationContext.xml")
public class CustomerDaoTest {
private CustomerDao customerDao;
/**
* 保存客户:调用save(obj)方法
*/
public void testSave() {
Customer c = new Customer();
c.setCustName("Java测试0706");
customerDao.save(c);
}
/**
* 修改客户:调用save(obj)方法
* 对于save方法的解释:如果执行此方法是对象中存在id属性,即为更新操作会先根据id查询,再更新
* 如果执行此方法中对象中不存在id属性,即为保存操作
*/
public void testUpdate() {
//根据id查询id为1的客户
Customer customer = customerDao.findOne(1l);
//修改客户名称
customer.setCustName("测试CRUD");
//更新
customerDao.save(customer);
}
/**
* 根据id删除:调用delete(id)方法
*/
public void testDelete() {
customerDao.delete(1l);
}
/**
* 根据id查询:调用findOne(id)方法
*/
public void testFindById() {
Customer customer = customerDao.findOne(2l);
System.out.println(customer);
}
}