Spring Data(一)概念和倉庫的定義

Spring Data(一)概念和倉庫的定義

Spring Data的主要任務是為數據訪問提供一個相似的、一致的、基于Spring的編程模型,同時又保留著下面各個數據存儲的特征。它使得使用數據訪問技術非常的簡單,包括關系型和非關系型數據庫、map-reduce框架、云數據服務等。這是一個傘項目,它包含許多指定數據庫的子項目。這個項目是許多公司和開發者一起開發而成的,他們是這項令人興奮的技術的幕后作者。

特征:

  • 強大的倉庫和定制的實體映射抽象
  • 從倉庫方法名字衍生出的動態查詢
  • 提供了基礎屬性實現的基礎類
  • 支持透明的審計(創建、最終修改)
  • 整合指定倉庫代碼的可能性
  • 通過JavaConfig和指定的xml命名空間非常容易的進行Spring整合
  • 用Spring MVC controller進行先進的整合
  • 交叉存儲持久化的實驗性的支持

主要的模塊

  • Spring Data Commons 每一個Spring Data項目的核心基礎概念
  • Spring Data Gemfire 提供了從Spring應用的簡單的配置和訪問Gemfire
  • Spring Data JPA 提供了非常簡單的基于JPA倉庫的實現
  • Spring Data JDBC 基于JDBC的倉庫
  • Spring Data KeyValue 基于Map的倉庫和非常簡單的創建鍵-值存儲的模塊
  • Spring Data LDAP 為Spring LDAP提供倉庫支持
  • Spring Data MongoDB 為MongoDB提供基于Spring的文檔實體和存儲
  • Spring Data REST 作為超媒體RESTful資源輸出Spring Data存儲
  • Spring Data Redis 提供簡單的配置和從Spring應用到redis的訪問
  • Spring Data for Apache Cassandra ——Apache Cassandra的Spring Data模塊
  • Spring Data for Apache Solr ——Apache Solr 的Spring Data模塊

社區模塊

  • Spring Data Aerospike
  • Spring Data ArangoDB
  • Spring Data Couchbase
  • Spring Data Azure DocumentDB
  • Spring Data DynamoDB
  • Spring Data Elasticsearch
  • Spring Data Hazelcast
  • Spring Data Jest
  • Spring Data Neo4j
  • Spring Data Vault

相關模塊

  • Spring Data JDBC Extensions 在Spring框架內提供了JDBC的擴展
  • Spring for Apache Hadoop 提供統一的配置模型、為HDFS, MapReduce, Pig,和 Hive提供API,簡化了Hadoop。
  • Spring Content 使內容和你的Spring Data實體發生聯系,存儲在不同的存儲介質中,File-system, S3, Database 或者MongoDB
  • Spring Boot 啟動器

如果你正在使用SpringBoot,你將繼承每一個項目的預定義版本。你可以配置spring-data-releasetrain.version 插入你想要的版本。

Spring Data顯著減小了樣板化代碼的數量,為各個持久化存儲實現了數據訪問層。

Spring Data Commons

Spring Data Commons項目是所有Spring Data子項目的基礎,它為許多關系型和非關系型數據庫提供開發解決方案。由于各個Spring Data模塊的起始日期不同,他們中的大多數都有著主要版本和次要版本,找到適合的版本的最簡單的方法是依賴Spring Data 版本串BOM,它是我們用最合適的版本定義的。在Maven項目中,你可以在<dependencyManagement />中聲明依賴,如下:

<dependencyManagement>
 <dependencies>
   <dependency>
     <groupId>org.springframework.data</groupId>
     <artifactId>spring-data-releasetrain</artifactId>
     <version>${release-train}</version>
     <scope>import</scope>
     <type>pom</type>
   </dependency>
 </dependencies>
</dependencyManagement>

如果你正在使用Spring Boot時,它已經給你選擇了一個最近的Spring Data版本。

Spring Data倉庫抽象化的中心接口是Repository,它使用域的類和ID的類型作為泛型參數。這個接口作為標記接口的角色,捕獲你要使用的類型,并幫助你發現繼承此類型的接口,CrudRepository為管理的實體類提供了復雜的CRUD功能。

public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
    <S extends T> S save(S entity);
    Optional<T> findById(ID primaryKey);
    Iterable<T> findAll();
    long count();
    void delete(T entity);
    boolean existsById(ID primaryKey);
 // … more functionality omitted.
}

我們也提供了特殊技術的持久化抽象,例如:JpaRepository、MongoRepository等。這些接口都繼承了CrudRepository,并且輸出了各自持久化技術的能力。

PagingAndSortingRepository接口繼承了CrudRepository,并添加了一些額外的分頁功能:

public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    Iterable<T> findAll(Sort sort);
    Page<T> findAll(Pageable pageable);
}

訪問一個實體第二頁的例子如下:

PagingAndSortingRepository<User, Long> repository = // … get access to a bean Page<User>
users = repository.findAll(new PageRequest(1, 20));

通過這些方法,還衍生出了刪除和統計的功能,如下:

interface UserRepository extends CrudRepository<User, Long> {
    long countByLastname(String lastname);
}
interface UserRepository extends CrudRepository<User, Long> {
    long deleteByLastname(String lastname);
    List<User> removeByLastname(String lastname);
}

查詢方法:

標準的CRUD方法在底層的數據存儲上都有對應的查詢,使用SpringData,聲明這些查詢分為4步:

1、聲明接口繼承Repository或者Repository的子類,并標注實體類型和ID類型

interface PersonRepository extends Repository<Person, Long> { … }

2、在接口中聲明方法:

interface PersonRepository extends Repository<Person, Long> {
    List<Person> findByLastname(String lastname);
}

3、設置Spring生成接口代理

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories
class Config {}

或者使用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: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.xsd">
  
   <jpa:repositories base-package="com.acme.repositories"/>
</beans>

上面的例子中,使用了Jpa的命名空間,如果你使用其他倉庫的抽象,換成其他倉庫對應的命名空間即可,例如:你正在使用MongoDB,可以換成MongoDB對應的命名空間。

值得注意的是,JavaConfig并沒有默認配置注解類的路徑作為包路徑,在xml中,掃描包路徑的參數配置在base-package參數中,對應的javaConfig將使用注解@Enable-*。

4、獲取注入的Repository實例,并使用它

class SomeClient {
    private final PersonRepository repository;
    SomeClient(PersonRepository repository) {
        this.repository = repository;
    }
    void doSomething() {
        List<Person> persons = repository.findByLastname("Matthews");
    }
}

接下來我們將詳細介紹每一步。

定義倉庫接口

第一步,你定義一個指定實體類的倉庫接口,這個接口必須繼承Repository并且定義了實體類型和ID類型,如果你想輸出Crud方法,你要繼承CrudRepository,不要繼承Repository。

典型的,你的倉庫接口繼承Repository, CrudRepository 或者 PagingAndSortingRepository。要不然,如果你不想繼承Spring Data的接口,你也可以使用@RepositoryDefinition注解你自己的倉庫接口。繼承CrudRepository將輸出一套完成的方法集來操作你的實體,如果你想選擇一些方法輸出,最簡單的方法是從CrudRepository中復制你想要輸出的方法到你自己的倉庫中。這些將允許你在Spring Data倉庫方法中的最頂端定義你自己的抽象。

@NoRepositoryBean
 interface MyBaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
     Optional<T> findById(ID id);
     <S extends T> S save(S entity);
 }
 
interface UserRepository extends MyBaseRepository<User, Long> {
     User findByEmailAddress(EmailAddress emailAddress);
 }

在第一步中,你定義了一個公用的基礎接口,并且輸出了findById和save方法。這些方法將路由到你選擇的存儲的基礎倉庫實現中。這個例子中,如果你定義了SimpleJpaRepository,因為它匹配了CrudRepository方法中的特性,所以UserRepository可以保存users,通過id查找users或者通過email查找users。值得注意的是,中間倉庫接口使用@NoRepositoryBean注解,確保你給所有的倉庫接口添加注解,Spring Data在運行期將不會創建實例。

倉庫方法中的null處理

作為Spring Data2.0,CRUD方法返回一個使用java8的Optional的獨立的聚合實例,標明值的潛在缺少。除此之外,Spring Data支持在查詢方法上返回其他的封裝類型。或者,查詢方法可以選擇根本不使用封裝類型。缺少查詢接口將通過返回null標明。倉庫方法返回集合、封裝類型和流來保護不返回null。

空值注解

你可以使用Spring的空值注解來表達倉庫的空值約束。它提供了在運行期的空值檢查。

  • @NonNullApi 在包級別使用,標明參數的默認行為,返回的結果不接受和生成null值。
  • @NonNull 在參數或者返回值上使用,他們不允許為null。
  • @Nullabe 在參數或者返回值上使用,他們允許為null。

Spring注解是用JSR305的元注解,JSR 305允許工具方IDEA、Eclipse等在通用的方法中提供空安全性的支持,不必提供Spring注解的硬編碼支持。為了提供運行期的空值約束檢查,你需要使非空值活動在包級別中,在package-info.java中使用@NonNullApi。

@org.springframework.lang.NonNullApi 
package com.acme;

一旦非空定義在這個地方,倉庫的查詢方法在運行期將得到一個空約束的驗證。如果查詢結果違反了約束,將會拋出異常,例如,在一些條件下方法返回了null,但是已經聲明了非空。如果你想選擇性的使用空結果,選擇@Nullable注解,使用前面提到的封裝類型將繼續按照希望的那樣工作,例如空結果將會轉入到那個值中。

使用不同空約束的例子:

package com.acme;                                                              
import org.springframework.lang.Nullable;
interface UserRepository extends Repository<User, Long> {
    User getByEmailAddress(EmailAddress emailAddress);                             
    @Nullable User findByEmailAddress(@Nullable EmailAddress emailAdress); 
    Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress); 
}

在前面的例子中,我們已經在package-info.java中,定義了非空的行為。

第一個方法,查詢的執行沒有產生結果,將拋出EmptyResultDataAccessException異常,emailAddress如果傳入空,將拋出IllegalArgumentException。

第二個方法如果沒有查詢結果將返回null,傳入的參數也接受null。

第三個方法如果沒有查詢結果將返回Optional.empty(),如果傳入空參數將拋出IllegalArgumentException。

多Spring Data模塊中使用Repository

在你的項目中使用唯一的Spring Data模塊是非常簡單的,定義范圍內的所有倉庫接口都綁定到Spring Data模塊。有時,應用需要使用多個Spring Data模塊。這種情況下,需要倉庫定義區分不同的持久化技術。Spring Data記錄嚴格的倉庫配置模型,因為它在類路徑下檢測到多個倉庫配置元素。嚴格的配置需要在倉庫或者實體類上的細節決定Spring Data綁定哪個倉庫定義。

如果倉庫定義繼承了指定的模塊倉庫,它是一個有效的特殊的Spring Data模塊的申請者。

如果實體類中使用了指定模塊的注解,它是一個有效的特殊的Spring Data模塊的申請者。Spring Data接受第三方的注解(如:jpa)或者自己提供的注解(如:mongodb)。

倉庫定義使用指定模塊接口:

interface MyRepository extends JpaRepository<User, Long> { } 
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { … } 
interface UserRepository extends MyBaseRepository<User, Long> { … }

MyBaseRepository和UserRepository都繼承了JpaRepository,所以他們都是有效的Jpa模塊的申請者。

倉庫定義使用通用的接口:

interface AmbiguousRepository extends Repository<User, Long> { … } 
@NoRepositoryBean
interface MyBaseRepository<T, ID extends Serializable> extends CrudRepository<T, ID> { … } 
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

AmbiguousRepository和AmbiguousUserRepository只繼承了Repository和CrudRepository,這種情況在使用了唯一的Spring Data模塊時是可行的,在多模塊的情況,它是不能區分使用哪個具體模塊的。

倉庫定義使用實體類注解:

interface PersonRepository extends Repository<Person, Long> { … } 
@Entity
class Person { … } 
interface UserRepository extends Repository<User, Long> { … } 
@Document
class User { … }

PersonRepository相關的Person使用了Jpa的@Entity注解,所以,它的倉庫屬于Jpa。UserRepository使用注解了@Document的User,所以它屬于MongoDB。

倉庫定義使用混合注解實體類:

interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
 
@Entity
@Document
class Person { … }

這個例子中,實體類即使用了Jpa注解,又使用了MongoDB注解。它定義了兩個倉庫:JpaPersonRepository和MongoDBPersonRepository。一個給Jpa使用,另一個給MongoDB使用。這種導致未定義的行為使Spring Data不再有能力區分倉庫的類型。

倉庫類型詳情和指定實體類注解,用來嚴格區分倉庫指向哪一個Spring Data模塊。在一個實體類中使用多個實體技術注解可以服用實體類,但是Spring Data將不能指定倉庫綁定哪一個Spring Data模塊。區分倉庫的最后一個方法是規范倉庫基礎包的路徑?;A包定義了掃描的開始點,倉庫接口都在適合的包中。

基礎包的注解驅動配置:

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
interface Configuration { }

定義查詢方法

倉庫代理有兩種方式導出指定倉庫的查詢。它可以從名字直接導出查詢,或者使用手工定義的查詢。可用的選項取決于實際的存儲??墒?,它有一個策略決定哪一個查詢被生成。

查詢查找策略

下面的策略是可用的倉庫解決查詢的基礎。你可以在命名空間配置策略,通過xml文件中的query-lookup-strategy參數或者Enable*的注解中的queryLookupStrategy參數。一些策略在特殊的存儲中不被支持。

CREATE嘗試從方法名中構造指定倉庫的查詢方法,大概的方法是從方法名中移除一個給定的眾所周知的前綴,然后解析剩余的部分。

USE_DECLARED_QUERY嘗試找到聲明的查詢,如果找不到,將拋出異常。查詢通過注解或其他方法的聲明定義。查看指定存儲的文檔找到可用的選項。如果倉庫不能找到存儲聲明的查詢,它將失敗。

CREATE_IF_NOT_FOUND結合了CREATE和USE_DECLARED_QUERY。它首先查找聲明的查詢,如果不能找到,它將生成一個基于命名的查詢。這是默認的查詢策略。它可以通過方法名字快速的生成查詢,也可以通過查詢的聲明生成查詢。

?這一章我們先介紹到這里,具體的方法命名規則將在下一篇中介紹。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,937評論 6 342
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,868評論 18 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,522評論 1 92
  • 相遇那么不容易 兩個名字相互點頭,致意 用孤獨交換孤獨 用色彩交換色彩 暗地里伸出觸角 試探對方是否接近人的純度 ...
    琴歌素簡閱讀 289評論 0 4
  • (馬太福音5:48) 所以,你們要完全,像你們的天父完全一樣。 登山寶訓是一篇關于幸福最重要的文章,因為在...
    天路客語閱讀 3,239評論 0 1