embulk-output-jdbc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|