Skip to content

Commit 8c2b0d7

Browse files
authored
Merge pull request #1150 from domaframework/feat/improve-multi-row-insert
Add `duplicateKeys` property to `Insert`, `BatchInsert`, and `MultiInsert` annotations
2 parents 4fb2a15 + b13e9b5 commit 8c2b0d7

File tree

45 files changed

+584
-73
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+584
-73
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
/target/
88
/.metadata
99
.idea
10+
.kotlin
1011
out
1112
.factorypath

doma-core/src/main/java/org/seasar/doma/BatchInsert.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,18 @@
120120
* @return the type of duplicate key handling strategy for an insert operation.
121121
*/
122122
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
123+
124+
/**
125+
* This variable represents the keys that should be used to determine if a duplicate key exists.
126+
* If the duplicate key exists, the operation will use the {@link #duplicateKeyType()} strategy to
127+
* handle the duplicate key.
128+
*
129+
* <p>Note: This value is only utilized when the {@link #duplicateKeyType()} value is either
130+
* {@code DuplicateKeyType.UPDATE} or {@code DuplicateKeyType.IGNORE}.
131+
*
132+
* <p>Note: Certain DBMSs, such as MySQL, do not utilize this value.
133+
*
134+
* @return the keys that should be used to determine if a duplicate key exists.
135+
*/
136+
String[] duplicateKeys() default {};
123137
}

doma-core/src/main/java/org/seasar/doma/Insert.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,18 @@
108108
* @return the type of duplicate key handling strategy for an insert operation.
109109
*/
110110
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
111+
112+
/**
113+
* This variable represents the keys that should be used to determine if a duplicate key exists.
114+
* If the duplicate key exists, the operation will use the {@link #duplicateKeyType()} strategy to
115+
* handle the duplicate key.
116+
*
117+
* <p>Note: This value is only utilized when the {@link #duplicateKeyType()} value is either
118+
* {@code DuplicateKeyType.UPDATE} or {@code DuplicateKeyType.IGNORE}.
119+
*
120+
* <p>Note: Certain DBMSs, such as MySQL, do not utilize this value.
121+
*
122+
* @return the keys that should be used to determine if a duplicate key exists.
123+
*/
124+
String[] duplicateKeys() default {};
111125
}

doma-core/src/main/java/org/seasar/doma/MultiInsert.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,18 @@
8787
* @return the type of duplicate key handling strategy for an insert operation.
8888
*/
8989
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
90+
91+
/**
92+
* This variable represents the keys that should be used to determine if a duplicate key exists.
93+
* If the duplicate key exists, the operation will use the {@link #duplicateKeyType()} strategy to
94+
* handle the duplicate key.
95+
*
96+
* <p>Note: This value is only utilized when the {@link #duplicateKeyType()} value is either
97+
* {@code DuplicateKeyType.UPDATE} or {@code DuplicateKeyType.IGNORE}.
98+
*
99+
* <p>Note: Certain DBMSs, such as MySQL, do not utilize this value.
100+
*
101+
* @return the keys that should be used to determine if a duplicate key exists.
102+
*/
103+
String[] duplicateKeys() default {};
90104
}

doma-core/src/main/java/org/seasar/doma/jdbc/query/AutoBatchInsertQuery.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import java.lang.reflect.Method;
77
import java.sql.Statement;
88
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.List;
911
import java.util.ListIterator;
12+
import java.util.Objects;
13+
import java.util.stream.Collectors;
1014
import org.seasar.doma.internal.jdbc.entity.AbstractPostInsertContext;
1115
import org.seasar.doma.internal.jdbc.entity.AbstractPreInsertContext;
1216
import org.seasar.doma.internal.jdbc.sql.PreparedSqlBuilder;
@@ -37,6 +41,8 @@ public class AutoBatchInsertQuery<ENTITY> extends AutoBatchModifyQuery<ENTITY>
3741

3842
protected DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
3943

44+
protected String[] duplicateKeyNames = EMPTY_STRINGS;
45+
4046
public AutoBatchInsertQuery(EntityType<ENTITY> entityType) {
4147
super(entityType);
4248
}
@@ -49,6 +55,10 @@ public void setDuplicateKeyType(DuplicateKeyType duplicateKeyType) {
4955
this.duplicateKeyType = duplicateKeyType;
5056
}
5157

58+
public void setDuplicateKeyNames(String... duplicateKeyNames) {
59+
this.duplicateKeyNames = duplicateKeyNames;
60+
}
61+
5262
@Override
5363
public void prepare() {
5464
super.prepare();
@@ -186,11 +196,18 @@ private void assembleInsertSql(PreparedSqlBuilder builder, Naming naming, Dialec
186196
}
187197

188198
private void assembleUpsertSql(PreparedSqlBuilder builder, Naming naming, Dialect dialect) {
199+
List<EntityPropertyType<ENTITY, ?>> duplicateKeys =
200+
Arrays.stream(this.duplicateKeyNames)
201+
.map(entityType::getEntityPropertyType)
202+
.filter(Objects::nonNull)
203+
.collect(Collectors.toList());
204+
189205
UpsertAssemblerContext context =
190206
UpsertAssemblerContextBuilder.buildFromEntity(
191207
builder,
192208
entityType,
193209
duplicateKeyType,
210+
duplicateKeys,
194211
naming,
195212
dialect,
196213
idPropertyTypes,

doma-core/src/main/java/org/seasar/doma/jdbc/query/AutoInsertQuery.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
import java.lang.reflect.Method;
66
import java.sql.Statement;
77
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import java.util.Objects;
11+
import java.util.stream.Collectors;
812
import org.seasar.doma.internal.jdbc.entity.AbstractPostInsertContext;
913
import org.seasar.doma.internal.jdbc.entity.AbstractPreInsertContext;
1014
import org.seasar.doma.internal.jdbc.sql.PreparedSqlBuilder;
@@ -30,6 +34,8 @@ public class AutoInsertQuery<ENTITY> extends AutoModifyQuery<ENTITY> implements
3034

3135
protected DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
3236

37+
protected String[] duplicateKeyNames = EMPTY_STRINGS;
38+
3339
public AutoInsertQuery(EntityType<ENTITY> entityType) {
3440
super(entityType);
3541
}
@@ -153,19 +159,25 @@ private void assembleInsertSql(PreparedSqlBuilder builder, Naming naming, Dialec
153159
}
154160

155161
private void assembleUpsertSql(PreparedSqlBuilder builder, Naming naming, Dialect dialect) {
162+
List<EntityPropertyType<ENTITY, ?>> duplicateKeys =
163+
Arrays.stream(this.duplicateKeyNames)
164+
.map(entityType::getEntityPropertyType)
165+
.filter(Objects::nonNull)
166+
.collect(Collectors.toList());
167+
156168
UpsertAssemblerContext context =
157169
UpsertAssemblerContextBuilder.buildFromEntity(
158170
builder,
159171
entityType,
160172
duplicateKeyType,
173+
duplicateKeys,
161174
naming,
162175
dialect,
163176
idPropertyTypes,
164177
targetPropertyTypes,
165178
entity);
166179
UpsertAssembler upsertAssembler = dialect.getUpsertAssembler(context);
167180
upsertAssembler.assemble();
168-
sql = builder.build(this::comment);
169181
}
170182

171183
@Override
@@ -198,6 +210,10 @@ public void setDuplicateKeyType(DuplicateKeyType duplicateKeyType) {
198210
this.duplicateKeyType = duplicateKeyType;
199211
}
200212

213+
public void setDuplicateKeyNames(String... duplicateKeyNames) {
214+
this.duplicateKeyNames = duplicateKeyNames;
215+
}
216+
201217
protected static class AutoPreInsertContext<E> extends AbstractPreInsertContext<E> {
202218

203219
public AutoPreInsertContext(

doma-core/src/main/java/org/seasar/doma/jdbc/query/AutoMultiInsertQuery.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import java.lang.reflect.Method;
66
import java.sql.Statement;
77
import java.util.ArrayList;
8+
import java.util.Arrays;
89
import java.util.List;
910
import java.util.ListIterator;
11+
import java.util.Objects;
12+
import java.util.stream.Collectors;
1013
import org.seasar.doma.GenerationType;
1114
import org.seasar.doma.internal.jdbc.entity.AbstractPostInsertContext;
1215
import org.seasar.doma.internal.jdbc.entity.AbstractPreInsertContext;
@@ -33,6 +36,8 @@ public class AutoMultiInsertQuery<ENTITY> extends AutoModifyQuery<ENTITY> implem
3336

3437
protected DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
3538

39+
protected String[] duplicateKeyNames = EMPTY_STRINGS;
40+
3641
public AutoMultiInsertQuery(EntityType<ENTITY> entityType) {
3742
super(entityType);
3843
}
@@ -165,11 +170,18 @@ private void assembleInsertSql(PreparedSqlBuilder builder, Naming naming, Dialec
165170
}
166171

167172
private void assembleUpsertSql(PreparedSqlBuilder builder, Naming naming, Dialect dialect) {
173+
List<EntityPropertyType<ENTITY, ?>> duplicateKeys =
174+
Arrays.stream(this.duplicateKeyNames)
175+
.map(entityType::getEntityPropertyType)
176+
.filter(Objects::nonNull)
177+
.collect(Collectors.toList());
178+
168179
UpsertAssemblerContext context =
169180
UpsertAssemblerContextBuilder.buildFromEntityList(
170181
builder,
171182
entityType,
172183
duplicateKeyType,
184+
duplicateKeys,
173185
naming,
174186
dialect,
175187
idPropertyTypes,
@@ -213,6 +225,10 @@ public void setDuplicateKeyType(DuplicateKeyType duplicateKeyType) {
213225
this.duplicateKeyType = duplicateKeyType;
214226
}
215227

228+
public void setDuplicateKeyNames(String... duplicateKeyNames) {
229+
this.duplicateKeyNames = duplicateKeyNames;
230+
}
231+
216232
protected static class AutoPreInsertContext<E> extends AbstractPreInsertContext<E> {
217233

218234
public AutoPreInsertContext(

doma-core/src/main/java/org/seasar/doma/jdbc/query/UpsertAssemblerContextBuilder.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public static <ENTITY> UpsertAssemblerContext buildFromEntity(
4848
PreparedSqlBuilder buf,
4949
EntityType<ENTITY> entityType,
5050
DuplicateKeyType duplicateKeyType,
51+
List<EntityPropertyType<ENTITY, ?>> duplicateKeys,
5152
Naming naming,
5253
Dialect dialect,
5354
List<EntityPropertyType<ENTITY, ?>> idPropertyTypes,
@@ -58,6 +59,7 @@ public static <ENTITY> UpsertAssemblerContext buildFromEntity(
5859
buf,
5960
entityType,
6061
duplicateKeyType,
62+
duplicateKeys,
6163
naming,
6264
dialect,
6365
idPropertyTypes,
@@ -69,6 +71,7 @@ public static <ENTITY> UpsertAssemblerContext buildFromEntityList(
6971
PreparedSqlBuilder buf,
7072
EntityType<ENTITY> entityType,
7173
DuplicateKeyType duplicateKeyType,
74+
List<EntityPropertyType<ENTITY, ?>> duplicateKeys,
7275
Naming naming,
7376
Dialect dialect,
7477
List<EntityPropertyType<ENTITY, ?>> idPropertyTypes,
@@ -97,8 +100,8 @@ public static <ENTITY> UpsertAssemblerContext buildFromEntityList(
97100
duplicateKeyType,
98101
naming,
99102
dialect,
100-
false,
101-
idPropertyTypes,
103+
!duplicateKeys.isEmpty(),
104+
!duplicateKeys.isEmpty() ? duplicateKeys : idPropertyTypes,
102105
insertPropertyTypes,
103106
rows,
104107
Collections.emptyList());

doma-core/src/main/java/org/seasar/doma/message/Message.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ public enum Message implements MessageResource {
929929
DOMA4461(
930930
"If a method annotated with @MultiInsert targets immutable entities for insertion, the return type must be org.seasar.doma.jdbc.MultiResult."
931931
+ "The type argument of org.seasar.doma.jdbc.MultiResult must be the immutable entity class."),
932+
DOMA4462("The property \"{0}\" is not found in the entity class \"{1}\"."),
932933

933934
// other
934935
DOMA5001(

doma-core/src/test/java/org/seasar/doma/jdbc/query/AutoBatchInsertQueryTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.seasar.doma.jdbc.InParameter;
1818
import org.seasar.doma.jdbc.PreparedSql;
1919
import org.seasar.doma.jdbc.SqlLogType;
20+
import org.seasar.doma.jdbc.dialect.PostgresDialect;
2021

2122
@SuppressWarnings("OptionalGetWithoutIsPresent")
2223
public class AutoBatchInsertQueryTest {
@@ -93,4 +94,51 @@ public void testOption_default() {
9394
assertEquals(new BigDecimal(2000), parameters.get(2).getWrapper().get());
9495
assertEquals(10, parameters.get(3).getWrapper().get());
9596
}
97+
98+
@Test
99+
public void testOption_duplicateKeys() {
100+
runtimeConfig.dialect = new PostgresDialect();
101+
102+
Emp emp1 = new Emp();
103+
emp1.setId(10);
104+
emp1.setName("aaa");
105+
106+
Emp emp2 = new Emp();
107+
emp2.setId(20);
108+
emp2.setSalary(new BigDecimal(2000));
109+
emp2.setVersion(10);
110+
111+
AutoBatchInsertQuery<Emp> query = new AutoBatchInsertQuery<>(_Emp.getSingletonInternal());
112+
query.setMethod(method);
113+
query.setConfig(runtimeConfig);
114+
query.setDuplicateKeyType(DuplicateKeyType.UPDATE);
115+
query.setDuplicateKeyNames("name");
116+
query.setCallerClassName("aaa");
117+
query.setCallerMethodName("bbb");
118+
query.setEntities(Arrays.asList(emp1, emp2));
119+
query.setSqlLogType(SqlLogType.FORMATTED);
120+
query.prepare();
121+
122+
PreparedSql sql = query.getSqls().get(0);
123+
assertEquals(
124+
"insert into EMP as target (ID, NAME, SALARY, VERSION) values (?, ?, ?, ?) on conflict (NAME) do update set SALARY = excluded.SALARY, VERSION = excluded.VERSION",
125+
sql.getRawSql());
126+
List<InParameter<?>> parameters = sql.getParameters();
127+
assertEquals(4, parameters.size());
128+
assertEquals(10, parameters.get(0).getWrapper().get());
129+
assertEquals("aaa", parameters.get(1).getWrapper().get());
130+
assertNull(parameters.get(2).getWrapper().get());
131+
assertEquals(1, parameters.get(3).getWrapper().get());
132+
133+
sql = query.getSqls().get(1);
134+
assertEquals(
135+
"insert into EMP as target (ID, NAME, SALARY, VERSION) values (?, ?, ?, ?) on conflict (NAME) do update set SALARY = excluded.SALARY, VERSION = excluded.VERSION",
136+
sql.getRawSql());
137+
parameters = sql.getParameters();
138+
assertEquals(4, parameters.size());
139+
assertEquals(20, parameters.get(0).getWrapper().get());
140+
assertNull(parameters.get(1).getWrapper().get());
141+
assertEquals(new BigDecimal(2000), parameters.get(2).getWrapper().get());
142+
assertEquals(10, parameters.get(3).getWrapper().get());
143+
}
96144
}

0 commit comments

Comments
 (0)