您好,登錄后才能下訂單哦!
本篇內容介紹了“JDBC批量操作方法是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
如果將多個調用批處理到同一條準備好的語句,則大多數JDBC驅動程序都會提高性能。通過將更新分組成批,可以限制到數據庫的往返次數。
通過實現特殊接口的兩個方法BatchPreparedStatementSetter并將該實現作為batchUpdate方法調用中的第二個參數傳入,可以完成JdbcTemplate批處理。你可以使用getBatchSize方法提供當前批處理的大小。你可以使用setValues方法設置語句的參數值。此方法稱為你在getBatchSize調用中指定的次數。以下示例根據列表中的條目更新t_actor表,并將整個列表用作批處理:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { return this.jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { Actor actor = actors.get(i); ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor.getId().longValue()); } public int getBatchSize() { return actors.size(); } }); } // ... additional methods }
如果處理更新流或從文件讀取,則可能具有首選的批處理大小,但最后一批可能沒有該數量的條目(譯者:意思是最后一批數據可能沒有分割數量大)。在這種情況下,可以使用InterruptibleBatchPreparedStatementSetter接口,該接口可在輸入源耗盡后中斷批處理(譯者:意思是數據源數據消耗完)。isBatchExhausted方法使你可以發出批處理結束的信號。
JdbcTemplate和NamedParameterJdbcTemplate都提供了另一種提供批處理更新的方式。無需實現特殊的批處理接口,而是將調用中的所有參數值作為列表提供。框架循環這些值,并使用一個內部語句setter。API會有所不同,具體取決于你是否使用命名參數。對于命名參數,你提供一個SqlParameterSource數組,該批處理的每個成員都有一個條目。你可以使用SqlParameterSourceUtils.createBatch便捷方法創建此數組,傳入一個bean樣式的對象數組(帶有與參數相對應的getter方法),字符串鍵Map實例(包含對應的參數作為值),或者混合使用。
以下示例顯示使用命名參數的批處理更新:
public class JdbcActorDao implements ActorDao { private NamedParameterTemplate namedParameterJdbcTemplate; public void setDataSource(DataSource dataSource) { this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource); } public int[] batchUpdate(List<Actor> actors) { return this.namedParameterJdbcTemplate.batchUpdate( "update t_actor set first_name = :firstName, last_name = :lastName where id = :id", SqlParameterSourceUtils.createBatch(actors)); } // ... additional methods }
對于使用經典的SQL語句?
占位符,則傳入包含更新值的對象數組的列表。該對象數組在SQL語句中的每個占位符必須具有一個條目,并且它們的順序必須與SQL語句中定義的順序相同。
以下示例與前面的示例相同,不同之處在于它使用經典的JDBC?
占位符:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[] batchUpdate(final List<Actor> actors) { List<Object[]> batch = new ArrayList<Object[]>(); for (Actor actor : actors) { Object[] values = new Object[] { actor.getFirstName(), actor.getLastName(), actor.getId()}; batch.add(values); } return this.jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", batch); } // ... additional methods }
我們前面介紹的所有批處理更新方法都返回一個int數組,其中包含每個批處理條目的受影響行數。此計數由JDBC驅動程序報告。如果該計數不可用,則JDBC驅動程序將返回值-2。
在這種情況下,通過在基礎PreparedStatement上自動設置值,需要從給定的Java類型派生每個值的對應JDBC類型。盡管這通常效果很好,但存在潛在的問題(例如,包含Map的空值)。在這種情況下,Spring默認情況下會調用ParameterMetaData.getParameterType,這對于JDBC驅動程序可能會很昂貴。如果遇到性能問題,則應使用最新的驅動程序版本,并考慮將spring.jdbc.getParameterType.ignore屬性設置為true(作為JVM系統屬性或在類路徑根目錄中的spring.properties文件中)。如關于Oracle 12c(SPR-16139)的報道。
或者,你可以考慮通過
BatchPreparedStatementSetter
(如前所示),通過為基于“List <Object []>
的調用提供的顯式類型數組,通過在服務器上的“registerSqlType
調用來顯式指定相應的JDBC類型。自定義“MapSqlParameterSource
實例,或者通過BeanPropertySqlParameterSource
實例從Java聲明的屬性類型中獲取SQL類型,即使對于null
值也是如此。
前面的批處理更新示例處理的批處理太大,以至于你想將它們分解成幾個較小的批處理。你可以通過多次調用batchUpdate方法來使用前面提到的方法來執行此操作,但是現在有一個更方便的方法。除了SQL語句外,此方法還包含一個對象集合,該對象包含參數,每個批處理要進行的更新次數以及一個ParameterizedPreparedStatementSetter來設置準備好的語句的參數值。框架遍歷提供的值,并將更新調用分成指定大小的批處理。
以下示例顯示了使用100的批量大小的批量更新:
public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } public int[][] batchUpdate(final Collection<Actor> actors) { int[][] updateCounts = jdbcTemplate.batchUpdate( "update t_actor set first_name = ?, last_name = ? where id = ?", actors, 100, (PreparedStatement ps, Actor actor) -> { ps.setString(1, actor.getFirstName()); ps.setString(2, actor.getLastName()); ps.setLong(3, actor.getId().longValue()); }); return updateCounts; } // ... additional methods }
此調用的批處理更新方法返回一個int數組,該數組包含每個批處理的數組條目以及每個更新受影響的行數的數組。頂層數組的長度指示運行的批處理數量,第二層樹脂的長度指示該批處理中的更新數量。 每個批次中的更新數量應該是為所有批次提供的批次大小(最后一個可能更少),這取決于所提供的更新對象的總數。每個更新語句的更新計數是JDBC驅動程序報告的更新計數。如果該計數不可用,則JDBC驅動程序將返回值-2。
SimpleJdbcInsert和SimpleJdbcCall類通過利用可通過JDBC驅動程序檢索的數據庫元數據來提供簡化的配置。這意味著你可以更少地進行前期配置,但是如果你愿意在代碼中提供所有詳細信息,則可以覆蓋或關閉元數據處理。
我們首先查看具有最少配置選項的SimpleJdbcInsert類。你應該在數據訪問層的初始化方法中實例化SimpleJdbcInsert。對于此示例,初始化方法是setDataSource方法。你不需要子類化SimpleJdbcInsert類。而是可以創建一個新實例,并使用withTableName方法設置表名稱。此類的配置方法遵循fluid
的樣式,該樣式返回SimpleJdbcInsert的實例,該實例使你可以鏈接所有配置方法。以下示例僅使用一種配置方法(我們稍后將顯示多種方法的示例):
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(3); parameters.put("id", actor.getId()); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); insertActor.execute(parameters); } // ... additional methods }
這里使用的execute方法將純java.util.Map作為其唯一參數。這里要注意的重要一點是,用于Map的鍵必須與數據庫中定義的表的列名匹配。這是因為我們讀取元數據來構造實際的insert語句。
下一個示例使用與前面的示例相同的插入,但是它沒有傳遞id,而是檢索自動生成的鍵并將其設置在新的Actor對象上。當創建SimpleJdbcInsert時,除了指定表名之外,它還使用usingGeneratedKeyColumns方法指定生成的鍵列的名稱。
以下清單顯示了它的工作方式:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
使用第二種方法運行插入時的主要區別在于,你沒有將ID添加到Map中,而是調用了executeAndReturnKey方法。這將返回一個java.lang.Number對象,你可以使用該對象創建領域類中使用的數值類型的實例。你不能依賴所有數據庫在這里返回特定的Java類。java.lang.Number是你能依賴的基礎類。如果你有多個自動生成的列,或者生成的值是非數字的,則可以使用從executeAndReturnKeyHolder方法返回的KeyHolder。
你可以使用usingColumns方法指定列名列表來限制插入的列,如以下示例所示:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingColumns("first_name", "last_name") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
插入的執行與依靠元數據確定要使用的列的執行相同。
使用Map提供參數值可以很好地工作,但這不是最方便使用的類。Spring提供了一些SqlParameterSource接口的實現,你可以使用它們來代替。第一個是BeanPropertySqlParameterSource,如果你有一個包含值的JavaBean兼容類,則這是一個非常方便的類。它使用相應的getter方法提取參數值。下面的示例演示如何使用BeanPropertySqlParameterSource:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
另一個選項是MapSqlParameterSource,它類似于Map,但提供了可以鏈式調用的更方便的addValue方法。以下示例顯示了如何使用它:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new MapSqlParameterSource() .addValue("first_name", actor.getFirstName()) .addValue("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
如你所見,配置是相同的。只有執行代碼才能更改為使用這些替代輸入類。
SimpleJdbcCall類使用數據庫中的元數據來查找in和out參數的名稱,因此你不必顯式聲明它們。如果愿意,可以聲明參數,也可以聲明沒有自動映射到Java類的參數(例如ARRAY或STRUCT)。第一個示例顯示了一個簡單的過程,該過程僅從MySQL數據庫返回VARCHAR和DATE格式的標量值。這個存儲過程示例讀取指定的參與者條目,并以out參數的形式返回first_name,last_name和birth_date列。以下清單顯示了第一個示例:
CREATE PROCEDURE read_actor ( IN in_id INTEGER, OUT out_first_name VARCHAR(100), OUT out_last_name VARCHAR(100), OUT out_birth_date DATE) BEGIN SELECT first_name, last_name, birth_date INTO out_first_name, out_last_name, out_birth_date FROM t_actor where id = in_id; END;
in_id參數包含你要查找的參與者的ID。out參數返回從表讀取的數據。
你可以采用類似于聲明SimpleJdbcInsert的方式聲明SimpleJdbcCall。你應該在數據訪問層的初始化方法中實例化并配置該類。與StoredProcedure類相比,你無需創建子類,也無需聲明可以在數據庫元數據中查找的參數。
下面的SimpleJdbcCall配置示例使用前面的存儲過程(除DataSource之外,唯一的配置選項是存儲過程的名稱):
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { this.procReadActor = new SimpleJdbcCall(dataSource) .withProcedureName("read_actor"); } public Actor readActor(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); Map out = procReadActor.execute(in); Actor actor = new Actor(); actor.setId(id); actor.setFirstName((String) out.get("out_first_name")); actor.setLastName((String) out.get("out_last_name")); actor.setBirthDate((Date) out.get("out_birth_date")); return actor; } // ... additional methods }
你為執行調用而編寫的代碼涉及創建一個包含IN參數的SqlParameterSource。你必須為輸入值提供的名稱與存儲過程中聲明的參數名稱的名稱匹配。大小寫不必匹配,因為你使用元數據來確定在存儲過程中應如何引用數據庫對象。源中為存儲過程指定的內容不一定是存儲過程在數據庫中存儲的方式。一些數據庫將名稱轉換為全部大寫,而另一些數據庫使用小寫或指定的大小寫。
execute方法采用IN參數,并返回一個Map,該Map包含由存儲過程中指定的名稱鍵入的所有out參數。在當前實例中,它們是out_first_name,out_last_name和out_birth_date。
execute方法的最后一部分創建一個Actor實例,以用于返回檢索到的數據。同樣,重要的是使用out參數的名稱,因為它們在存儲過程中已聲明。同樣,結果映射表中存儲的out參數名稱的大小寫與數據庫中out參數名稱的大小寫匹配,這在數據庫之間可能會有所不同。為了使代碼更具可移植性,你應該執行不區分大小寫的查找或指示Spring使用LinkedCaseInsensitiveMap。為此,你可以創建自己的JdbcTemplate并將setResultsMapCaseInsensitive屬性設置為true。然后,你可以將此自定義的JdbcTemplate實例傳遞到SimpleJdbcCall的構造函數中。以下示例顯示了此配置:
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor"); } // ... additional methods }
通過執行此操作,可以避免在用于返回參數名稱的情況下發生沖突。
在本章的前面,我們描述了如何從元數據推導出參數,但是如果需要,可以顯式聲明它們。你可以通過使用defineParameters方法創建和配置SimpleJdbcCall來實現,該方法將可變數量的SqlParameter對象作為輸入。有關如何定義SqlParameter的詳細信息,請參見下一部分。
如果你使用的數據庫不是Spring支持的數據庫,則必須進行顯式聲明。當前,Spring支持針對以下數據庫的存儲過程調用的元數據查找:Apache Derby,DB2,MySQL,Microsoft SQL Server,Oracle和Sybase。我們還支持MySQL,Microsoft SQL Server和Oracle存儲方法的元數據查找。
你可以選擇顯式聲明一個、一些或所有參數。在未顯式聲明參數的地方,仍使用參數元數據。要繞過對潛在參數的元數據查找的所有處理并僅使用已聲明的參數,可以將不帶ProcedureColumnMetaDataAccess的方法作為聲明的一部分來調用。假設你為數據庫函數聲明了兩個或多個不同的調用簽名。在這種情況下,你調用useInParameterNames來指定要包含在給定簽名中的IN參數名稱的列表。
下面的示例顯示一個完全聲明的過程調用,并使用前面示例中的信息:
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor") .withoutProcedureColumnMetaDataAccess() .useInParameterNames("in_id") .declareParameters( new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_last_name", Types.VARCHAR), new SqlOutParameter("out_birth_date", Types.DATE) ); } // ... additional methods }
兩個示例的執行和最終結果相同。第二個示例明確指定所有細節,而不是依賴于元數據。
要為SimpleJdbc類和RDBMS操作類(在Java對象作為JDBC操作模型中描述)定義參數,可以使用SqlParameter或其子類之一。為此,你通常在構造函數中指定參數名稱和SQL類型。通過使用java.sql.Types常量指定SQL類型。在本章的前面,我們看到了類似于以下內容的聲明:
new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR),
帶有SqlParameter的第一行聲明一個IN參數。通過使用SqlQuery及其子類(可以在理解SqlQuery中找到),可以將IN參數用于存儲過程調用和查詢。
第二行(帶有SqlOutParameter)聲明在存儲過程調用中使用的out參數。還有一個用于InOut參數的SqlInOutParameter(為過程提供IN值并返回值的參數)。
僅聲明為SqlParameter和SqlInOutParameter的參數用于提供輸入值。這不同于StoredProcedure類,該類(出于向后兼容的原因)允許為聲明為SqlOutParameter的參數提供輸入值。
對于IN參數,除了名稱和SQL類型,還可以為數字數據指定小數位,或者為自定義數據庫類型指定類型名。對于out參數,可以提供RowMapper來處理從REF游標返回的行的映射。另一個選擇是指定一個SqlReturnType,它提供了一個定義返回值的自定義處理的機會。
可以使用與調用存儲過程幾乎相同的方式來調用存儲函數,除了提供函數名而不是過程名。你將withFunctionName方法用作配置的一部分,以指示你要對函數進行調用,并生成函數調用的相應字符串。專門調用(executeFunction)用于運行該函數,它以指定類型的對象的形式返回函數的返回值,這意味著你不必從結果Map檢索返回值。對于只有一個out參數的存儲過程,也可以使用類似的便捷方法(名為executeObject)。以下示例(對于MySQL)基于一個名為get_actor_name的存儲函數,該函數返回參與者的全名:
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 }
所使用的executeFunction方法返回一個String,其中包含函數調用的返回值。
SimpleJdbcInsert和SimpleJdbcCall類通過利用可通過JDBC驅動程序檢索的數據庫元數據來提供簡化的配置。這意味著你可以更少地進行前期配置,但是如果你愿意在代碼中提供所有詳細信息,則可以覆蓋或關閉元數據處理。
SimpleJdbcInsert
插入數據
我們首先查看具有最少配置選項的SimpleJdbcInsert類。你應該在數據訪問層的初始化方法中實例化SimpleJdbcInsert。對于此示例,初始化方法是setDataSource方法。你不需要子類化SimpleJdbcInsert類。而是可以創建一個新實例,并使用withTableName方法設置表名稱。此類的配置方法遵循fluid
的樣式,該樣式返回SimpleJdbcInsert的實例,該實例使你可以鏈接所有配置方法。以下示例僅使用一種配置方法(我們稍后將顯示多種方法的示例):
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(3); parameters.put("id", actor.getId()); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); insertActor.execute(parameters); } // ... additional methods }
這里使用的execute方法將純java.util.Map作為其唯一參數。這里要注意的重要一點是,用于Map的鍵必須與數據庫中定義的表的列名匹配。這是因為我們讀取元數據來構造實際的insert語句。
SimpleJdbcInsert
檢索自動生成主鍵
下一個示例使用與前面的示例相同的插入,但是它沒有傳遞id,而是檢索自動生成的鍵并將其設置在新的Actor對象上。當創建SimpleJdbcInsert時,除了指定表名之外,它還使用usingGeneratedKeyColumns方法指定生成的鍵列的名稱。以下清單顯示了它的工作方式:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
使用第二種方法運行插入時的主要區別在于,你沒有將ID添加到Map中,而是調用了executeAndReturnKey方法。這將返回一個java.lang.Number對象,你可以使用該對象創建域類中使用的數字類型的實例。你不能依賴所有數據庫在這里返回特定的Java類。你可以依賴這個基本的java.lang.Number類型。如果你有多個自動生成的列,或者生成的值是非數字的,則可以使用從executeAndReturnKeyHolder方法返回的KeyHolder。
SimpleJdbcInsert
指定列
你可以使用usingColumns方法指定列名列表來限制插入的列,如以下示例所示:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingColumns("first_name", "last_name") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { Map<String, Object> parameters = new HashMap<String, Object>(2); parameters.put("first_name", actor.getFirstName()); parameters.put("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
插入的執行與依靠元數據確定要使用的列的執行相同。
SqlParameterSource
提供參數值
使用Map提供參數值可以很好地工作,但這不是最方便使用的類。Spring提供了一些SqlParameterSource接口的實現,你可以使用它們來代替。第一個是BeanPropertySqlParameterSource,如果你有一個包含值的JavaBean兼容類,則這是一個非常方便的類。它使用相應的getter方法提取參數值。下面的示例演示如何使用BeanPropertySqlParameterSource:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
另一個選項是MapSqlParameterSource,它類似于Map,但提供了可以鏈式調用的更方便的addValue方法。以下示例顯示了如何使用它:
public class JdbcActorDao implements ActorDao { private SimpleJdbcInsert insertActor; public void setDataSource(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("t_actor") .usingGeneratedKeyColumns("id"); } public void add(Actor actor) { SqlParameterSource parameters = new MapSqlParameterSource() .addValue("first_name", actor.getFirstName()) .addValue("last_name", actor.getLastName()); Number newId = insertActor.executeAndReturnKey(parameters); actor.setId(newId.longValue()); } // ... additional methods }
如你所見,配置是相同的。只有執行代碼才能更改為使用這些替代輸入類。
SimpleJdbcCall
調用存儲過程
SimpleJdbcCall類使用數據庫中的元數據來查找in和out參數的名稱,因此你不必顯式聲明它們。如果愿意,可以聲明參數,也可以聲明沒有自動映射到Java類的參數(例如ARRAY或STRUCT)。第一個示例顯示了一個簡單的過程,該過程僅從MySQL數據庫返回VARCHAR和DATE格式的標量值。示例存儲過程讀取指定的actor條目,并以out參數的形式返回first_name,last_name和birth_date列。以下清單顯示了第一個示例:
CREATE PROCEDURE read_actor ( IN in_id INTEGER, OUT out_first_name VARCHAR(100), OUT out_last_name VARCHAR(100), OUT out_birth_date DATE) BEGIN SELECT first_name, last_name, birth_date INTO out_first_name, out_last_name, out_birth_date FROM t_actor where id = in_id; END;
in_id參數包含您要查找的actor的ID。out參數返回從表讀取的數據。
你可以采用類似于聲明SimpleJdbcInsert的方式聲明SimpleJdbcCall。你應該在數據訪問層的初始化方法中實例化并配置該類。與StoredProcedure類相比,你無需創建子類,也無需聲明可以在數據庫元數據中查找的參數。下面的SimpleJdbcCall配置示例使用前面的存儲過程(除DataSource之外,唯一的配置選項是存儲過程的名稱):
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { this.procReadActor = new SimpleJdbcCall(dataSource) .withProcedureName("read_actor"); } public Actor readActor(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); Map out = procReadActor.execute(in); Actor actor = new Actor(); actor.setId(id); actor.setFirstName((String) out.get("out_first_name")); actor.setLastName((String) out.get("out_last_name")); actor.setBirthDate((Date) out.get("out_birth_date")); return actor; } // ... additional methods }
你為執行調用而編寫的代碼涉及創建一個包含IN參數的SqlParameterSource。你必須為輸入值提供的名稱與存儲過程中聲明的參數名稱的名稱匹配。大小寫不必匹配,因為你使用元數據來確定在存儲過程中應如何引用數據庫對象。源中為存儲過程指定的內容不一定是存儲過程在數據庫中存儲的方式。一些數據庫將名稱轉換為全部大寫,而另一些數據庫使用小寫或指定的大小寫。
execute方法采用IN參數,并返回一個Map,該Map包含由存儲過程中指定的名稱鍵入的所有out參數。在當前實例中,它們是out_first_name,out_last_name和out_birth_date。
execute方法的最后一部分創建一個Actor實例,以用于返回檢索到的數據。同樣,重要的是使用out參數的名稱,因為它們在存儲過程中已聲明。同樣,結果映射表中存儲的out參數名稱的大小寫與數據庫中out參數名稱的大小寫匹配,這在數據庫之間可能會有所不同。為了使代碼更具可移植性,你應該執行不區分大小寫的查找或指示Spring使用LinkedCaseInsensitiveMap。為此,你可以創建自己的JdbcTemplate并將setResultsMapCaseInsensitive屬性設置為true。然后,你可以將此自定義的JdbcTemplate實例傳遞到SimpleJdbcCall的構造函數中。以下示例顯示了此配置:
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor"); } // ... additional methods }
通過執行此操作,可以避免在用于返回參數名稱的情況下發生沖突。
在本章的前面,我們描述了如何從元數據推導出參數,但是如果需要,可以顯式聲明它們。你可以通過使用defineParameters方法創建和配置SimpleJdbcCall來實現,該方法將可變數量的SqlParameter對象作為輸入。有關如何定義SqlParameter的詳細信息,請參見下一部分。
如果你使用的數據庫不是Spring支持的數據庫,則必須進行顯式聲明。當前,Spring支持針對以下數據庫的存儲過程調用的元數據查找:Apache Derby,DB2,MySQL,Microsoft SQL Server,Oracle和Sybase。我們還支持MySQL,Microsoft SQL Server和Oracle存儲功能的元數據查找。
你可以選擇顯式聲明一、一些或所有參數。在未顯式聲明參數的地方,仍使用參數元數據。要繞過對潛在參數的元數據查找的所有處理并僅使用已聲明的參數,可以將不帶ProcedureColumnMetaDataAccess的方法作為聲明的一部分來調用。假設你為數據庫函數聲明了兩個或多個不同的調用簽名。在這種情況下,你調用useInParameterNames來指定要包含在給定簽名中的IN參數名稱的列表。
下面的示例顯示一個完全聲明的過程調用,并使用前面示例中的信息:
public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadActor; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadActor = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_actor") .withoutProcedureColumnMetaDataAccess() .useInParameterNames("in_id") .declareParameters( new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR), new SqlOutParameter("out_last_name", Types.VARCHAR), new SqlOutParameter("out_birth_date", Types.DATE) ); } // ... additional methods }
兩個示例的執行和最終結果相同。第二個示例明確指定所有細節,而不是依賴于元數據。
SqlParameters
要為SimpleJdbc類和RDBMS操作類(在JDBC操作建模為Java對象中發現)定義參數,可以使用SqlParameter或其子類之一。為此,通常在構造函數中指定參數名稱和SQL類型。通過使用java.sql.Types常量指定SQL類型。在本章的前面,我們看到了類似于以下內容的聲明:
new SqlParameter("in_id", Types.NUMERIC), new SqlOutParameter("out_first_name", Types.VARCHAR),
帶有SqlParameter的第一行聲明一個IN參數。通過使用SqlQuery及其子類(可以在理解SqlQuery中找到),可以將IN參數用于存儲過程調用和查詢。
第二行(帶有SqlOutParameter)聲明在存儲過程調用中使用的out參數。還有一個用于InOut參數的SqlInOutParameter(為過程提供IN值并返回值的參數)。
僅聲明為SqlParameter和SqlInOutParameter的參數用于提供輸入值。這不同于StoredProcedure類,該類(出于向后兼容的原因)允許為聲明為SqlOutParameter的參數提供輸入值。
對于IN參數,除了名稱和SQL類型,還可以為數字數據指定小數位,或者為自定義數據庫類型指定類型名。對于out參數,可以提供RowMapper來處理從REF游標返回的行的映射。另一個選擇是指定一個SqlReturnType,它提供了一個定義返回值的自定義處理的機會。
可以使用與調用存儲過程幾乎相同的方式來調用存儲函數,除了提供函數名而不是存儲過程名。你將withFunctionName方法用作配置的一部分,以指示你要對函數進行調用,并生成函數調用的相應字符串。專門調用(executeFunction)用于運行該函數,它以指定類型的對象的形式返回函數的返回值,這意味著你不必從結果Map中檢索返回值。對于只有一個out參數的存儲過程,也可以使用類似的便捷方法(名為executeObject)。以下示例(對于MySQL)基于一個名為get_actor_name的存儲函數,該函數返回actor的全名:
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 }
所使用的executeFunction方法返回一個String,其中包含函數調用的返回值。
調用返回結果集的存儲過程或函數有點棘手。一些數據庫在JDBC結果處理期間返回結果集,而另一些數據庫則需要顯式注冊的特定類型的參數。兩種方法都需要進行額外的處理才能遍歷結果集并處理返回的行。通過SimpleJdbcCall,可以使用returningResultSet方法并聲明要用于特定參數的RowMapper實現。如果在結果存儲過程中返回了結果集,沒有定義名稱,因此返回的結果必須與聲明RowMapper實現的順序匹配。指定的名稱仍用于將處理后的結果列表存儲在由execute語句返回的結果Map中。
下一個示例(對于MySQL)使用存儲過程,該存儲過程不使用IN參數,并返回t_actor表中的所有行:
CREATE PROCEDURE read_all_actors() BEGIN SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END;
要調用此存儲過程,可以聲明RowMapper。因為要映射到的類遵循JavaBean規則,所以可以使用BeanPropertyRowMapper,該類是通過在newInstance方法中傳入要映射的必需類而創建的。以下示例顯示了如何執行此操作:
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 }
execute調用傳遞一個空的Map,因為此調用不帶任何參數。然后從結果Map中檢索actor列表,并將其返回給調用者。
“JDBC批量操作方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。