embulk-output-jdbc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/build.gradle +2 -0
- data/classpath/embulk-output-jdbc-0.1.0.jar +0 -0
- data/lib/embulk/output/jdbc.rb +3 -0
- data/src/main/java/org/embulk/output/JdbcOutputPlugin.java +104 -0
- data/src/main/java/org/embulk/output/jdbc/AbstractJdbcOutputPlugin.java +701 -0
- data/src/main/java/org/embulk/output/jdbc/BatchInsert.java +54 -0
- data/src/main/java/org/embulk/output/jdbc/JdbcColumn.java +71 -0
- data/src/main/java/org/embulk/output/jdbc/JdbcOutputConnection.java +423 -0
- data/src/main/java/org/embulk/output/jdbc/JdbcOutputConnector.java +8 -0
- data/src/main/java/org/embulk/output/jdbc/JdbcSchema.java +37 -0
- data/src/main/java/org/embulk/output/jdbc/JdbcUtils.java +155 -0
- data/src/main/java/org/embulk/output/jdbc/RetryExecutor.java +105 -0
- data/src/main/java/org/embulk/output/jdbc/StandardBatchInsert.java +180 -0
- data/src/main/java/org/embulk/output/jdbc/setter/BooleanColumnSetter.java +52 -0
- data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetter.java +121 -0
- data/src/main/java/org/embulk/output/jdbc/setter/ColumnSetterFactory.java +137 -0
- data/src/main/java/org/embulk/output/jdbc/setter/DoubleColumnSetter.java +51 -0
- data/src/main/java/org/embulk/output/jdbc/setter/LongColumnSetter.java +62 -0
- data/src/main/java/org/embulk/output/jdbc/setter/NullColumnSetter.java +43 -0
- data/src/main/java/org/embulk/output/jdbc/setter/SkipColumnSetter.java +35 -0
- data/src/main/java/org/embulk/output/jdbc/setter/SqlTimestampColumnSetter.java +48 -0
- data/src/main/java/org/embulk/output/jdbc/setter/StringColumnSetter.java +48 -0
- data/src/test/java/org/embulk/output/TestJdbcOutputPlugin.java +5 -0
- metadata +67 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
package org.embulk.output.jdbc;
|
2
|
+
|
3
|
+
import java.math.BigDecimal;
|
4
|
+
import java.io.IOException;
|
5
|
+
import java.sql.SQLException;
|
6
|
+
import java.sql.PreparedStatement;
|
7
|
+
import java.sql.Date;
|
8
|
+
import java.sql.Time;
|
9
|
+
import java.sql.Timestamp;
|
10
|
+
|
11
|
+
public interface BatchInsert
|
12
|
+
{
|
13
|
+
public void prepare(String loadTable, JdbcSchema insertSchema) throws SQLException;
|
14
|
+
|
15
|
+
public int getBatchWeight();
|
16
|
+
|
17
|
+
public void add() throws IOException, SQLException;
|
18
|
+
|
19
|
+
public void close() throws IOException, SQLException;
|
20
|
+
|
21
|
+
public void flush() throws IOException, SQLException;
|
22
|
+
|
23
|
+
public void finish() throws IOException, SQLException;
|
24
|
+
|
25
|
+
public void setNull(int sqlType) throws IOException, SQLException;
|
26
|
+
|
27
|
+
public void setBoolean(boolean v) throws IOException, SQLException;
|
28
|
+
|
29
|
+
public void setByte(byte v) throws IOException, SQLException;
|
30
|
+
|
31
|
+
public void setShort(short v) throws IOException, SQLException;
|
32
|
+
|
33
|
+
public void setInt(int v) throws IOException, SQLException;
|
34
|
+
|
35
|
+
public void setLong(long v) throws IOException, SQLException;
|
36
|
+
|
37
|
+
public void setFloat(float v) throws IOException, SQLException;
|
38
|
+
|
39
|
+
public void setDouble(double v) throws IOException, SQLException;
|
40
|
+
|
41
|
+
public void setBigDecimal(BigDecimal v) throws IOException, SQLException;
|
42
|
+
|
43
|
+
public void setString(String v) throws IOException, SQLException;
|
44
|
+
|
45
|
+
public void setNString(String v) throws IOException, SQLException;
|
46
|
+
|
47
|
+
public void setBytes(byte[] v) throws IOException, SQLException;
|
48
|
+
|
49
|
+
public void setSqlDate(Date v, int sqlType) throws IOException, SQLException;
|
50
|
+
|
51
|
+
public void setSqlTime(Time v, int sqlType) throws IOException, SQLException;
|
52
|
+
|
53
|
+
public void setSqlTimestamp(Timestamp v, int sqlType) throws IOException, SQLException;
|
54
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
package org.embulk.output.jdbc;
|
2
|
+
|
3
|
+
import com.fasterxml.jackson.annotation.JsonCreator;
|
4
|
+
import com.fasterxml.jackson.annotation.JsonProperty;
|
5
|
+
import com.fasterxml.jackson.annotation.JsonIgnore;
|
6
|
+
|
7
|
+
public class JdbcColumn
|
8
|
+
{
|
9
|
+
private String name;
|
10
|
+
private String typeName;
|
11
|
+
private int sqlType;
|
12
|
+
private int sizeTypeParameter;
|
13
|
+
private int scaleTypeParameter;
|
14
|
+
|
15
|
+
@JsonCreator
|
16
|
+
public JdbcColumn(
|
17
|
+
@JsonProperty("name") String name,
|
18
|
+
@JsonProperty("typeName") String typeName,
|
19
|
+
@JsonProperty("sqlType") int sqlType,
|
20
|
+
@JsonProperty("sizeTypeParameter") int sizeTypeParameter,
|
21
|
+
@JsonProperty("scaleTypeParameter") int scaleTypeParameter)
|
22
|
+
{
|
23
|
+
this.name = name;
|
24
|
+
this.typeName = typeName;
|
25
|
+
this.sqlType = sqlType;
|
26
|
+
this.sizeTypeParameter = sizeTypeParameter;
|
27
|
+
this.scaleTypeParameter = scaleTypeParameter;
|
28
|
+
}
|
29
|
+
|
30
|
+
@JsonIgnore
|
31
|
+
public static JdbcColumn skipColumn()
|
32
|
+
{
|
33
|
+
return new JdbcColumn(null, null, 0, 0, 0);
|
34
|
+
}
|
35
|
+
|
36
|
+
@JsonIgnore
|
37
|
+
public boolean isSkipColumn()
|
38
|
+
{
|
39
|
+
return name == null;
|
40
|
+
}
|
41
|
+
|
42
|
+
@JsonProperty("name")
|
43
|
+
public String getName()
|
44
|
+
{
|
45
|
+
return name;
|
46
|
+
}
|
47
|
+
|
48
|
+
@JsonProperty("typeName")
|
49
|
+
public String getTypeName()
|
50
|
+
{
|
51
|
+
return typeName;
|
52
|
+
}
|
53
|
+
|
54
|
+
@JsonProperty("sqlType")
|
55
|
+
public int getSqlType()
|
56
|
+
{
|
57
|
+
return sqlType;
|
58
|
+
}
|
59
|
+
|
60
|
+
@JsonProperty("sizeTypeParameter")
|
61
|
+
public int getSizeTypeParameter()
|
62
|
+
{
|
63
|
+
return sizeTypeParameter;
|
64
|
+
}
|
65
|
+
|
66
|
+
@JsonProperty("scaleTypeParameter")
|
67
|
+
public int getScaleTypeParameter()
|
68
|
+
{
|
69
|
+
return scaleTypeParameter;
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,423 @@
|
|
1
|
+
package org.embulk.output.jdbc;
|
2
|
+
|
3
|
+
import java.util.List;
|
4
|
+
import java.sql.Connection;
|
5
|
+
import java.sql.DatabaseMetaData;
|
6
|
+
import java.sql.PreparedStatement;
|
7
|
+
import java.sql.SQLException;
|
8
|
+
import java.sql.Statement;
|
9
|
+
import org.slf4j.Logger;
|
10
|
+
import org.embulk.spi.Exec;
|
11
|
+
|
12
|
+
public class JdbcOutputConnection
|
13
|
+
implements AutoCloseable
|
14
|
+
{
|
15
|
+
private final Logger logger = Exec.getLogger(JdbcOutputConnection.class);
|
16
|
+
protected final Connection connection;
|
17
|
+
protected final String schemaName;
|
18
|
+
protected final DatabaseMetaData databaseMetaData;
|
19
|
+
protected String identifierQuoteString;
|
20
|
+
|
21
|
+
public JdbcOutputConnection(Connection connection, String schemaName)
|
22
|
+
throws SQLException
|
23
|
+
{
|
24
|
+
this.connection = connection;
|
25
|
+
this.schemaName = schemaName;
|
26
|
+
this.databaseMetaData = connection.getMetaData();
|
27
|
+
this.identifierQuoteString = databaseMetaData.getIdentifierQuoteString();
|
28
|
+
if (schemaName != null) {
|
29
|
+
setSearchPath(schemaName);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
@Override
|
34
|
+
public void close() throws SQLException
|
35
|
+
{
|
36
|
+
connection.close();
|
37
|
+
}
|
38
|
+
|
39
|
+
public String getSchemaName()
|
40
|
+
{
|
41
|
+
return schemaName;
|
42
|
+
}
|
43
|
+
|
44
|
+
public DatabaseMetaData getMetaData() throws SQLException
|
45
|
+
{
|
46
|
+
return databaseMetaData;
|
47
|
+
}
|
48
|
+
|
49
|
+
protected void setSearchPath(String schema) throws SQLException
|
50
|
+
{
|
51
|
+
Statement stmt = connection.createStatement();
|
52
|
+
try {
|
53
|
+
String sql = "SET search_path TO " + quoteIdentifierString(schema);
|
54
|
+
executeUpdate(stmt, sql);
|
55
|
+
} finally {
|
56
|
+
stmt.close();
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
public void dropTableIfExists(String tableName) throws SQLException
|
61
|
+
{
|
62
|
+
Statement stmt = connection.createStatement();
|
63
|
+
try {
|
64
|
+
String sql = String.format("DROP TABLE IF EXISTS %s", quoteIdentifierString(tableName));
|
65
|
+
executeUpdate(stmt, sql);
|
66
|
+
connection.commit();
|
67
|
+
} catch (SQLException ex) {
|
68
|
+
connection.rollback();
|
69
|
+
throw ex;
|
70
|
+
} finally {
|
71
|
+
stmt.close();
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
public void createTableIfNotExists(String tableName, JdbcSchema schema) throws SQLException
|
76
|
+
{
|
77
|
+
Statement stmt = connection.createStatement();
|
78
|
+
try {
|
79
|
+
String sql = buildCreateTableIfNotExistsSql(tableName, schema);
|
80
|
+
executeUpdate(stmt, sql);
|
81
|
+
connection.commit();
|
82
|
+
} catch (SQLException ex) {
|
83
|
+
connection.rollback();
|
84
|
+
throw ex;
|
85
|
+
} finally {
|
86
|
+
stmt.close();
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
protected String buildCreateTableIfNotExistsSql(String name, JdbcSchema schema)
|
91
|
+
{
|
92
|
+
StringBuilder sb = new StringBuilder();
|
93
|
+
|
94
|
+
sb.append("CREATE TABLE IF NOT EXISTS ");
|
95
|
+
quoteIdentifierString(sb, name);
|
96
|
+
sb.append(" (");
|
97
|
+
boolean first = true;
|
98
|
+
for (JdbcColumn c : schema.getColumns()) {
|
99
|
+
if (first) { first = false; }
|
100
|
+
else { sb.append(", "); }
|
101
|
+
quoteIdentifierString(sb, c.getName());
|
102
|
+
sb.append(" ");
|
103
|
+
String typeName = getCreateTableTypeName(c);
|
104
|
+
sb.append(typeName);
|
105
|
+
}
|
106
|
+
sb.append(")");
|
107
|
+
|
108
|
+
return sb.toString();
|
109
|
+
}
|
110
|
+
|
111
|
+
public static enum ColumnDeclareType
|
112
|
+
{
|
113
|
+
SIMPLE,
|
114
|
+
SIZE,
|
115
|
+
SIZE_AND_SCALE,
|
116
|
+
SIZE_AND_OPTIONAL_SCALE,
|
117
|
+
};
|
118
|
+
|
119
|
+
protected String getCreateTableTypeName(JdbcColumn c)
|
120
|
+
{
|
121
|
+
String convertedTypeName = convertTypeName(c.getTypeName());
|
122
|
+
switch (getColumnDeclareType(convertedTypeName, c)) {
|
123
|
+
case SIZE:
|
124
|
+
return String.format("%s(%d)", convertedTypeName, c.getSizeTypeParameter());
|
125
|
+
case SIZE_AND_SCALE:
|
126
|
+
if (c.getScaleTypeParameter() < 0) {
|
127
|
+
return String.format("%s(%d,0)", convertedTypeName, c.getSizeTypeParameter());
|
128
|
+
} else {
|
129
|
+
return String.format("%s(%d,%d)", convertedTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
|
130
|
+
}
|
131
|
+
case SIZE_AND_OPTIONAL_SCALE:
|
132
|
+
if (c.getScaleTypeParameter() < 0) {
|
133
|
+
return String.format("%s(%d)", convertedTypeName, c.getSizeTypeParameter());
|
134
|
+
} else {
|
135
|
+
return String.format("%s(%d,%d)", convertedTypeName, c.getSizeTypeParameter(), c.getScaleTypeParameter());
|
136
|
+
}
|
137
|
+
default: // SIMPLE
|
138
|
+
return convertedTypeName;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
// hook point for subclasses
|
143
|
+
protected String convertTypeName(String typeName)
|
144
|
+
{
|
145
|
+
return typeName;
|
146
|
+
}
|
147
|
+
|
148
|
+
// TODO
|
149
|
+
private static final String[] STANDARD_SIZE_TYPE_NAMES = new String[] {
|
150
|
+
"CHAR",
|
151
|
+
"VARCHAR", "CHAR VARYING", "CHARACTER VARYING", "LONGVARCHAR",
|
152
|
+
"NCHAR",
|
153
|
+
"NVARCHAR", "NCHAR VARYING", "NATIONAL CHAR VARYING", "NATIONAL CHARACTER VARYING",
|
154
|
+
"BINARY",
|
155
|
+
"VARBINARY", "BINARY VARYING", "LONGVARBINARY",
|
156
|
+
"BIT",
|
157
|
+
"VARBIT", "BIT VARYING",
|
158
|
+
"FLOAT", // SQL standard's FLOAT[(p)] optionally accepts precision
|
159
|
+
};
|
160
|
+
|
161
|
+
private static final String[] STANDARD_SIZE_AND_SCALE_TYPE_NAMES = new String[] {
|
162
|
+
"DECIMAL",
|
163
|
+
};
|
164
|
+
|
165
|
+
protected ColumnDeclareType getColumnDeclareType(String convertedTypeName, JdbcColumn col)
|
166
|
+
{
|
167
|
+
for (String x : STANDARD_SIZE_TYPE_NAMES) {
|
168
|
+
if (x.equals(convertedTypeName)) {
|
169
|
+
return ColumnDeclareType.SIZE;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
for (String x : STANDARD_SIZE_AND_SCALE_TYPE_NAMES) {
|
174
|
+
if (x.equals(convertedTypeName)) {
|
175
|
+
return ColumnDeclareType.SIZE_AND_SCALE;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
|
179
|
+
return ColumnDeclareType.SIMPLE;
|
180
|
+
}
|
181
|
+
|
182
|
+
protected String buildInsertTableSql(String fromTable, JdbcSchema fromTableSchema, String toTable)
|
183
|
+
{
|
184
|
+
StringBuilder sb = new StringBuilder();
|
185
|
+
|
186
|
+
sb.append("INSERT INTO ");
|
187
|
+
quoteIdentifierString(sb, toTable);
|
188
|
+
sb.append(" (");
|
189
|
+
boolean first = true;
|
190
|
+
for (JdbcColumn c : fromTableSchema.getColumns()) {
|
191
|
+
if (first) { first = false; }
|
192
|
+
else { sb.append(", "); }
|
193
|
+
quoteIdentifierString(sb, c.getName());
|
194
|
+
}
|
195
|
+
sb.append(") ");
|
196
|
+
sb.append("SELECT ");
|
197
|
+
for (JdbcColumn c : fromTableSchema.getColumns()) {
|
198
|
+
if (first) { first = false; }
|
199
|
+
else { sb.append(", "); }
|
200
|
+
quoteIdentifierString(sb, c.getName());
|
201
|
+
}
|
202
|
+
sb.append(" FROM ");
|
203
|
+
quoteIdentifierString(sb, fromTable);
|
204
|
+
|
205
|
+
return sb.toString();
|
206
|
+
}
|
207
|
+
|
208
|
+
protected String buildTruncateSql(String table)
|
209
|
+
{
|
210
|
+
StringBuilder sb = new StringBuilder();
|
211
|
+
|
212
|
+
sb.append("DELETE FROM ");
|
213
|
+
quoteIdentifierString(sb, table);
|
214
|
+
|
215
|
+
return sb.toString();
|
216
|
+
}
|
217
|
+
|
218
|
+
protected void insertTable(String fromTable, JdbcSchema fromTableSchema, String toTable,
|
219
|
+
boolean truncateDestinationFirst) throws SQLException
|
220
|
+
{
|
221
|
+
Statement stmt = connection.createStatement();
|
222
|
+
try {
|
223
|
+
if(truncateDestinationFirst) {
|
224
|
+
String sql = buildTruncateSql(toTable);
|
225
|
+
executeUpdate(stmt, sql);
|
226
|
+
}
|
227
|
+
String sql = buildInsertTableSql(fromTable, fromTableSchema, toTable);
|
228
|
+
executeUpdate(stmt, sql);
|
229
|
+
connection.commit();
|
230
|
+
} catch (SQLException ex) {
|
231
|
+
connection.rollback();
|
232
|
+
throw ex;
|
233
|
+
} finally {
|
234
|
+
stmt.close();
|
235
|
+
}
|
236
|
+
}
|
237
|
+
|
238
|
+
public PreparedStatement prepareInsertSql(String toTable, JdbcSchema toTableSchema) throws SQLException
|
239
|
+
{
|
240
|
+
String insertSql = buildPrepareInsertSql(toTable, toTableSchema);
|
241
|
+
logger.info("Prepared SQL: {}", insertSql);
|
242
|
+
return connection.prepareStatement(insertSql);
|
243
|
+
}
|
244
|
+
|
245
|
+
protected String buildPrepareInsertSql(String toTable, JdbcSchema toTableSchema) throws SQLException
|
246
|
+
{
|
247
|
+
StringBuilder sb = new StringBuilder();
|
248
|
+
|
249
|
+
sb.append("INSERT INTO ");
|
250
|
+
quoteIdentifierString(sb, toTable);
|
251
|
+
|
252
|
+
sb.append(" (");
|
253
|
+
for (int i=0; i < toTableSchema.getCount(); i++) {
|
254
|
+
if(i != 0) { sb.append(", "); }
|
255
|
+
quoteIdentifierString(sb, toTableSchema.getColumnName(i));
|
256
|
+
}
|
257
|
+
sb.append(") VALUES (");
|
258
|
+
for(int i=0; i < toTableSchema.getCount(); i++) {
|
259
|
+
if(i != 0) { sb.append(", "); }
|
260
|
+
sb.append("?");
|
261
|
+
}
|
262
|
+
sb.append(")");
|
263
|
+
|
264
|
+
return sb.toString();
|
265
|
+
}
|
266
|
+
|
267
|
+
// TODO
|
268
|
+
//protected void gatherInsertTables(List<String> fromTables, JdbcSchema fromTableSchema, String toTable,
|
269
|
+
// boolean truncateDestinationFirst) throws SQLException
|
270
|
+
//{
|
271
|
+
// Statement stmt = connection.createStatement();
|
272
|
+
// try {
|
273
|
+
// if(truncateDestinationFirst) {
|
274
|
+
// String sql = buildTruncateSql(toTable);
|
275
|
+
// executeUpdate(stmt, sql);
|
276
|
+
// }
|
277
|
+
// String sql = buildGatherInsertTables(fromTable, fromTableSchema, toTable);
|
278
|
+
// executeUpdate(stmt, sql);
|
279
|
+
// connection.commit();
|
280
|
+
// } catch (SQLException ex) {
|
281
|
+
// connection.rollback();
|
282
|
+
// throw ex;
|
283
|
+
// } finally {
|
284
|
+
// stmt.close();
|
285
|
+
// }
|
286
|
+
//}
|
287
|
+
|
288
|
+
public void replaceTable(String fromTable, JdbcSchema schema, String toTable) throws SQLException
|
289
|
+
{
|
290
|
+
Statement stmt = connection.createStatement();
|
291
|
+
try {
|
292
|
+
{
|
293
|
+
StringBuilder sb = new StringBuilder();
|
294
|
+
sb.append("DROP TABLE IF EXISTS ");
|
295
|
+
quoteIdentifierString(sb, toTable);
|
296
|
+
String sql = sb.toString();
|
297
|
+
executeUpdate(stmt, sql);
|
298
|
+
}
|
299
|
+
|
300
|
+
{
|
301
|
+
StringBuilder sb = new StringBuilder();
|
302
|
+
sb.append("ALTER TABLE ");
|
303
|
+
quoteIdentifierString(sb, fromTable);
|
304
|
+
sb.append(" RENAME TO ");
|
305
|
+
quoteIdentifierString(sb, toTable);
|
306
|
+
String sql = sb.toString();
|
307
|
+
executeUpdate(stmt, sql);
|
308
|
+
}
|
309
|
+
|
310
|
+
connection.commit();
|
311
|
+
} catch (SQLException ex) {
|
312
|
+
connection.rollback();
|
313
|
+
throw ex;
|
314
|
+
} finally {
|
315
|
+
stmt.close();
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
protected void quoteIdentifierString(StringBuilder sb, String str)
|
320
|
+
{
|
321
|
+
sb.append(quoteIdentifierString(str, identifierQuoteString));
|
322
|
+
}
|
323
|
+
|
324
|
+
protected String quoteIdentifierString(String str)
|
325
|
+
{
|
326
|
+
return quoteIdentifierString(str, identifierQuoteString);
|
327
|
+
}
|
328
|
+
|
329
|
+
protected String quoteIdentifierString(String str, String quoteString)
|
330
|
+
{
|
331
|
+
// TODO if identifierQuoteString.equals(" ") && str.contains([^a-zA-Z0-9_connection.getMetaData().getExtraNameCharacters()])
|
332
|
+
// TODO if str.contains(identifierQuoteString);
|
333
|
+
return quoteString + str + quoteString;
|
334
|
+
}
|
335
|
+
|
336
|
+
// PostgreSQL JDBC driver implements isValid() method. But the
|
337
|
+
// implementation throws following exception:
|
338
|
+
// "java.io.IOException: Method org.postgresql.jdbc4.Jdbc4Connection.isValid(int) is not yet implemented."
|
339
|
+
//
|
340
|
+
// So, checking mechanism doesn't work at all.
|
341
|
+
// Thus here just runs "SELECT 1" to check connectivity.
|
342
|
+
//
|
343
|
+
public boolean isValidConnection(int timeout) throws SQLException
|
344
|
+
{
|
345
|
+
Statement stmt = connection.createStatement();
|
346
|
+
try {
|
347
|
+
stmt.executeQuery("SELECT 1").close();
|
348
|
+
return true;
|
349
|
+
} catch (SQLException ex) {
|
350
|
+
return false;
|
351
|
+
} finally {
|
352
|
+
stmt.close();
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
protected String[] getDeterministicSqlStates()
|
357
|
+
{
|
358
|
+
return new String[0];
|
359
|
+
}
|
360
|
+
|
361
|
+
protected int[] getDeterministicErrorCodes()
|
362
|
+
{
|
363
|
+
return new int[0];
|
364
|
+
}
|
365
|
+
|
366
|
+
protected Class[] getDeterministicRootCauses()
|
367
|
+
{
|
368
|
+
return new Class[] {
|
369
|
+
// Don't retry on UnknownHostException.
|
370
|
+
java.net.UnknownHostException.class,
|
371
|
+
|
372
|
+
//// we should not retry on connect() error?
|
373
|
+
//java.net.ConnectException.class,
|
374
|
+
};
|
375
|
+
}
|
376
|
+
|
377
|
+
public boolean isRetryableException(SQLException exception)
|
378
|
+
{
|
379
|
+
String sqlState = exception.getSQLState();
|
380
|
+
for (String deterministic : getDeterministicSqlStates()) {
|
381
|
+
if (sqlState.equals(deterministic)) {
|
382
|
+
return false;
|
383
|
+
}
|
384
|
+
}
|
385
|
+
|
386
|
+
int errorCode = exception.getErrorCode();
|
387
|
+
for (int deterministic : getDeterministicErrorCodes()) {
|
388
|
+
if (errorCode == deterministic) {
|
389
|
+
return false;
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
Throwable rootCause = getRootCause(exception);
|
394
|
+
for (Class deterministic : getDeterministicRootCauses()) {
|
395
|
+
if (deterministic.equals(rootCause.getClass())) {
|
396
|
+
return false;
|
397
|
+
}
|
398
|
+
}
|
399
|
+
|
400
|
+
return true;
|
401
|
+
}
|
402
|
+
|
403
|
+
private Throwable getRootCause(Throwable e) {
|
404
|
+
while (e.getCause() != null) {
|
405
|
+
e = e.getCause();
|
406
|
+
}
|
407
|
+
return e;
|
408
|
+
}
|
409
|
+
|
410
|
+
protected int executeUpdate(Statement stmt, String sql) throws SQLException
|
411
|
+
{
|
412
|
+
logger.info("SQL: " + sql);
|
413
|
+
long startTime = System.currentTimeMillis();
|
414
|
+
int count = stmt.executeUpdate(sql);
|
415
|
+
double seconds = (System.currentTimeMillis() - startTime) / 1000.0;
|
416
|
+
if (count == 0) {
|
417
|
+
logger.info(String.format("> %.2f seconds", seconds));
|
418
|
+
} else {
|
419
|
+
logger.info(String.format("> %.2f seconds (%,d rows)", seconds, count));
|
420
|
+
}
|
421
|
+
return count;
|
422
|
+
}
|
423
|
+
}
|