看Spring Data如何簡化數據操做

Spring Data 概述

Spring Data

用於簡化數據庫訪問,支持NoSQL 和 關係數據存儲,其主要目標是使數據庫的訪問變得方便快捷。
SpringData 項目所支持 NoSQL 存儲:
MongoDB (文檔數據庫)
Neo4j(圖形數據庫)
Redis(鍵/值存儲)
Hbase(列族數據庫)
SpringData 項目所支持的關係數據存儲技術:
JDBC
JPAjava

Spring Data JPA

JPA Spring Data : 致力於減小數據訪問層 (DAO) 的開發量. 開發者惟一要作的,就只是聲明持久層的接口,其餘都交給 Spring Data JPA 來幫你完成!mysql

開發步驟

配置 Spring 整合 JPAweb

在 Spring 配置文件中配置 Spring Data,讓 Spring 爲聲明的接口建立代理對象,配置了 \<jpa:repositories> 後,Spring 初始化容器時將會掃描 base-package 指定的包目錄及其子目錄,爲繼承 Repository 或其子接口的接口建立代理對象,並將代理對象註冊爲 Spring Bean,業務層即可以經過 Spring 自動封裝的特性來直接使用該對象。spring

聲明持久層的接口,該接口繼承 Repository,Repository 是一個標記型接口,它不包含任何方法,如必要,Spring Data 可實現 Repository 其餘子接口,其中定義了一些經常使用的增刪改查,以及分頁相關的方法。sql

在接口中聲明須要的方法,Spring Data 將根據給定的策略來爲其生成實現代碼。數據庫

搭建環境

導包:express

antlr-2.7.7.jar
c3p0-0.9.2.1.jar
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.3.jar
dom4j-1.6.1.jar
hamcrest-core-1.3.jar
hibernate-c3p0-4.2.4.Final.jar
hibernate-commons-annotations-4.0.2.Final.jar
hibernate-core-4.2.4.Final.jar
hibernate-entitymanager-4.2.4.Final.jar
hibernate-jpa-2.0-api-1.0.1.Final.jar
javassist-3.15.0-GA.jar
jboss-logging-3.1.0.GA.jar
jboss-transaction-api_1.1_spec-1.0.1.Final.jar
junit-4.12.jar
mchange-commons-java-0.2.3.4.jar
mysql-connector-java-5.1.7-bin.jar
slf4j-api-1.6.1.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-data-commons-1.6.2.RELEASE.jar
spring-data-jpa-1.4.2.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jarapi

applicationContext.xml緩存

<?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: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/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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    <context:component-scan base-package="com.kernel.spring.data"/>
    <!--配置數據源-->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置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"/>
        </property>
        <property name="packagesToScan" value="com.kernel.spring.data"/>
        <property name="jpaProperties">
            <props>
                <!--生成的數據表的映射策略-->
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <!--生成的數據表的列的映射策略-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</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>
    <!--配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <!--配置支持註解的事務-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!--配置Spring Data-->
    <jpa:repositories base-package="com.kernel.spring.data" entity-manager-factory-ref="entityManagerFactory"/>
</beans>

Repository 接口

Repository 接口是 Spring Data 的一個核心接口,不提供任何方法,只要遵循接口方法定義規範,就無需寫實現類,與繼承 Repository 接口等價的一種方式,就是在持久層接口上使用 @RepositoryDefinition 註解,併爲其指定domainClass 和 idClass 屬性。架構

Repository 的子接口

Repository: 僅僅是一個標識,代表任何繼承它的均爲倉庫接口類。

CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法 。

PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法 。

JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規範相關的方法 。

自定義的 XxxxRepository 須要繼承 JpaRepository,這樣的 XxxxRepository 接口就具有了通用的數據訪問控制層的能力。

JpaSpecificationExecutor: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法 。

Spring Data 方法定義規範

簡單條件查詢,查詢某一個實體或者集合,查詢方法以 get、find、read 開頭,涉及條件查詢時,條件的屬性用關鍵字鏈接,條件屬性以首字母大寫。

例如

public interface PersonRepository extends Repository<Person, Integer> {
    Person getByLastName(String lastName);

    List<Person> getByLastNameStartingWithAndIdLessThan(String lastName, Integer id);

    List<Person> getByLastNameEndingWithAndIdLessThan(String lastName, Integer id);

    List<Person> getByEmailInAndAgeLessThan(List list, Integer id);

    List<Person> getByAddress_IdGreaterThan(Integer id);
}

Spring Data 支持的關鍵字

Keyword Sample JPQL snippet
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = 1?
Betwee findByStartDateBetween … where x.startDate between 1? and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1(parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1(parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> age) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

@Query 註解

這種查詢能夠聲明在 Repository 方法中,擺脫像命名查詢那樣的約束,將查詢直接在相應的接口方法中聲明,結構更爲清晰,這是 Spring Data 的特有實現。

索引參數:

索引值從1開始,查詢中 ?x 的個數須要與方法形參個數相等,而且順序也要一致。

@Query("SELECT p FROM Person p WHERE p.lastName = ?1 and p.age = ?2")
Person getByLastNameAndAge(String lastName, int age);

命名參數:

能夠定義好參數名,賦值值採用 @Param(參數名),而不用管順序。

@Query("SELECT p FROM Person p WHERE p.lastName = :lastName and p.id = :id")
Person getByLastNameAndId(@Param("id")int id, @Param("lastName")String lastName);

本地查詢:

可使用 @Query 執行本地查詢,只要設置 nativeQuery 爲 true。

@Query(value = "SELECT COUNT (id) FROM jpa_persons", nativeQuery = true)
long getTotalCount();

@Modifying 註解和事務

@Query 與 @Modifying 這兩個註解一塊兒使用,能夠執行 UPDATE、DELETE 操做。

注意,UPDATE、DELETE 須要使用事務,所以須要定義在 Service 層。

事務:

Spring Data 提供了默認的事務處理方式,即全部的查詢均爲只讀事務。

對於自定義的方法,如需改變 Spring Data 提供的事務方式,能夠在方法上添加 @Transactional 註解。

進行多個 Repository 操做時,也應該使它們在同一個事務中處理,按照分層架構的思想,這部分屬於業務邏輯層,所以,須要在 Service 層實現對多個 Repository 的調用,並在相應的方法上聲明事務。

CrudRepository 接口

CrudRepository 接口提供了最基本的對實體類的添刪改查操做
T save(T entity) 保存單個實體
Iterable<T> save(Iterable<? extends T> entities) 保存集合
T findOne(ID id) 根據id查找實體
boolean exists(ID id) 根據id判斷實體是否存在
Iterable<T> findAll() 查詢全部實體,不用或慎用
long count() 查詢實體數量
void delete(ID id) 根據Id刪除實體
void delete(T entity) 刪除一個實體
void delete(Iterable<? extends T> entities) 刪除一個實體的集合
void deleteAll() 刪除全部實體,不用或慎用

PagingAndSortingRepository 接口

該接口提供了分頁與排序功能

Iterable<T> findAll(Sort sort) 排序

Page<T> findAll(Pageable pageable) 分頁查詢(含排序功能)

@Test
public void testPagingAndSortingRespository(){
    int pageNo = 3;
    int pageSize = 5;
    Sort.Order order = new Sort.Order(Sort.Direction.DESC, "id");
    Sort.Order order1 = new Sort.Order(Sort.Direction.ASC, "email");
    Sort sort = new Sort(order,order1);
    PageRequest pageable = new PageRequest(pageNo, pageSize, sort);
    Page<Person> page = personRepository.findAll(pageable);
    System.out.println(page.getNumberOfElements());
    System.out.println(page.getContent());
}

JpaRepository 接口

該接口提供了JPA的相關功能

List<T> findAll() 查找全部實體

List<T> findAll(Sort sort) 排序、查找全部實體

List<T> save(Iterable<? extends T> entities) 保存集合

void flush() 執行緩存與數據庫同步

T saveAndFlush(T entity) 強制執行持久化

void deleteInBatch(Iterable<T> entities) 刪除一個實體集合

JpaSpecificationExecutor 接口

不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法

Specification:封裝 JPA Criteria 查詢條件。一般使用匿名內部類的方式來建立該接口的對象

@Test
public void testJpaSpecificationExecutor(){
    int pageNo = 3;
    int pageSize = 5;
    PageRequest pageable = new PageRequest(pageNo, pageSize);
    Specification<Person> specification = new Specification<Person>() {
        /**
             * @param root 表明查詢的實體類
             * @param criteriaQuery 能夠從中獲得root對象,即告知 JPA Criteria 查詢哪一個實體類,
             *                      還能夠添加查詢條件,還能夠結合EntityManager對象獲得最終查詢的                                    TypedQuery
             * @param criteriaBuilder 用於建立criteria工廠
             * @return Predicate類型,表明一個查詢條件
             */
        @Override
        public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            Predicate predicate = criteriaBuilder.ge(root.get("id"),5);
            return predicate;
        }
    };
    Page<Person> page = personRepository.findAll(specification, pageable);
    System.out.println(page.getNumberOfElements());
    System.out.println(page.getContent());
}

自定義 Repository 方法

爲某一個 Repository 上添加自定義方法

建立一個 XXXDAO 接口,聲明接口方法。

建立一個 XXXRepository 接口,並繼承 XXXDAO 接口。

提供 XXXRepository 的實現類 XXXRepositoryImpl,並實現 XXXDAO 定義的接口方法。

實際上在使用 XXXRepository 的自定義方法時會調用 XXXRepositoryImpl 中的實現。

爲全部的 Repository 都添加自實現的方法

建立一個 XXXRepository 接口,須要繼承 建立一個 Repository 接口。

提供 XXXRepository 的實現類 XXXRepositoryImpl,且繼承 SimpleJpaRepository,並提供方法的實現。

定義 JpaRepositoryFactoryBean 的實現類,使其生成 XXXRepository 的接口實現類的對象。

修改 \<jpa:repositories/> 節點的 factory-class 屬性指向 JpaRepositoryFactoryBean 的實現類的全類名。

注意:

全局的擴展實現類不要用 Imp 做爲後綴名,
或者爲全局擴展接口添加 @NoRepositoryBean註解告知 SpringData 不要把其做爲 Repository。

個人博客即將同步至騰訊雲+社區,邀請你們一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=2o6lnn9to9mo4
相關文章
相關標籤/搜索