《Spring实战》-第十一章:利用对象-关系映射持久化数据(Spring整合Spring Data )

慢来比较快,虚心学技术

Ⅰ、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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章