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