Springソース学習-JdbcTemplate batchUpdate一括操作
Spring JdbcTemplateのbatch操作は結局JDBCが提供する方法を利用して、Springはただ改造とパッケージングをしただけです
JDBCのbatch操作:
上記のコードを分析すると、実際のアプリケーションでは2つの部分だけが変わります.1つはsql文、2つは挿入するデータです.
Springの仕事は「変」と「不変」の部分を引き抜くことです
sql文はStringタイプのパラメータとして渡され、挿入データの書き込みはBatchPreparedStatementSetterインタフェースとして抽出されます.
BatchPreparedStatementSetterは、通常、匿名の内部クラスとして表示されます(テンプレートモード):
次は「不変」のセクションで、PreparedStatementをオンにしてbatch操作を実行します.
JdbcTemplateのbatchUpdateメソッド:
pssが見えますsetValues(ps,i)、ps.addBatch()ps.executeBatch()などの操作は、JDBCと同じです
また、一括操作がサポートされていない場合は、1つ1つ実行されると判断しました.
PreparedStatementCallback:匿名の内部クラスとして提供され、定義されたdoInPreparedStatementはexecuteメソッドでコールバックされます.
このステップでは、sqlをパラメータとして、PreparedStatementCreatorを使用してPreparedStatementを作成します.
SimplePreparedStatementCreatorのcreatePreparedStatementメソッド:
このexecuteメソッドは、主にPreparedStatementを作成し、PreparedStatementCallbackにコールバックするdoInPreparedStatementメソッドです.
なぜnativeJdbcExtractorを使用するのか、公式ドキュメントは次のとおりです.
Sometimes you need to access vendor specific JDBC methods that differ from the standard JDBC API. This can be problematic if you are running in an application server or with a DataSource that wraps the Connection, Statement and ResultSet objects with its own wrapper objects. To gain access to the native objects you can configure yourJdbcTemplate or OracleLobHandler with a NativeJdbcExtractor.
従って、主にオリジナルで標準的なConnectionを取得するために、Statement and ResultSet(包装後ではなく)
最後に、sql文と挿入対象データ(customers)の2つの変数を手がかりに考えてみましょう.
まず、sql文は、最後にPreparedStatementを作成します.
次に,データ書き込みの設定を1つのインタフェースとして抽出し,使用時に匿名の内部クラスを作成する,すなわちデータはBatchPreparedStatementSetterが保有する.
さらに、PreparedStatementCallbackはBatchPreparedStatementSetter(つまりデータを持っている)を持っています.では、PreparedStatementも必要です.
を使用してbatch操作を実行します.では、このPreparedStatementはどのように提供されますか?executeメソッドでコールバックするときに提供します
もう一つ質問があります.なぜSpring JdbcTemplateのbatchUpdateでconn.settAutoCommit(false)の操作が見られなかったのですか.
Springには独自のトランザクション管理メカニズムがあるからです
JDBCのトランザクション管理を構成している場合は、DataSourceTransactionManagerが自動的に設定します.
DataSourceTransactionManagerrのdoBeginメソッド:
JDBCのbatch操作:
String sql = "INSERT INTO CUSTOMER " +
"(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";
List<Customer> customers = getCustomersToInsert();
PreparedStatement pstmt = conn.prepareStatement(sql);
// auto-commit=true, statement transaction。 statement, false
conn.setAutoCommit(false);
for (Customer customer : customers) {
pstmt.setLong(1, customer.getCustId());
pstmt.setString(2, customer.getName());
pstmt.setInt(3, customer.getAge() );
pstmt.addBatch();
}
int[] count = stmt.executeBatch();
conn.commit();
上記のコードを分析すると、実際のアプリケーションでは2つの部分だけが変わります.1つはsql文、2つは挿入するデータです.
Springの仕事は「変」と「不変」の部分を引き抜くことです
sql文はStringタイプのパラメータとして渡され、挿入データの書き込みはBatchPreparedStatementSetterインタフェースとして抽出されます.
class MyBatchPreparedStatementSetter implements BatchPreparedStatementSetter{
private List<Customer> customers;
public MyBatchPreparedStatementSetter(List<Customer> customers) {
this.customers = customers;
}
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Customer customer = customers.get(i);
ps.setLong(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge() );
}
@Override
public int getBatchSize() {
return customers.size();
}
}
BatchPreparedStatementSetterは、通常、匿名の内部クラスとして表示されます(テンプレートモード):
String sql = ...;
List<Customer> customers = ...;
getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Customer customer = customers.get(i);
ps.setLong(1, customer.getCustId());
ps.setString(2, customer.getName());
ps.setInt(3, customer.getAge() );
}
@Override
public int getBatchSize() {
return customers.size();
}
});
次は「不変」のセクションで、PreparedStatementをオンにしてbatch操作を実行します.
JdbcTemplateのbatchUpdateメソッド:
public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException {
return execute(sql, new PreparedStatementCallback<int[]>() {
public int[] doInPreparedStatement(PreparedStatement ps) throws SQLException {
try {
int batchSize = pss.getBatchSize();
InterruptibleBatchPreparedStatementSetter ipss =
(pss instanceof InterruptibleBatchPreparedStatementSetter ?
(InterruptibleBatchPreparedStatementSetter) pss : null);
if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
for (int i = 0; i < batchSize; i++) {
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
ps.addBatch();
}
return ps.executeBatch();
}
else {
List<Integer> rowsAffected = new ArrayList<Integer>();
for (int i = 0; i < batchSize; i++) {
pss.setValues(ps, i);
if (ipss != null && ipss.isBatchExhausted(i)) {
break;
}
rowsAffected.add(ps.executeUpdate());
}
int[] rowsAffectedArray = new int[rowsAffected.size()];
for (int i = 0; i < rowsAffectedArray.length; i++) {
rowsAffectedArray[i] = rowsAffected.get(i);
}
return rowsAffectedArray;
}
}
finally {
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
pssが見えますsetValues(ps,i)、ps.addBatch()ps.executeBatch()などの操作は、JDBCと同じです
また、一括操作がサポートされていない場合は、1つ1つ実行されると判断しました.
PreparedStatementCallback:匿名の内部クラスとして提供され、定義されたdoInPreparedStatementはexecuteメソッドでコールバックされます.
public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException {
return execute(new SimplePreparedStatementCreator(sql), action);
}
このステップでは、sqlをパラメータとして、PreparedStatementCreatorを使用してPreparedStatementを作成します.
SimplePreparedStatementCreatorのcreatePreparedStatementメソッド:
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(this.sql);
}
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
throws DataAccessException {
Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
ps = psc.createPreparedStatement(conToUse);
applyStatementSettings(ps);
PreparedStatement psToUse = ps;
if (this.nativeJdbcExtractor != null) {
psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
}
T result = action.doInPreparedStatement(psToUse);
handleWarnings(ps);
return result;
}
//omitted
}
このexecuteメソッドは、主にPreparedStatementを作成し、PreparedStatementCallbackにコールバックするdoInPreparedStatementメソッドです.
Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
ps = psc.createPreparedStatement(con);
T result = action.doInPreparedStatement(ps);
return result;
}
なぜnativeJdbcExtractorを使用するのか、公式ドキュメントは次のとおりです.
Sometimes you need to access vendor specific JDBC methods that differ from the standard JDBC API. This can be problematic if you are running in an application server or with a DataSource that wraps the Connection, Statement and ResultSet objects with its own wrapper objects. To gain access to the native objects you can configure yourJdbcTemplate or OracleLobHandler with a NativeJdbcExtractor.
従って、主にオリジナルで標準的なConnectionを取得するために、Statement and ResultSet(包装後ではなく)
最後に、sql文と挿入対象データ(customers)の2つの変数を手がかりに考えてみましょう.
まず、sql文は、最後にPreparedStatementを作成します.
次に,データ書き込みの設定を1つのインタフェースとして抽出し,使用時に匿名の内部クラスを作成する,すなわちデータはBatchPreparedStatementSetterが保有する.
さらに、PreparedStatementCallbackはBatchPreparedStatementSetter(つまりデータを持っている)を持っています.では、PreparedStatementも必要です.
を使用してbatch操作を実行します.では、このPreparedStatementはどのように提供されますか?executeメソッドでコールバックするときに提供します
もう一つ質問があります.なぜSpring JdbcTemplateのbatchUpdateでconn.settAutoCommit(false)の操作が見られなかったのですか.
Springには独自のトランザクション管理メカニズムがあるからです
JDBCのトランザクション管理を構成している場合は、DataSourceTransactionManagerが自動的に設定します.
DataSourceTransactionManagerrのdoBeginメソッド:
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}