《Spring实战》-第十一章:利用对象-关系映射持久化数据(S
慢来比较快,虚心学技术
Ⅰ、Spring Data JPA简介
Spring-Data:Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷。
Spring-data系列产品:
Spring Data Common:提供共享的基础框架,适合各个子项目使用,支持跨数据库持久化
Spring Data JDBC:提供对关系数据库的访问,而无需处理JPA的复杂性
Spring Data JDBC Extensions:支持 Oracle RAD、高级队列和高级数据类型
Spring Data JPA:简化创建 JPA 数据访问层和跨存储的持久层功能
Spring Data Mongodb:提供对文档数据库的支持
Spring Data Redis:提供对键值对数据库的支持
其中,Spring-data-jpa是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套 JPA 应用框架,底层使用了 Hibernate 的 JPA 技术实现,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展。
简单来说,其目的在于简化Spring未简化的持久层业务工作,开发者仅需要完成的是声明持久层接口,其余则由Spring Data JPA来完成。
Ⅱ、Spring Data JPA架构体系分析
如上所述,我们知道Spring Data JPA依赖于接口即可实现持久层操作,那么了解它提供的核心接口类,即可开始我们的使用:
Repository:最顶层的接口,是一个空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别
Specification:Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件
CrudRepository接口:提供了CRUD的基本功能
PagingAndSortingRepository分页排序接口:封装了查询分页和排序的功能
JpaRepository接口:PagingAndSortingRepository和QueryByExampleExecutor的子接口,除了提供CRUD的功能之外,还额外提供了分页和排序、过滤等功能
** JpaSpecificationExecutor接口**:提供了对JPA Criteria查询(动态查询)的支持
以下分析部分源码:
①Repository
T:要操作的实体类类型
ID:实体类的主键类型class
@Indexed
public interface Repository<T, ID> {
}
②CrudRepository
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
//保存一个实体记录,返回保存后的实体
<S extends T> S save(S var1);
//保存传入的实体列表,返回保存后的实体列表
<S extends T> Iterable<S> saveAll(Iterable<S> var1);
//根据主键查询实体,返回封装实体信息的对象
Optional<T> findById(ID var1);
//根据主键查询数据库是否已存在该记录,返回boolean值
boolean existsById(ID var1);
//获取数据表所有记录,返回实体列表
Iterable<T> findAll();
//根据主键列表获取对应实体记录,返回实体列表
Iterable<T> findAllById(Iterable<ID> var1);
//统计数据表记录总数
long count();
//根据主键删除记录
void deleteById(ID var1);
//根据对象删除记录
void delete(T var1);
//根据传入的对象列表删除记录
void deleteAll(Iterable<? extends T> var1);
//清空数据表
void deleteAll();
}
③PagingAndSortingRepository
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
//获取数据表所有记录并根据传入的Sort对象进行排序,返回实体列表
Iterable<T> findAll(Sort var1);
//获取数据表所有记录并根据传入的分页对象进行分页排序,返回实体列表
Page<T> findAll(Pageable var1);
}
④JpaSpecificationExecutor
public interface JpaSpecificationExecutor<T> {
//根据传入的条件对象查询某个实体,返回实体
Optional<T> findOne(@Nullable Specification<T> var1);
//根据传入的条件对象查询符合条件的实体,返回实体列表
List<T> findAll(@Nullable Specification<T> var1);
//根据传入的条件对象和分页对象查询符合条件的实体,返回实体分页列表
Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
//根据传入的条件对象查询实体并根据Sort对象排序,返回实体列表
List<T> findAll(@Nullable Specification<T> var1, Sort var2);
//根据传入的条件对象查询符合条件的实体个数,返回个数
long count(@Nullable Specification<T> var1);
}
⑤JpaRepository
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
//获取数据表所有记录,返回实体列表
List<T> findAll();
//获取数据表所有记录并根据Sort对象进行排序,返回实体列表
List<T> findAll(Sort var1);
//根据主键列表获取对应实体记录,返回实体列表
List<T> findAllById(Iterable<ID> var1);
//保存传入的实体列表,返回保存后的实体列表
<S extends T> List<S> saveAll(Iterable<S> var1);
//清除session缓存
void flush();
//保存并清除session缓存
<S extends T> S saveAndFlush(S var1);
//根据传入的实体对象批量删除数据表记录
void deleteInBatch(Iterable<T> var1);
//清空数据库表
void deleteAllInBatch();
//根据主键获取实体,返回实体
T getOne(ID var1);
//根据传入的Example对象获取符合条件的实体,返回实体列表
<S extends T> List<S> findAll(Example<S> var1);
//根据传入的Example对象获取符合条件的实体并根据Sort对象进行排序,返回实体列表
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
Ⅲ、Spring整合使用Sring Data JPA
① 引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--logback日志实现引入-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<!--slf4j日志门面引入-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!--引入alibaba的数据库连接池-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!--引入Spring支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入Spring事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入Spring对ORM框架的支持依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!--引入jpa支持-->
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<!--引入Hibernate对象管理器-->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-entitymanager -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!--引入数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
②配置jpa
<?xml version="1.0" encoding="UTF-8"?>
<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:aop="http://www.springframework.org/schema/aop" 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/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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<!--开启注解-->
<context:annotation-config/>
<!--配置组件扫描范围-->
<context:component-scan base-package="com.my.spring"></context:component-scan>
<!-- 导入资源文件 -->
<context:property-placeholder location="classpath:datasource.properties"/>
<!--配置数据库连接池-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="maxActive" value="${druid.maxActive}"></property>
<property name="maxWait" value="${druid.maxWait}"></property>
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}"></property>
</bean>
<!--配置JPA的持久化实现厂商类-->
<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- 自动检查注解的实体和数据表,如果数据库不存在的标,会根据实体自动生成 -->
<property name="generateDdl" value="true" />
<property name="database" value="HSQL" />
</bean>
<!--配置Jpa Entity Manager-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="false">
<!--注入数据库-->
<property name="dataSource" ref="dataSource" />
<!-- 指定Jpa持久化实现厂商类,这里以Hibernate为例 -->
<property name="jpaVendorAdapter" ref="jpaVendorAdapter"></property>
<!-- 指定Entity实体类包路径,使用注解方式进行映射 -->
<property name="packagesToScan" value="com.my.spring.bean" />
<!-- 指定JPA属性;如Hibernate中指定是否显示SQL的是否显示、方言等 -->
<property name="jpaProperties">
<props>
<!-- 命名规则 My_NAME->MyName -->
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.DefaultComponentSafeNamingStrategy</prop>
<!-- 打印sql语句 -->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
<!--配置jpa的方言对象-->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<!-- 配置Spring声明式事务 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"></property>
<property name="entityManagerFactory" ref="entityManagerFactory"></property>
</bean>
<!--开启注解事务-->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 重要配置:启用扫描并自动创建代理的功能 -->
<jpa:repositories base-package="com.my.spring" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory" />
</beans>
③编写实体类
@Entity//指定当前实体类需要被扫描
@Table(name = "basebean")//指定当前实体类映射到数据库表名
public class BaseBean {
@Id//当前字段为主键字段
@GeneratedValue(strategy = GenerationType.IDENTITY)//指定id自增
private Integer id;
private String name;
private Integer age;
}
④编写Repository接口,继承CrudRepository接口,获得默认接口
public interface BaseRepository extends CrudRepository<BaseBean,Integer>{
}
⑤编写Service和Service实现类
public interface BaseService {
/**
* 保存记录
* @param baseBean
* @return
*/
void save(BaseBean baseBean);
/**
* 获取所有记录
* @return
*/
List<BaseBean> findAll();
/**
* 获取指定id记录
* @param id
* @return
*/
BaseBean findOne(Integer id);
}
@Component
public class BaseServiceImpl implements BaseService {
@Autowired
private BaseRepository baseRepository;
@Override
@Transactional(rollbackFor = RuntimeException.class)
public void save(BaseBean baseBean) {
this.baseRepository.save(baseBean);
}
@Override
public List<BaseBean> findAll() {
Iterable<BaseBean> iterable = this.baseRepository.findAll();
if(null!=iterable){
return (List<BaseBean>) iterable;
}
return null;
}
@Override
public BaseBean findOne(Integer id) {
Optional<BaseBean> optional = this.baseRepository.findById(id);
if(null!=optional){
return optional.get();
}
return null;
}
}
⑥编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:application.xml"})//以application.xml为配置环境文件
public class JPATest {
@Autowired
private BaseService baseService;
@Autowired
private ApplicationContext applicationContext;
@Test
public void testJPA() {
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames){
System.out.println(beanDefinitionName);
}
}
@Test
public void testSave(){
BaseBean baseBean = new BaseBean();
baseBean.setName("jpa bean");
baseBean.setAge(50);
this.baseService.save(baseBean);
}
@Test
public void testFindAll(){
List<BaseBean> list = this.baseService.findAll();
if(null==list||list.isEmpty()){
System.err.println("空表");
}else{
for (BaseBean baseBean : list){
System.err.println(baseBean.toString());
}
}
}
}
运行测试,测试结果:
testJPA()
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalPersistenceAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
baseServiceImpl
org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0
dataSource
jpaVendorAdapter
entityManagerFactory
jpaDialect
transactionManager
org.springframework.transaction.config.internalTransactionalEventListenerFactory
org.springframework.aop.config.internalAutoProxyCreator
org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0
org.springframework.transaction.interceptor.TransactionInterceptor#0
org.springframework.transaction.config.internalTransactionAdvisor
org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension#0
emBeanDefinitionRegistrarPostProcessor
jpaMappingContext
jpaContext
org.springframework.data.jpa.util.JpaMetamodelCacheCleanup
baseRepository
testSave()
insert into basebean (age, name) values (?, ?)
testFindAll()
BaseBean(id=1, name=jpa bean, age=50)