ref:https://blog.csdn.net/u011054333/article/details/54772491html
先來看看一個JDBC的例子。咱們能夠看到爲了執行一條SQL語句,咱們須要建立鏈接,建立語句對象,而後執行SQL,而後操縱結果集獲取數據。java
try(Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD)){ List<User> users = new ArrayList<>(); try (Statement statement = connection.createStatement()) { try (ResultSet rs = statement.executeQuery("SELECT *FROM user")) { while (rs.next()) { User user = new User(); user.setId(rs.getInt(1)); user.setUsername(rs.getString(2)); user.setPassword(rs.getString(3)); user.setNickname(rs.getString(4)); user.setBirthday(rs.getDate(5)); users.add(user); } } } }
其實這些步驟中有不少步驟都是固定的,Spring JDBC框架將這些操做封裝起來, 咱們只須要關注業務邏輯點便可。在Spring JDBC框架中,咱們要作的事情以下:mysql
Spring會幫咱們完成如下事情:程序員
JdbcTemplate是Jdbc框架最重要的類,提供了較爲底層的Jdbc操做。其它幾個類都是在JdbcTemplate基礎上封裝了相關功能。spring
要在Gradle項目中使用Spring JDBC框架,添加以下一段。因爲Spring JDBC的主要類JdbcTemlate須要一個數據源用來初始化,因此還須要一個數據源的實現。sql
compile group: 'org.springframework', name: 'spring-jdbc', version: '4.3.5.RELEASE' compile group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.1.1'
若是要使用Spring框架的其餘功能,可能還須要添加對應的依賴。數據庫
首先須要建立一個數據源Bean。爲了將配置分離,咱們先新建一個jdbc.properties
文件。apache
jdbc.url=jdbc:mysql://localhost:3306/test jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=12345678
而後建立一個Spring配置文件jdbc.xml
。這裏用到了<context:property-placeholder>
節點來導入其它配置文件。而後用這些屬性建立一個數據源Bean,而後再利用數據源Bean來建立一個JdbcTemplate。數組
<?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="dataSource" class="org.apache.commons.dbcp2.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> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <context:property-placeholder location="jdbc.properties"/> </beans>
註冊了JdbcTemplate以後,就能夠將它注入到任何地方來使用了。首先它可使用execute方法,執行任何SQL語句。這裏建立了一個簡單的MySQL用戶表,只有主鍵和用戶名。安全
jdbcTemplate.execute("CREATE TABLE user(id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) UNIQUE)");
它還可使用update方法執行增長、更新和刪除操做。
jdbcTemplate.update("INSERT INTO user(name) VALUES(?)", "yitian"); jdbcTemplate.update("INSERT INTO user(name) VALUES(?)", "zhang2"); jdbcTemplate.update("UPDATE user SET name=? WHERE name=?", "zhang3", "zhang2"); jdbcTemplate.update("DELETE FROM user WHERE name=?", "zhang3");
查詢操做也很簡單,使用queryForObject方法,傳入SQL字符串和結果類型便可。
int count = jdbcTemplate.queryForObject("SELECT count(*) FROM user", Integer.class); System.out.println("記錄數目是:" + count); String name = jdbcTemplate.queryForObject("SELECT name FROM user WHERE id=1", String.class); System.out.println("姓名是:" + name);
若是要查詢整條記錄也能夠。Spring提供了一個接口RowMapper,只須要實現該接口的mapRow方法,便可將結果集的一條記錄轉化爲一個Java對象,該方法的第二個參數是當前行的行數。下面是一個RowMapper實現。
public class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
User實體類對應於上面建立表時簡單的用戶表(其餘方法已省略)。
public class User { private int id; private String name; }
實現了RowMapper接口以後,咱們就能夠查詢一條記錄並轉化爲Java對象了。
User user = jdbcTemplate.queryForObject("SELECT id,name FROM user WHERE id=?", new UserRowMapper(), 1); System.out.println(user);
查詢多條記錄也能夠,這時候須要使用query方法。
List<User> users = jdbcTemplate.query("SELECT id,name FROM usr", new UserRowMapper()); System.out.println(users);
還有一個通用方法queryForList,返回一個List,每個元素都是一個Map,在Map中存放着列名和值組成的鍵值對。
List<Map<String, Object>> results = jdbcTemplate.queryForList("SELECT id,name FROM user"); System.out.println(results);
前面的JdbcTemplate提供了很是方便的JDBC操做封裝,可是在綁定參數的時候只能採用通配符?
方式以順序方式綁定參數。若是SQL語句比較複雜,參數比較多,那麼這種方式顯得不太方便。所以Spring提供了一個更加方便的類NamedParameterJdbcTemplate,它能夠以命名方式綁定SQL語句參數。NamedParameterJdbcTemplate在內部使用一個JdbcTemplate,你也能夠調用getJdbcOperations方法獲取底層的JdbcTemplate對象,而後用前面的方法進行基本操做。
建立NamedParameterJdbcTemplate和JdbcTemplate相同,只須要傳入一個數據源便可。
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean>
NamedParameterJdbcTemplate和JdbcTemplate的大部分操做相同,這裏僅介紹綁定命名參數的部分。首先,SQL語句必須使用:參數名稱
的形式做爲參數。而後,咱們建立一個MapSqlParameterSource對象,它的內部使用了一個Map保存的命名參數的名稱和值。而後咱們使用它的addValue方法傳遞須要的命名參數的名稱和值,這個方法還能夠接受第三個參數指定參數類型,這個類型以java.sql.Types
的一些公共字段的形式給出。最後,將MapSqlParameterSource傳遞給相應的方法執行便可。
String sql = "SELECT id,name FROM user WHERE name=:name AND id<:user_id"; MapSqlParameterSource namedParameters = new MapSqlParameterSource(); namedParameters.addValue("name", "test"); namedParameters.addValue("user_id", 100, Types.INTEGER); User user = namedParameterJdbcTemplate.queryForObject(sql, namedParameters, new UserRowMapper()); System.out.println(user);
若是不想建立MapSqlParameterSource對象,還能夠直接使用一個Map傳遞命名參數的名稱和值。
Map<String, Object> map = new HashMap<>(); map.put("user_id", 100); map.put("name", "test"); List<User> users = namedParameterJdbcTemplate.query(sql, map, new UserRowMapper()); System.out.println(users);
上面討論的MapSqlParameterSource
實際上實現了SqlParameterSource
接口,上面的幾個方法簽名也是接受SqlParameterSource
接口。這個接口表示用來傳遞命名參數和值的集合。除了MapSqlParameterSource
以外,還有另一個經常使用的實現,BeanPropertySqlParameterSource
,這個類接受一個Java Bean對象,而後使用Bean的屬性名和值做爲命名參數的名稱和值。這一點須要注意。
User bean = new User(100, "test"); SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(bean); users = namedParameterJdbcTemplate.query(sql, parameterSource, new UserRowMapper()); System.out.println(users);
前面所說的JdbcTemplate封裝了一些功能,讓咱們方便的使用JDBC。Spring還提供了幾個更高級、功能更具體的SimpleJdbc類。這些類會讀取JDBC的元數據Metadata,使用起來更加方便。
SimpleJdbcInsert類用來插入數據。簡單的使用方法以下。SimpleJdbcInsert須要一個數據源來建立,withTableName方法指定要插入的表名,usingGeneratedKeyColumns指定設置了主鍵自增的列名。其餘使用方法和前面所說的類相似。executeAndReturnKey這個方法很特別,它會將數據插入數據庫並返回該條記錄對應的自增鍵。有時候咱們可能但願使用自增主鍵來插入一條數據,因爲主鍵是數據庫自動生成的,咱們必須再次查詢數據庫才能得到主鍵。這種狀況下使用executeAndReturnKey很是方便。注意這個方法返回的是java.lang.Number
類型,能夠調用其XXXvalue
方法轉換成各類數值。
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource) .withTableName("user") .usingGeneratedKeyColumns("id"); User user = new User(); user.setName("test"); Map<String, Object> params = new HashMap<>(); params.put("names", user.getName()); int id = simpleJdbcInsert.executeAndReturnKey(params).intValue(); System.out.println("simpleJdbcInsert" + user);
SimpleJdbcCall類用來調用存儲過程的。使用方法相似。這裏就直接給出Spring官方文檔的示例代碼了。
MySQL存儲過程。 CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200) READS SQL DATA BEGIN DECLARE out_name VARCHAR(200); SELECT concat(first_name, ' ', last_name) INTO out_name FROM t_actor where id = in_id; RETURN out_name; END; SimpleJdbcCall調用存儲過程。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall funcGetActorName; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) .withFunctionName("get_actor_name"); } public String getActorName(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); String name = funcGetActorName.executeFunction(String.class, in); return name; } // ... additional methods } 若是要從存儲過程獲取記錄的話,能夠這樣。如下是一個MySQL存儲過程。 CREATE PROCEDURE read_all_actors() BEGIN SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END; 相對應的Java代碼。 public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadAllActors; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_all_actors") .returningResultSet("actors", BeanPropertyRowMapper.newInstance(Actor.class)); } public List getActorsList() { Map m = procReadAllActors.execute(new HashMap<String, Object>(0)); return (List) m.get("actors"); } // ... additional methods }
org.springframework.jdbc.object
包下提供了一組類,讓咱們用更加面向對象的方式來操做數據庫。咱們能夠將SQL查詢轉化爲一組業務對象,也能夠方便的進行查詢、更新和執行存儲過程的操做。
MappingSqlQuery是一個抽象類,繼承自SQLQuery。咱們在使用這個類的時候須要建立一個自定義類,繼承自MappingSqlQuery,而後在其構造方法中初始化一個查詢字符串,並在這裏設置查詢參數;而後須要實現該類的mapRow方法,將結果集的行轉化爲實體類對象。下面是一個例子。構造方法中定義的查詢字符串會被建立爲PreparedStatement,所以能夠在查詢字符串中使用佔位符?
。對於每一個出現的佔位符,咱們都必須調用declareParameter方法聲明參數,該方法接受一個SqlParameter對象,該對象須要參數名和類型兩個參數。最後須要調用compile方法編譯和準備查詢。該類是線程安全的,所以能夠安全的在多個線程之間共享對象。
public class UserMappingSqlQuery extends MappingSqlQuery<User> { public UserMappingSqlQuery(DataSource ds) { super(ds, "SELECT id,name FROM user WHERE id=:id"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
而後咱們建立一個對象,調用findObject方法並傳入查詢參數,便可得到結果對象。
@Test public void testMappingSqlQuery() { MappingSqlQuery<User> mappingSqlQuery = new UserMappingSqlQuery(dataSource); User user = mappingSqlQuery.findObject(1); logger.debug(user); }
若是查詢要返回一組記錄並傳遞多個查詢參數。須要調用相應的execute方法。一下是另外一個MappingSqlQuery,以及其測試代碼。
public class UsersMappingSqlQuery extends MappingSqlQuery<User> { public UsersMappingSqlQuery(DataSource ds) { super(ds, "SELECT id,name FROM user WHERE id<? AND name LIKE ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); super.declareParameter(new SqlParameter("name", Types.VARCHAR)); compile(); } @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
//獲取多個對象 mappingSqlQuery = new UsersMappingSqlQuery(dataSource); List<User> users = mappingSqlQuery.execute(100, "test"); logger.debug(users);
這個類的使用方法和SqlQuery相似,可是因爲它是一個具體類,所以不須要定義子類便可使用。下面是它的簡單使用方法。爲了更新具體的數據(例如一個Java Bean對象),你也能夠繼承該類,並提供本身的更新方法,就和上面同樣。
@Test public void testSqlUpdate() { SqlUpdate sqlUpdate = new SqlUpdate(dataSource, "INSERT INTO user(name) VALUES(?)"); sqlUpdate.declareParameter(new SqlParameter("name", Types.VARCHAR)); sqlUpdate.compile(); sqlUpdate.update("wang5"); List<User> users = jdbcTemplate.query("SELECT id,name FROM user", new UserRowMapper()); logger.debug(users); }
StoredProcedure是關係數據庫中存儲過程概念的抽象類,提供了一組方便的受保護方法。所以在使用該類的時候須要咱們建立一個子類,繼承該類。在使用這個類的時候咱們須要使用setSql方法設置數據庫中存儲過程的名稱。在傳遞參數的時候,使用SqlParameter傳遞IN參數,使用SqlOutParameter傳遞OUT參數,使用SqlInOutParameter傳遞INOUT參數。
如下是Spring官方文檔的一個例子。
class GetSysdateProcedure extends StoredProcedure {
private static final String SQL = "sysdate"; public GetSysdateProcedure(DataSource dataSource) { setDataSource(dataSource); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Date execute() { // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... Map<String, Object> results = execute(new HashMap<String, Object>()); Date sysdate = (Date) results.get("date"); return sysdate; } }
通常狀況下Spring能夠自行決定SQL參數的類型,可是有時候或者說最好由咱們提供準確的SQL參數信息。
JdbcTemplate
的不少查詢和更新方法包含一個額外的參數,一個int數組,該數組應該是java.sql.Types
指定的一些常量,代表SQL參數的類型。SqlParameterValue
來設置參數的值,在建立該對象的時候提供參數的值和類型。SqlParameterSource
類(BeanPropertySqlParameterSource
或MapSqlParameterSource
)來指定命名參數和其類型。咱們在學習JDBC的時候,基本上都是從DriverManager類建立一個數據庫鏈接。在實際環境中,咱們應該使用數據源(DataSource)來建立數據庫鏈接。數據源將建立數據庫的職責和應用代碼分離,數據源能夠交給數據庫管理員來設置,程序員只須要獲取數據源對象,而後開發相關代碼。
在上面的例子中咱們使用的是Apache的commons-dbcp2
數據源,Spring本身也實現了幾個數據源方便咱們開發和測試。
DriverManagerDataSource
是一個簡單的數據源,每次請求都會返回一個新的數據庫鏈接。它使用數據庫驅動來建立數據源,就像咱們使用DriverManager
那樣。這是一個簡單的測試類,能夠幫助咱們在不借助任何Java EE容器的狀況下獲取數據源。可是因爲使用commons-dbcp2
這樣的成熟數據源也很容易,因此其實咱們只須要使用commons-dbcp2
便可。
SingleConnectionDataSource
也是一個數據源,它包裝了一個單獨的數據庫鏈接,在每次請求都會返回同一個數據庫鏈接對象。和DriverManagerDataSource
相比它更輕量,由於沒有建立額外數據庫鏈接的開銷。
在建立數據源的時候咱們能夠在Spring配置文件中設置數據源的初始化腳本。
<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/> </jdbc:initialize-database>
有時候咱們但願在初始化數據的時候刪除上一次的測試數據。可是若是數據庫不支持相似DROP TABLE IF EXISTS
這樣的語法,那麼咱們就必須在初始化腳本中添加一些DROP
語句。這些刪除語句可能會失敗(若是沒有測試數據的狀況下執行刪除),這時候就能夠忽略刪除失敗。當初始化腳本出現錯誤的時候就會拋出異常,可是若是設置了忽略刪除失敗,Spring就會直接忽略這些失敗而不拋出異常。ignore-failures屬性還能夠取另外兩個值NONE
和ALL
,分別表示不忽略失敗和忽略全部失敗。
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS"> <jdbc:script location="..."/> </jdbc:initialize-database>
咱們還能夠設置初始化腳本的間隔符。
<jdbc:initialize-database data-source="dataSource" separator="@@"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql" separator=";"/> <jdbc:script location="classpath:com/foo/sql/db-test-data-1.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data-2.sql"/> </jdbc:initialize-database>
org.springframework.jdbc.datasource.DataSourceUtils
,這是一個方便的工具類,包含了一組和數據源相關的工具方法。
org.springframework.jdbc.support.JdbcUtils
類提供了一些方法來操做JDBC,在Spring內部使用,也能夠用於本身的JDBC操做。
還有幾個工具類主要由Spring內部使用,這裏就不列舉了。
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc
項目代碼在Csdn代碼庫,有興趣的同窗能夠看看。