04.spring framework的DAO支持

一、Spring數據訪問原則

1、將數據訪問的功能放到一個或多個專注於此項任務的組件中。這樣的組件通常稱爲數據訪問對象(data access objectDAO)或Repository

2、編寫良好的Repository應該以接口的方式暴露數據訪問層所提供的功能(好處1:便於測試,好處2:數據訪問的具體實現通過接口進行了隔離);

3、爲了將數據訪問層與應用程序的其他部分隔離開來,

二、Spring數據訪問異常體系

1、目的

將數據訪問層與應用程序的其他部分隔離開來,Spring採用的方式之一就是提供統一的數據訪問異常體系

2、spring異常體系優勢

Spring數據訪問的異常體系用來解決:

  • JDBC的異常體系過於簡單——實際上,JDBC的異常體系算不上一個體系。
  • Hibernate的異常體系是其本身所獨有的。我們需要的數據訪問異常要具有描述性而且又與特定的持久化框架無關。
不管對於JPA還是Hibernate,異常轉換都不是強制要求的。如果你希望在Repository中拋出特定的JPAHibernate異常,只需
PersistenceException-TranslationPostProcessor省略掉即可,這樣原來的異常就會正常地處理。但是,如果使用了Spring
的異常轉換,你會將所有的數據訪問異常置於Spring的體系之下,這樣以後切換持久化機制的話會更容易。

3、特點

  • 沒有與特定的持久化方式相關聯
  • 異常種類很多、很細粒度
  • 異常都繼承自DataAccessException。這是非檢查型異常:可以根據需要選擇是否捕獲。之所以spring這麼設計是因爲Spring認爲觸發異常的很多問題是不能(無法)在catch代碼塊中修復的,比如數據庫連接失敗等等,是應用程序無法解決的。而不是強制開發人員編寫catch代碼塊(裏面經常是空的)。這把是否要捕獲異常的權力留給了開發人員。

4、使用

爲了利用Spring的數據訪問異常,我們必須使用Spring所支持的數據訪問模板

三、數據訪問模板化

1、思想

    儘管在某個複雜的過程中包含多個步驟,但是涉及到子類實現參與的只有幾個。承運人(抽象層)負責推動整個流程。你只會在必要的時候進行參與,其餘的過程不必關心。這反映了一個強大的設計模式:模板方法模式
    這也是Spring在數據訪問中所使用的模式。不管我們使用什麼樣的技術,都需要一些特定的數據訪問步驟。例如,我們都需要獲取一個到數據存儲的連接並在處理完成後釋放資源。這都是在數據訪問處理過程中的固定步驟,但是每種數據訪問方法又會有些不同,我們會查詢不同的對象或以不同的方式更新數據,這都是數據訪問過程中變化的部分
 
2、設計
 
Spring將數據訪問過程中固定的和可變的部分明確劃分爲兩個不同的類:模板(template)回調(callback)
 
這兩部分職責與協作:
 
  • Spring的模板類負責:事務控制、 管理資源、處理異常(處理數據訪問的固定部分)。
  • 回調負責:應用程序相關的數據訪問的語句、 綁定參數、整理結果集

3、使用(實現)

針對不同的持久化平臺,Spring提供了多個可選的模板

​​​​下表是Spring提供的數據訪問模板,分別適用於不同的持久化機制:

 
jca.cci.core.CciTemplate
JCA CCI連接
jdbc.core.JdbcTemplate
JDBC連接
jdbc.core.namedparam.NamedParameterJdbcTemplate
支持命名參數的JDBC連接
jdbc.core.simple.SimpleJdbcTemplate
通過Java 5簡化後的JDBC連接(Spring 3.1中已經廢棄)
orm.hibernate3.HibernateTemplate
Hibernate 3.x以上的Session
orm.ibatis.SqlMapClientTemplate
iBATIS SqlMap客戶端
orm.jdo.JdoTemplate
Java數據對象(Java Data Object)實現
orm.jpa.JpaTemplate
Java持久化API的實體管理器

以下三個是比較常用的:

  • JDBCJdbcTemplate
  • Hibernate:HibernateTemplate(最流行的基於POJOORM方案)
  • JPA:JpaTemplate(最流行的基於POJOORM方案)

4、準備

但首先要說明的是Spring所支持的大多數持久化功能都依賴於數據源。

3.1 配置數據源

1、目的
 
通過數據源建立了與數據庫的連接
 
2、Spring配置數據源
spring提供了在Spring上下文中配置數據源bean的多種方式,包括:
  • 通過JDBC驅動程序定義的數據源:無連接池、適合小應用
  • 通過JNDI查找的數據源;(最好)
  • 數據庫連接池的數據源。(其次)
    • Apache Commons DBCP
    • c3p0
    • BoneCP
  • 【嵌入式】的數據源:測試、開發

3.1.1 使用【JNDI】數據源

1、前提

使用web服務器時(如tomcat),web服務器允許你配置通過JNDI獲取數據源。

2、好處

  • 數據源完全可以在應用程序之外進行管理,這樣應用程序只需在訪問數據庫的時候查找數據源就可以了。
  • 在應用服務器中管理的數據源通常以池的方式組織,從而具備更好的性能
  • 支持系統管理員對其進行熱切換

3、使用方式

利用Spring,我們可以像使用Spring bean那樣配置JNDI中數據源的引用並將其裝配到需要的類中。
 
1)XML方式
jee命名空間下的<jee:jndi-lookup>元素可以用於檢索JNDI中的任何對象(包括數據源)並將其作爲Springbean。如果應用程序的數據源配置在JNDI中,可以使用<jee:jndi-lookup>元素將其裝配到Spring中,如下

其中jndi-name屬性用於指定JNDI中資源的名稱。如果只設置了jndi-name屬性,那麼就會根據指定的名稱查找數據源。但是,如
果應用程序運行在Java應用服務器中,你需要將resource-ref屬性設置爲true,這樣給定的jndi-name將會自動添加“java:comp/env/前綴。
 
2)JavaConfig方式
其中JndiObjectFactoryBean是spring內置提供的

3.1.2 使用【數據庫連接池】數據源

1、說明

  • 這些都是第三方數據庫連接池作爲數據源
  • 如果無法使用JNDL配置數據源,其次選擇在spring中配置數據庫連接池數據源,spring沒有提供實現,但是可以通過集成下面連接池進行實現:
    • Apache Commons DBCP
    • c3p0
    • BoneCP
2、使用
2.1 配置apache的DBCP數據源
依賴包:
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>7.0.47</version>
</dependency>
1)XML形式
2)JavaConfig形式

前四個屬性是必需的:

  • driverClassName指定了JDBC驅動類的全限定類名。這裏配置的是H2數據庫的數據源。
  • url用於設置數據庫的JDBCURL。最
  • usernamepassword用於在連接數據庫時進行認

其他屬性如下表:

initialSize
池啓動時創建的連接數量
maxActive
同一時間可從池中分配的最多連接數。如果設置爲0,表示無限制
maxIdle
池裏不會被釋放的最多空閒連接數。如果設置爲0,表示無限制
maxOpenPreparedStatements
在同一時間能夠從語句池中分配的預處理語句(prepared statement)的最大數量。如果設置爲0,表示無限制
maxWait
在拋出異常之前,池等待連接回收的最大時間(當沒有可用連接時)。如果設置爲-1,表示無限等待
minEvictableIdleTimeMillis
連接在池中保持空閒而不被回收的最大時間
minIdle
在不創建新連接的情況下,池中保持空閒的最小連接數
poolPreparedStatements
是否對預處理語句(prepared statement)進行池管理(布爾值)

2.2 配置C3P0數據源

依賴包:

<dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
</dependency>

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		">

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.5.xxx:3306/ml_test"/>
        <property name="user" value="root"/>
        <property name="password" value="xxxx"/>
    </bean>
</beans>

其中value部分可以引用外部配置文件:

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        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-4.3.xsd">

    <context:property-placeholder location="db/db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

其中通過<context:property-placeholder location="db/db.properties"/>引入外部配置文件後,通過${xxx}的方式使用

3.1.3 使用【基於JDBC驅動】的數據源

1、說明:使用簡單,無連接池,適用場景有限(小應用,開發環境)

2、種類:

  • org.springframework.jdbc.datasource.DriverManagerDataSource:每次返回新連接,無池化管理
  • org.springframework.jdbc.datasource.SimpleDriverDataSource:直接使用JDBC驅動,來解決在特定環境下的類加載問題,這樣的環境包括OSGi容器
  • org.springframework.jdbc.datasource.SingleConnectionDataSource:在每個連接請求時都會返回同一個的連接

3、使用

3.1.4 使用【嵌入式】的數據源

1、作用(適用場景)

開發 和 測試時,好處:每次重啓應用或運行測試的時候,都能夠重新填充測試數據

2、使用:

以下配置會預先加載一組測試數據:

  • h2數據庫位於類路徑下
  • type配置爲H2爲使用H2數據庫,配置成DERBY爲使用derby數據庫
  • 可以不配置也可以配置多個<jdbc:script>元素來搭建數據庫,上面的配置包含了兩個<jdbc:script>元素:第一個引用了schema.sql,它包含了在數據庫中創建表的SQL;第二個引用了test-data.sql,用來將測試數據填充到數據庫中。
  • <jdbc:embedded-database>元素還會暴露一個數據源,我們可以像使用其他的數據源那樣來使用它。 在這裏,id屬性被設置成了dataSource,這也是所暴露數據源的bean ID。因此,當我們需要javax.sql.DataSource的時候,就可以注入dataSource bean
使用javaConfig方法配置:

3.2 選擇數據源

1、使用場景:

在某種環境下需要其中一種數據源,而在另外的環境中需要不同的數據源
 
2、方案
 
使用系列文章springframework IOC一文中提到的profile機制,將每個數據源配置在不同的profile
3、使用
1)javaConfig方式
package com.mzj.springframework.dao._01_dataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

import javax.sql.DataSource;

/**
 * @Auther: mazhongjia
 * @Date: 2020/4/20 13:07
 * @Version: 1.0
 */
@Configuration
public class DataSourceConfiguration {

    /**
     * 開發數據源
     *
     * @return
     */
    @Profile("development")
    @Bean
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }

    /**
     * 生成環境的數據源
     *
     * @return
     */
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/SpittrDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

2)XML方式

 
4、結論
通過使用profile功能,會在運行時選擇數據源,這取決於哪一個profile處於激活狀態。
  • 當且僅當developmentprofile處於激活狀態時,會創建嵌入式數據庫;
  • 當且僅當qa profile處於激活狀態時,會創建DBCPBasicDataSource;
  • 當且僅當productionprofile處於激活狀態時,會從JNDI獲取數據源

3.3 使用Spring的JDBC框架訪問數據庫

方式1:使用JDBC訪問數據庫

  • 優點
    • 低層次,完全控制如何讀取和管理數據
    • 使用JDBC能夠更好地對數據訪問的性能進行調優
    • JDBC允許使用數據庫所有特性
  • 缺點
    • 業務無關模式化代碼、冗長,如資源申請、資源釋放、異常處理
    • 不知道SQLException異常的具體原因,不知道如何處理

方式2:使用持久層框架訪問數據庫

  • 引入:清理資源和異常處理,非常重要,要保證正確性、健壯性。
  • Spring的JDBC框架作用:承擔了資源管理和異常處理的工作,從而簡化了JDBC代碼,讓我們只需編寫從數據庫讀寫數據的必需代碼。

Spring的JDBC框架使用

1、spring的JDBC模板

     1)三種可選的模板

JdbcTemplate
最基本的Spring JDBC模板,這個模板支持簡單的JDBC數據庫訪問功能以及基於索引參數的查詢
絕大多數時候使用
NamedParameterJdbcTemplate
使用該模板類執行查詢時可以將值以命名參數的形式綁定到SQL中,而不是使用簡單的索引參數;
只有在你需要使用命名參數的時候,才需要使用
SimpleJdbcTemplate
該模板類利用Java 5的一些特性如自
動裝箱、泛型以及可變參數列表來簡化JDBC模板的使用
Spring 3.1開始被廢棄

2、JdbcTemplate使用

步驟1:定義DataSource

略,見前文。

步驟2:聲明JdbcTemplate的bean,並向其注入DataSource

JavaConfig方式:

DataSource是通過構造器參數注入進來的。這裏所引用的dataSourcebean可以是javax.sql.DataSource的任意實現,比如:

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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        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-4.3.xsd">

    <context:property-placeholder location="db/db.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

步驟3:將jdbcTemplate裝配到Repository(業務DAO)中並使用它來訪問數據庫

/**
   * Repository1 bean(DAO),依賴JdbcTemplate
   * @param jdbcTemplate
   * @return
   */
  @Bean
  public SpitterRepository spitterRepository(JdbcTemplate jdbcTemplate) {
    return new JdbcSpitterRepository(jdbcTemplate);
  }

步驟4:SpitterRepository是接口,需要實現之,即JdbcSpitterRepository

比如insert:

        public Spittle save(Spittle spittle) {
		long spittleId = insertSpittleAndReturnId(spittle);
		return new Spittle(spittleId, spittle.getSpitter(), spittle.getMessage(), spittle.getPostedTime());
	}

	private long insertSpittleAndReturnId(Spittle spittle) {
			SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName("Spittle");
			jdbcInsert.setGeneratedKeyName("id");
			Map<String, Object> args = new HashMap<String, Object>();
			args.put("spitter", spittle.getSpitter().getId());
			args.put("message", spittle.getMessage());
			args.put("postedTime", spittle.getPostedTime());
			long spittleId = jdbcInsert.executeAndReturnKey(args).longValue();
			return spittleId;
	}
  • 已經沒有創建、釋放、連接的樣板代碼:樣板代碼被巧妙地隱藏到JDBC模板類中了
  • 已經沒有SQLException處理的代碼:在內部,JdbcTemplate將會捕獲所有可能拋出的SQLException,並將通用的SQLException轉換爲表10.1所列的那些更明確的數據訪問異常,然後將其重新拋出。因爲Spring的數據訪問異常都是運行時異常,所以我們不必在save()方法中進行捕獲

3.4 使用Spring集成ORM框架訪問數據庫

1、ORM框架:

  • Hibernate
  • MyBatis
  • JDO,(Java Data Objects),Java數據對象
  • JPA、(Java Persistence API)、Java持久化API
2、使用ORM框架可以實現
  • 延遲加載:複雜的對象關係,用到時才真正加載
  • 預先抓取:使用一次查詢獲取完整的關聯對象
  • 級聯:更改數據庫中的表會同時修改其他表

3、ORM框架可以提供

  • 支持集成Spring聲明式事務;
  • 透明的異常處理;
  • 線程安全的、輕量級的模板類;
  • DAO支持類;
  • 資源管理

3.4.1 使用Spring集成Hibernate框架訪問數據庫

1、支持特性

  • 緩存
  • 延遲加載
  • 預先抓取
  • 分佈式緩存

2、使用說明

直接使用Hibernate,不在spring中使用時
(依賴關係:SessionFactory創建Session實現,業務Repository依賴  Session實現)
  • Session接口提供了基本的數據訪問功能,如保存、更新、刪除以及從數據庫加載對象的功能
  • SessionFactory主要負責創建Session接口的實現以及Session的打開、關閉以及管理

Spring中使用時,需要通過spring提供的HibernateSession工廠bean實現

(依賴關係:Spring的SessionFactoryBean依賴DataSource,業務Repository依賴  SessionFactory)

通過Spring的某一個SessionFactoryBean來獲取Hibernate的SessionFactory,Spring提供的Hibernate Session工廠bean有三類:

  • org.springframework.orm.hibernate3.LocalSessionFactoryBean:Hibernate使用的是3.2-4之間版本(不包括4)+ 使用XML定義映射
  • AnnotationSessionFactoryBean:Hibernate使用的4以下的版本(不包括4)+ 使用註解方式定義持久
  • org.springframework.orm.hibernate4.LocalSessionFactoryBean:Hibernate使用4及以上版本 + 支持基於XML的映射和基於註解(通過JPA@Entity或@MappedSuperclass以及Hibernate@Entity的映射

這裏只列出與hibernate4集成代碼示例:

1)增加依賴:

<springframe.version>4.3.3.RELEASE</springframe.version>

<!--基礎依賴-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframe.version}</version>
</dependency>
<!--集成hibernate依賴-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${springframe.version}</version>
</dependency>
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>4.1.6.Final</version>
</dependency>
<dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
</dependency>
<!--測試數據庫依賴-->
<dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.182</version>
</dependency>

2)Spring應用上下文中配置【數據源】以及【spring提供的HibernateSession工廠bean】

3)創建自己的(業務)Repository類了

方式一:SpringHibernate的早期版本,使用HibernateTemplate

       缺點:Repository實現會直接與Spring耦合

方式二:使用上下文SessionContextual session

原理:會直接將Hibernate的SessionFactory裝配到業務Repository中,並使用它來獲取Session,在Repository中不會看到spring的影子

優點:Repository類及實現不與spring偶合(除了@Repository註解以外.....)

代碼見:com.mzj.springframework.dao._03_hibernate._4子包中代碼。

3.4.2 使用Spring集成JPA訪問數據庫

1、說明

JPA:Java持久化API

JPA是下一代Java持久化標準,JPA基於POJO的持久化機制,JPA借鑑了Hibernate+Java數據對象(JDO)+Java5的註解

2、Spring集成JPA傳統方式(開發麻煩(業務Repository中使用EntityManager操作數據庫),過時的,瞭解即可,後面的Spring Data JPA實現原理沒變,只不過Spring給我們簡化了這個過程)

原理:使用EntityManagerFactory(實體管理器工廠)的實現類來獲取EntityManager(實體管理器)實例

EntityManager的創建與管理有兩種方式(關鍵的區別不在於EntityManager本身,而是在於EntityManager的創建和管理方式):

  • 應用程序管理類型:適用於非JAVAEE程序(程序直接向實體管理器工廠請求實體管理器,EntityManagerFactory創建EntityManager,程序負責打開或關閉實體管理器、在事務中對其進行控制
  • 容器管理類型:適用於JAVAEE程序(實體管理器由Java EE創建和管理、實體管理器直接通過注入或JNDI來獲取,應用程序根本不與實體管理器工廠打交道)

Spring都會負責管理EntityManager。如果你使用的是應用程序管理類型的實體管理器,Spring承擔了應用程序的角色並以透明的方式處理EntityManager。在容器管理的場景下,Spring會擔當容器的角色。

這兩種實體管理器工廠分別由對應的Spring工廠Bean創建:

  • LocalEntityManagerFactoryBean生成應用程序管理類型EntityManager-Factory
  • LocalContainerEntityManagerFactoryBean生成容器管理類型的EntityManagerFactory
選擇應用程序管理類型的還是容器管理類型的EntityManager Factory,對於基於Spring的應用程序來講是完全透明的,唯一的區別在於如何在spring中進行配置


使用方式:

1)spring上下文中配置實體管理器工廠LocalEntityManagerFactoryBean

略。
2)編寫基於JPA的業務Repository

略。

3、Spring Data JPA(自動化的Spring集成JPA實現方式)

 優點:只編寫Repository接口就可以了。根本就不再需要實現類了。

 使用

1)增加依賴

<springframe.version>4.3.3.RELEASE</springframe.version>

<!--基礎依賴-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframe.version}</version>
</dependency>

<!--集成springdata-jpa依賴-->
<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>${springframe.version}</version>
</dependency>
<dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-jpa</artifactId>
      <version>1.3.2.RELEASE</version>
</dependency>
<dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>4.0.1.Final</version>
</dependency>
<dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
</dependency>
<!--測試數據庫依賴-->
<dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.182</version>
</dependency>
2)編寫Spring Data JPA Repository接口
編寫Repository接口關鍵是要從一組接口中挑選一個進行擴展。這裏,SpitterRepository擴展了Spring Data JPA的JpaRepository
package com.mzj.springframework.dao._04_springdata_jpa.db;

import java.util.List;

import com.mzj.springframework.dao._04_springdata_jpa.vo.Spitter;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * Repository interface with operations for {@link Spitter} persistence.
 * @author habuma
 */
public interface SpitterRepository extends JpaRepository<Spitter, Long> {
	  
	Spitter findByUsername(String username);
	
	List<Spitter> findByUsernameOrFullNameLike(String username, String fullName);

}
  • 通過給出泛型類型<Spitter,Long>,JpaRepository進行了參數化,使他能知道這是一個用來持久化Spitter對象的Repository,並且SpitterID類型爲Long
  • 另外,它還會自動繼承18個執行持久化操作的通用方法,如保存Spitter、刪除Spitter以及根據ID查詢Spitter
3)不需要編寫Repository接口的實現Spring Data已經爲我們做好了這件事請,但是要在spring中聲明一下啓動Spring Data JPA
方式一:JavaConfig的方式:
在Javaconfig類上增加如下註解:
@Configuration
@EnableJpaRepositories("com.mzj.springframework.dao._04_springdata_jpa")

      會根據這個包路徑進行掃描,掃描它的基礎包及其子包來查找擴展自Spring Data JPA Repository接口的所有接口。如果發現了擴展自Repository的接口,它會自動生成(在應用啓動的時候)這個接口的實現(在Spring的應用上下文創建的時候生成的)。

方式二:XML的方式

4)如果自帶的18個方法不夠,可以添加自定義方法

方式一:在業務Repository接口中按照下面的DSL【語法規則】增加自定義查詢

原理:當創建Repository實現的時候,Spring Data會檢查Repository接口的所有方法,解析方法的名稱,並基於被持久化的對象來試圖推測方法的目的。本質上,Spring Data定義了一組小型的領域特定語言(domain-specific language ,DSL),在這裏,持久化的細節都是通過Repository方法的簽名來描述的。

語法規則:
由一個動詞、一個可選的主題 (Subject)、關鍵詞By以及一個斷言所組成。
  • Spring Data允許在方法名中使用四種動詞:getreadfindcount。其中,動詞getreadfind是同義的,這三個動詞對應的Repository方法都會查詢數據並返回對象。而動詞count則會返回匹配對象的數量,而不是對象本身
  • 主題是可選的。它的主要目的是讓你在命名方法的時候,有更多的靈活性,對於大部分場景來說,主題會被省略掉。readSpittersByFirstnameOrLastname()與readPuppiesByFirstnameOrLastname()並沒有什麼差別
  • 要查詢的對象類型是通過如何參數化JpaRepository接口來確定的,而不是方法名稱中的主題
  • 在省略主題的時候,有一種例外情況。如果主題的名稱以Distinct頭的話,那麼在生成查詢的時候會確保所返回結果集中不包含重複記錄。
  • 斷言:用於限制結果集,在readByFirstnameOrLastname()這個樣例中,會通過firstname或lastname屬性的值來限制結果。
    • 在斷言中,會有一個或多個限制結果的條件。每個條件必須引用一個屬性,並且還可以指定一種比較操作。如果省略比較操作符的話,那麼這暗指是一種相等比較操作
    •  
      比較操作有(下面不全):
  • 要對比的屬性值就是方法的參數,比如:

  • 處理String類型的屬性時,條件中可能還會包含【IgnoringCase】或【IgnoresCase】,這樣在執行對比的時候就會不再考慮字符是大寫還是小寫
  • 方法參數的名稱是無關緊要的,但是它們的順序必須要與方法名稱中的操作符相匹配。
  • 可以在方法名稱的結尾處添加【OrderBy】,實現結果集排序,如果要根據多個屬性排序的話,只需將其依序添加到OrderBy中即可,升序【Asc】,降序【Desc】

  • 條件部分是通過【And】或者【Or】進行分割的
findByUsername()這個樣例中,動詞是find,斷言是Username,主題並沒有指定,暗含的主題是Spitter

完整的示例爲:

一句話總結:使用屬性名和關鍵字構建Repository方法簽名,就能讓Spring Data JPA生成方法實現,完成幾乎所有能夠想象到的查詢。Spring Data這個小型的DSL依舊有其侷限性,有時候通過方法名稱表達預期的查詢很煩瑣,甚至無法實現。如果遇到這種情形的話,Spring Data能夠讓我們通過@Query註解來解決問題。

方式二:通過@Query註解添加(聲明)自定義查詢

使用場景1:

如果所需的數據無法通過方法名稱進行恰當地描述,那麼我們可以使用@Query註解,爲Spring Data提供要執行的查詢。

 
使用場景2:
如果按照命名約定,方法的名稱特別長的時候,也可以使用這個註解
使用方式:
其中@Query中的參數是實現業務查詢的SQL語句                                                      

侷限性:

@Query註解僅限於單個JPA查詢。如果我們需要更爲複雜的功能,無法在一個簡單的查詢中處理的話,使用Spring Data JPA的混合自定義的功能
 
方式三:使用混合自定義的功能
使用場景:複雜查詢時,@Query註解無法解決
原理:當Spring Data JPA爲Repository接口生成實現的時候,它還會查找名字與接口相同,並且添加了Impl後綴的一個類。如果這個類存在的話(我們自己創建的話),Spring Data JPA將會把它的方法與Spring Data JPA自動生成的方法合併在一起。
 
混合自定義的功能使用:
  • 按照spring data jpa約定我們自己創建接口的實現類
    • 約定:Repository接口的實現類名叫【Repository接口Impl】,但是此類不需要implements Repository接口
    • 爲了讓使用Repository接口的代碼能通過Repository接口看到這個自定義方法,需要再單獨定義一個接口,將準備實現的方法放入接口中,然後讓【Repository接口Impl】實現這個接口,並讓【Repository接口】也extends這個接口

  • 在【Repository接口Impl】中注入EntityManager(通過@PersistenceContext註解注入)
  • 【Repository接口Impl】實現這個接口的方法實現中,通過EntityManager實現複雜查詢

其他說明:Impl後綴只是默認的做法,如果你想使用其他後綴的話,只需在配置@EnableJpa-Repositories的時候,設置repositoryImplementationPostfix屬性即可

XML中使用<jpa:repositories>元素來配置Spring Data JPA的話,我們可以藉助repository-impl-postfix屬性指定後綴

5)使用JPA註解或者hibernate JPA註解對實體類屬性進行聲明以進行ORM映射

參考:https://blog.csdn.net/tianya846/article/details/81053343?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

或者《Spring Data Jpa 從入門到精通》

 

市場上 ORM 框架比對

MyBatis:MyBatis 本是 Apache 的一個開源項目 iBatis,2010 年這個項目由 Apache Software Foundation 遷移到了 Google Code,並且改名爲 MyBatis,其着力於 POJO 與 SQL 之間的映射關係,可以進行更爲細緻的 SQL,使用起來十分靈活、上手簡單、容易掌握,所以深受開發者的喜歡,目前市場佔有率最高,比較適合互聯應用公司的 API 場景;缺點就是工作量比較大,需要各種配置文件的配置和 SQL 語句。
Hibernate:Hibernate 是一個開放源代碼的對象關係映射框架,它對 JDBC 進行了非常輕量級的對象封裝,使得 Java 程序員可以隨心所欲的使用對象編程思維來操縱數據庫,並且對象有自己的生命週期,着力點對象與對象之間關係,有自己的 HQL 查詢語言,所以數據庫移植性很好。Hibernate 是完備的 ORM 框架,是符合 JPA 規範的,有自己的緩存機制,上手來說比較難,比較適合企業級的應用系統開發。
Spring Data JPA:可以理解爲 JPA 規範的再次封裝抽象,底層還是使用了 Hibernate 的 JPA 技術實現,引用 JPQL(Java Persistence Query Language)查詢語言,屬於 Spring 的整個生態體系的一部分。由於 Spring Boot 和 Spring Cloud 在市場上的流行,Spring Data JPA 也逐漸進入大家的視野,他們有機的整體,使用起來比較方便,加快了開發的效率,使開發者不需要關係和配置更多的東西,完全可以沉浸在 Spring 的完整生態標準的實現下,上手簡單、開發效率高,又對對象的支持比較好,又有很大的靈活性,市場的認可度越來越高。
OpenJPA :是 Apache 組織提供的開源項目,它實現了 EJB 3.0 中的 JPA 標準,爲開發者提供功能強大、使用簡單的持久化數據管理框架,但功能、性能、普及性等方面更加需要加大力度,所以用的人不人不是特別多。
QueryDSL:QueryDSL 可以在任何支持的 ORM 框架或者 SQL 平臺上以一種通用的 API 方式來構建查詢,目前 QueryDSL 支持的平臺包括 JPA、JDO、SQL、Java Collections、RDF、Lucene、Hibernate Search,同時 Spring Data JPA 也對 QueryDSL 做了很好的支持。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章