15
15
16
16
package org .alfasoftware .morf .dataset ;
17
17
18
+ import static java .util .stream .Collectors .toList ;
19
+ import static org .alfasoftware .morf .sql .SqlUtils .parameter ;
20
+ import static org .alfasoftware .morf .sql .SqlUtils .tableRef ;
21
+
18
22
import java .sql .Connection ;
19
23
import java .sql .PreparedStatement ;
20
24
25
29
import org .alfasoftware .morf .metadata .SchemaUtils ;
26
30
import org .alfasoftware .morf .metadata .Table ;
27
31
import org .alfasoftware .morf .sql .InsertStatement ;
32
+ import org .alfasoftware .morf .sql .MergeStatement ;
33
+ import org .alfasoftware .morf .sql .SelectStatement ;
28
34
import org .alfasoftware .morf .sql .element .SqlParameter ;
29
- import org .alfasoftware .morf .sql .element .TableReference ;
30
35
import org .apache .commons .logging .Log ;
31
36
import org .apache .commons .logging .LogFactory ;
32
37
@@ -43,12 +48,12 @@ public class TableLoader {
43
48
private final Connection connection ;
44
49
private final SqlDialect sqlDialect ;
45
50
private final boolean explicitCommit ;
51
+ private final boolean merge ;
46
52
private final SqlScriptExecutor sqlExecutor ;
47
53
private final Table table ;
48
54
private final boolean insertingWithPresetAutonums ;
49
55
private final boolean insertingUnderAutonumLimit ;
50
56
private final boolean truncateBeforeLoad ;
51
- private final String insertStatement ;
52
57
private final int batchSize ;
53
58
54
59
@@ -64,28 +69,26 @@ public static TableLoaderBuilder builder() {
64
69
* Constructor.
65
70
*/
66
71
TableLoader (Connection connection ,
67
- SqlScriptExecutor sqlScriptExecutor ,
68
- SqlDialect dialect ,
69
- boolean explicitCommit ,
70
- Table table ,
71
- boolean insertingWithPresetAutonums ,
72
- boolean insertingUnderAutonumLimit ,
73
- boolean truncateBeforeLoad ,
74
- int batchSize ) {
72
+ SqlScriptExecutor sqlScriptExecutor ,
73
+ SqlDialect dialect ,
74
+ boolean explicitCommit ,
75
+ boolean merge ,
76
+ Table table ,
77
+ boolean insertingWithPresetAutonums ,
78
+ boolean insertingUnderAutonumLimit ,
79
+ boolean truncateBeforeLoad ,
80
+ int batchSize ) {
75
81
super ();
76
82
this .connection = connection ;
77
83
this .sqlExecutor = sqlScriptExecutor ;
78
84
this .sqlDialect = dialect ;
79
85
this .explicitCommit = explicitCommit ;
86
+ this .merge = merge ;
80
87
this .table = table ;
81
88
this .insertingWithPresetAutonums = insertingWithPresetAutonums ;
82
89
this .insertingUnderAutonumLimit = insertingUnderAutonumLimit ;
83
90
this .truncateBeforeLoad = truncateBeforeLoad ;
84
91
this .batchSize = batchSize ;
85
- this .insertStatement = sqlDialect .convertStatementToSQL (
86
- new InsertStatement ().into (new TableReference (table .getName ())),
87
- SchemaUtils .schema (table )
88
- );
89
92
}
90
93
91
94
@@ -100,7 +103,7 @@ public void load(final Iterable<Record> records) {
100
103
truncate (table , connection );
101
104
}
102
105
103
- insertRecords (records );
106
+ insertOrMergeRecords (records );
104
107
}
105
108
106
109
@@ -131,13 +134,13 @@ private void truncate(Table table, Connection connection) {
131
134
* @param records The records to insert
132
135
* @param bulk Indicates if we should call {@link SqlDialect#preInsertStatements(Table)} and {@link SqlDialect#postInsertStatements(Table)}
133
136
*/
134
- private void insertRecords (Iterable <Record > records ) {
137
+ private void insertOrMergeRecords (Iterable <Record > records ) {
135
138
136
139
if (insertingWithPresetAutonums ) {
137
140
sqlExecutor .execute (sqlDialect .preInsertWithPresetAutonumStatements (table , insertingUnderAutonumLimit ), connection );
138
141
}
139
142
140
- sqlInsertLoad (table , records , connection );
143
+ sqlInsertOrMergeLoad (table , records , connection );
141
144
142
145
if (insertingWithPresetAutonums ) {
143
146
sqlDialect .postInsertWithPresetAutonumStatements (table , sqlExecutor , connection ,insertingUnderAutonumLimit );
@@ -152,11 +155,26 @@ private void insertRecords(Iterable<Record> records) {
152
155
* @param records The data to insert.
153
156
* @param connection The connection.
154
157
*/
155
- private void sqlInsertLoad (Table table , Iterable <Record > records , Connection connection ) {
158
+ private void sqlInsertOrMergeLoad (Table table , Iterable <Record > records , Connection connection ) {
156
159
try {
157
- sqlExecutor .executeStatementBatch (insertStatement , SqlParameter .parametersFromColumns (table .columns ()), records , connection , explicitCommit , batchSize );
158
- } catch (Exception e ) {
159
- throw new RuntimeException (String .format ("Failure in batch insert for table [%s]" , table .getName ()), e );
160
+ if (merge && !table .primaryKey ().isEmpty ()) { // if the table has no primary we don't have a merge ON criterion
161
+ SelectStatement selectStatement = SelectStatement .select ()
162
+ .fields (table .columns ().stream ().filter (c -> !c .isAutoNumbered ()).map (c -> parameter (c )).collect (toList ()))
163
+ .build ();
164
+ MergeStatement mergeStatement = MergeStatement .merge ()
165
+ .from (selectStatement )
166
+ .into (tableRef (table .getName ()))
167
+ .tableUniqueKey (table .primaryKey ().stream ().map (c -> parameter (c )).collect (toList ()))
168
+ .build ();
169
+ String mergeSQL = sqlDialect .convertStatementToSQL (mergeStatement );
170
+ sqlExecutor .executeStatementBatch (mergeSQL , SqlParameter .parametersFromColumns (table .columns ()), records , connection , explicitCommit , batchSize );
171
+ } else {
172
+ InsertStatement insertStatement = InsertStatement .insert ().into (tableRef (table .getName ())).build ();
173
+ String insertSQL = sqlDialect .convertStatementToSQL (insertStatement , SchemaUtils .schema (table ));
174
+ sqlExecutor .executeStatementBatch (insertSQL , SqlParameter .parametersFromColumns (table .columns ()), records , connection , explicitCommit , batchSize );
175
+ }
176
+ } catch (Exception exceptionOnBatch ) {
177
+ throw new RuntimeException (String .format ("Failure in batch insert for table [%s]" , table .getName ()), exceptionOnBatch );
160
178
}
161
179
}
162
180
}
0 commit comments