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模塊。區分倉庫的最后一個方法是規范倉庫基礎包的路徑。基礎包定義了掃描的開始點,倉庫接口都在適合的包中。

基礎包的注解驅動配置:

@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。它首先查找聲明的查詢,如果不能找到,它將生成一個基於命名的查詢。這是默認的查詢策略。它可以通過方法名字快速的生成查詢,也可以通過查詢的聲明生成查詢。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM