關於Spring連線資料庫
目錄
jdbcTemplate更新操作update/delete...
概述
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 的原生包就可以寫出來,不需要使用任何第三方的包。
上述程式碼主要有下面這幾個步驟:
- 載入JDBC驅動程式
- 建立資料庫的連線(需要使用者名稱密碼等資訊)
- 建立一個Statement
- 執行SQL語句(處理結果)
- 處理異常,關閉連線
上面的程式碼可以看出一些問題,比如模版程式碼過多,對於一個使用者來說,他可能只是想執行一條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的提供的資料庫連線的好處也是顯而易見的,很多資料庫連線的問題,它都幫我們管理解決了,如關閉連線等。