1. 程式人生 > >關於Spring連線資料庫

關於Spring連線資料庫

目錄

概述

java原生JDBC

JdbcTemplate

jdbcTemplate查詢query

jdbcTemplate更新操作update/delete...

jdbcTemplate最佳實踐

資料庫連線和DataSource

總結


 

概述

Spring框架提供了可擴充套件的SQL資料庫的支援。然而spring的東西使用久了之後,如果不去了解背後的原理的話可能越來越迷糊,本篇部落格的目的是為了 分析spring框架如何實現的和各種資料庫在各種場景下的連線配置的問題。我們將從最原生java資料庫連線講起。

java原生JDBC

相信不少學習java的朋友在第一次接觸資料庫的時候,一定都接觸過最原生的資料庫連線,以mysql為例,大致程式碼按如下:

try{
    //載入JDBC驅動程式:
    Class.forName("com.mysql.jdbc.Driver");

    String url = "jdbc:mysql://127.0.0.1:3306/dbname";
    String username = "";
    String password = "";

    // 建立與MySQL資料庫的連線類的例項
    Connection conn = DriverManager.getConnection(url, username, password);

    // 用conn建立Statement物件類例項
    Statement sql_statement = conn.createStatement();

    // 執行sql
    ResultSet result = sql_statement.executeQuery(query);
    //處理結果
     ...
}catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                // 關閉連線
                try {
                    conn.close();
                    System.out.println("Database connection terminated");
                } catch (Exception e) { /* ignore close errors */
                }
      }
 }

上面的程式碼就是純jdbc的連線資料庫程式碼,只需安裝jdk匯入java 的原生包就可以寫出來,不需要使用任何第三方的包。

上述程式碼主要有下面這幾個步驟:

  1. 載入JDBC驅動程式
  2. 建立資料庫的連線(需要使用者名稱密碼等資訊) 
  3. 建立一個Statement  
  4. 執行SQL語句(處理結果)
  5. 處理異常,關閉連線  

上面的程式碼可以看出一些問題,比如模版程式碼過多,對於一個使用者來說,他可能只是想執行一條sql語句,並不想關心其他連線,異常處理,載入驅動的問題。我們可以很容易想到的是,如果有一個方法或者一個工具,讓使用者可以只需要指定好資料庫連線資訊,就可以執行各種資料庫操作,豈不是非常好嗎?這就是框架開發這想做的事情。

JdbcTemplate

JdbcTemplate是spring提供的最經典也是最流行的jdbc實現方式之一。現在除了JdbcTemplate之外,還有SimpleJdbcInsert,SimpleJdbcCall兩個更新的實現方式。

JdbcTemplate是“最底層”的方法,所有其他spring的方法在他們的包裝之下都使用了JdbcTemplate。

JdbcTemplate是spring JDBC core這個包中的重點類。 它處理資源的建立和釋放,幫助我們避免常見錯誤,例如上面直接使用java原生程式碼而忘記關閉連線。 它執行核心JDBC工作流的基本任務(例如語句建立和執行),應用程式程式碼只需要提供SQL並提取結果。 JdbcTemplate類主要包含下面這些功能:

  • 執行SQL查詢語句
  • 執行更新語句和儲存過程呼叫
  • 對ResultSet例項執行迭代並提取返回的引數值。
  • 捕獲JDBC異常並將它們轉換為org.springframework.dao packag中定義的通用的,資訊量更大的異常層次結構。

使用jdbcTemplate需要配置datasource。使用spring ioc容器的話,則可以把datasource配置為一個bean,直接讓資料庫操作的時候引用即可。spring文件特別指出:應始終將DataSource配置為Spring IoC容器中的bean。這說明spring對於ioc容器中的datasource bean提供了非常多方便的功能。

我們可以使用jdbcTemplate來執行查詢,更新,以及一些其他操作。如下:

jdbcTemplate查詢query

下面的例子是返回資料的數量

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);

下面的查詢是返回一個字串:

String lastName = this.jdbcTemplate.queryForObject(
        "select last_name from t_actor where id = ?",
        new Object[]{1212L}, String.class);

也可以查詢並填充單個物件:

Actor actor = this.jdbcTemplate.queryForObject(
        "select first_name, last_name from t_actor where id = ?",
        new Object[]{1212L},
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

查詢並填充一系列物件:

List<Actor> actors = this.jdbcTemplate.query(
        "select first_name, last_name from t_actor",
        new RowMapper<Actor>() {
            public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                Actor actor = new Actor();
                actor.setFirstName(rs.getString("first_name"));
                actor.setLastName(rs.getString("last_name"));
                return actor;
            }
        });

在應用程式當中為了分離邏輯,我們常常將上述程式碼的RowMapper邏輯提出去,變成如下兩個方法:

public List<Actor> findAllActors() {
    return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}

private static final class ActorMapper implements RowMapper<Actor> {

    public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
        Actor actor = new Actor();
        actor.setFirstName(rs.getString("first_name"));
        actor.setLastName(rs.getString("last_name"));
        return actor;
    }
}

值得一提的是,上述程式碼在spring batch的讀資料環節也有應用。

jdbcTemplate更新操作update/delete...

下面是一個insert插入的例子:

this.jdbcTemplate.update(
        "insert into t_actor (first_name, last_name) values (?, ?)",
        "Leonor", "Watling");

下面是一個更新update的例子:

this.jdbcTemplate.update(
        "update t_actor set last_name = ? where id = ?",
        "Banjo", 5276L);

下面是一個刪除的例子:

this.jdbcTemplate.update(
        "delete from actor where id = ?",
        Long.valueOf(actorId));

事實上上述三個操作我們可以看到呼叫的都是jdbcTemplate的同一個方法,只是sql語句不同。

jdbcTemplate最佳實踐

使用jdbcTemplate的好處有執行緒安全,配置簡單,只需一次配置即可等有點。在使用jdbcTemplate的時候也有最佳實踐可以遵循。

使用JdbcTemplate類時的常見做法是在Spring配置檔案中配置DataSource,然後將共享的DataSource的bean依賴注入到DAO類中。 JdbcTemplate是在DataSource的setter中建立的。 這意味著DAO類有似於以下的程式碼:

public class JdbcCorporateEventDao implements CorporateEventDao {

    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    // JDBC-backed implementations of the methods on the CorporateEventDao follow...
}

對應的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:context="http://www.springframework.org/schema/context"
    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">

    <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <context:property-placeholder location="jdbc.properties"/>

</beans>

在使用springboot開發的實際體驗中,我們常常用java註解配置以及application.properties配置檔案代替上述內容,但是其內容都是一樣的,必定有驅動名,url,使用者名稱,密碼等資訊,這就是和我們使用原生jdbc程式碼去連資料庫有聯絡的地方。無論使用何種方式,該有的一個也不會少。

無論您選擇使用上述哪種模板初始化樣式,每次要執行SQL時,很少有需要建立JdbcTemplate類的新例項。 配置完成後,JdbcTemplate例項是執行緒安全的。 如果我們應用程式訪問多個數據庫,則可能需要多個JdbcTemplate例項(例如做資料遷移,就是一個典型的場景),這需要多個DataSource,隨後需要多個不同配置的JdbcTemplate例項。

關於JdbcTemplate的內容我們暫時瞭解到這裡,他當然還提供了很多其他方法,可以在需要使用時直接檢視其文件或者介面即可。

資料庫連線和DataSource

我們在上面已極多次提到了DataSource,可以猜出它是spring用來管理資料庫連線的,現在,我們專門來研究一下dataSource的內容。

Spring通過DataSource獲取與資料庫的連線。 DataSource是java定義的JDBC規範的一部分,是一個通用的連線工廠。 它允許容器或框架從應用程式程式碼中隱藏連線池和事務管理問題。在大型專案中,作為開發人員,則無需瞭解有關如何連線到資料庫的詳細資訊。 這是設定資料來源的管理員的責任。 

使用Spring的JDBC層時,可以從JNDI獲取資料來源,也可以使用第三方提供的連線池實現配置自己的資料來源。 流行的實現是Apache Jakarta Commons DBCP和C3P0。 Spring發行版中的實現僅用於測試目的,不提供池。

以下示例是如何在Java中配置DriverManagerDataSource型別的datasource的例子:

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");

可以看到配置的就是連線資料庫必須的資訊。

下面是一個DBCP實現的DataSource配置

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

雖然實現類不一樣,其他其要求的配置都是一樣的。

除了上述的配置方式外,我們還有下面這些方法配置datasource

  • 使用DataSourceUtils類提供的功能,
  • 實現SmartDataSource
  • 繼承AbstractDataSource
  • 使用SingleConnectionDataSource
  • 使用DriverManagerDataSource
  • 使用TransactionAwareDataSourceProxy

上述方式就不一一展開,我們可以在使用時根據需求選擇。

總結

最後做個總結,我們不難看出,無論使用spring提供的什麼方式去進行資料庫操作,我們始終需要去配置datasouce,而無論使用哪種實現的datasource我們也必須提供和配置對應的和原生jdbc連線一樣的資料庫連線資訊,springboot通過jar包可以幫我們決定驅動,但是url,使用者名稱,密碼仍然要我們提供。我們不難想到其實現必然要做原生jdbc連線做的事情。

而使用spring的提供的資料庫連線的好處也是顯而易見的,很多資料庫連線的問題,它都幫我們管理解決了,如關閉連線等。