Skip to content

Commit 6cc94c2

Browse files
authored
Merge pull request #1064 from momosetkn/feat/upsert
Support upsert
2 parents ae08233 + c819209 commit 6cc94c2

File tree

86 files changed

+3630
-51
lines changed

Some content is hidden

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

86 files changed

+3630
-51
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.seasar.doma.jdbc.SqlFileNotFoundException;
1212
import org.seasar.doma.jdbc.SqlLogType;
1313
import org.seasar.doma.jdbc.UniqueConstraintException;
14+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1415

1516
/**
1617
* Indicates a batch insert.
@@ -103,4 +104,15 @@
103104
* @return Whether auto-generated keys are not retrieved.
104105
*/
105106
boolean ignoreGeneratedKeys() default false;
107+
108+
/**
109+
* This variable represents the type of duplicate key handling strategy for an insert operation.
110+
* It can have one of three values: - UPDATE: If a duplicate key is encountered, the existing row
111+
* in the table will be updated. - IGNORE: If a duplicate key is encountered, the insert operation
112+
* will be ignored, and no changes will be made to the table. - EXCEPTION: If a duplicate key is
113+
* encountered, the operation will throw an exception, indicating that a duplicate key exists.
114+
*
115+
* @return the type of duplicate key handling strategy for an insert operation.
116+
*/
117+
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
106118
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.seasar.doma.jdbc.SqlFileNotFoundException;
1111
import org.seasar.doma.jdbc.SqlLogType;
1212
import org.seasar.doma.jdbc.UniqueConstraintException;
13+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1314

1415
/**
1516
* Indicates a insert.
@@ -91,4 +92,15 @@
9192
* @return the output format of SQL logs.
9293
*/
9394
SqlLogType sqlLog() default SqlLogType.FORMATTED;
95+
96+
/**
97+
* This variable represents the type of duplicate key handling strategy for an insert operation.
98+
* It can have one of three values: - UPDATE: If a duplicate key is encountered, the existing row
99+
* in the table will be updated. - IGNORE: If a duplicate key is encountered, the insert operation
100+
* will be ignored, and no changes will be made to the table. - EXCEPTION: If a duplicate key is
101+
* encountered, the operation will throw an exception, indicating that a duplicate key exists.
102+
*
103+
* @return the type of duplicate key handling strategy for an insert operation.
104+
*/
105+
DuplicateKeyType duplicateKeyType() default DuplicateKeyType.EXCEPTION;
94106
}

doma-core/src/main/java/org/seasar/doma/internal/jdbc/entity/AbstractPostInsertContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44
import org.seasar.doma.jdbc.Config;
55
import org.seasar.doma.jdbc.entity.EntityType;
66
import org.seasar.doma.jdbc.entity.PostInsertContext;
7+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
78

89
public class AbstractPostInsertContext<E> extends AbstractEntityListenerContext<E>
910
implements PostInsertContext<E> {
1011

11-
protected AbstractPostInsertContext(EntityType<E> entityType, Method method, Config config) {
12+
private final DuplicateKeyType duplicateKeyType;
13+
14+
protected AbstractPostInsertContext(
15+
EntityType<E> entityType, Method method, Config config, DuplicateKeyType duplicateKeyType) {
1216
super(entityType, method, config);
17+
this.duplicateKeyType = duplicateKeyType;
18+
}
19+
20+
@Override
21+
public DuplicateKeyType getDuplicateKeyType() {
22+
return duplicateKeyType;
1323
}
1424
}

doma-core/src/main/java/org/seasar/doma/internal/jdbc/entity/AbstractPreInsertContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@
44
import org.seasar.doma.jdbc.Config;
55
import org.seasar.doma.jdbc.entity.EntityType;
66
import org.seasar.doma.jdbc.entity.PreInsertContext;
7+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
78

89
public class AbstractPreInsertContext<E> extends AbstractEntityListenerContext<E>
910
implements PreInsertContext<E> {
1011

11-
protected AbstractPreInsertContext(EntityType<E> entityType, Method method, Config config) {
12+
private final DuplicateKeyType duplicateKeyType;
13+
14+
protected AbstractPreInsertContext(
15+
EntityType<E> entityType, Method method, Config config, DuplicateKeyType duplicateKeyType) {
1216
super(entityType, method, config);
17+
this.duplicateKeyType = duplicateKeyType;
18+
}
19+
20+
@Override
21+
public DuplicateKeyType getDuplicateKeyType() {
22+
return duplicateKeyType;
1323
}
1424
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/Entityql.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ public <ENTITY> Statement<Result<ENTITY>> delete(
105105
return new EntityqlDeleteStatement<>(config, entityMetamodel, entity, settings);
106106
}
107107

108-
public <ENTITY> Statement<Result<ENTITY>> insert(
108+
public <ENTITY> EntityqlInsertStatement<ENTITY> insert(
109109
EntityMetamodel<ENTITY> entityMetamodel, ENTITY entity) {
110110
Objects.requireNonNull(entityMetamodel);
111111
Objects.requireNonNull(entity);
112112
return insert(entityMetamodel, entity, settings -> {});
113113
}
114114

115-
public <ENTITY> Statement<Result<ENTITY>> insert(
115+
public <ENTITY> EntityqlInsertStatement<ENTITY> insert(
116116
EntityMetamodel<ENTITY> entityMetamodel,
117117
ENTITY entity,
118118
Consumer<InsertSettings> settingsConsumer) {
@@ -162,14 +162,14 @@ public <ENTITY> Statement<BatchResult<ENTITY>> delete(
162162
return new EntityqlBatchDeleteStatement<>(config, entityMetamodel, entities, settings);
163163
}
164164

165-
public <ENTITY> Statement<BatchResult<ENTITY>> insert(
165+
public <ENTITY> EntityqlBatchInsertStatement<ENTITY> insert(
166166
EntityMetamodel<ENTITY> entityMetamodel, List<ENTITY> entities) {
167167
Objects.requireNonNull(entityMetamodel);
168168
Objects.requireNonNull(entities);
169169
return insert(entityMetamodel, entities, settings -> {});
170170
}
171171

172-
public <ENTITY> Statement<BatchResult<ENTITY>> insert(
172+
public <ENTITY> EntityqlBatchInsertStatement<ENTITY> insert(
173173
EntityMetamodel<ENTITY> entityMetamodel,
174174
List<ENTITY> entities,
175175
Consumer<InsertSettings> settingsConsumer) {

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/context/InsertContext.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,28 @@
66
import java.util.Map;
77
import java.util.Objects;
88
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
9+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
10+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
911

1012
public class InsertContext implements Context {
1113
public final EntityMetamodel<?> entityMetamodel;
1214
public final List<EntityMetamodel<?>> entityMetamodels;
1315
public final Map<Operand.Prop, Operand.Param> values = new LinkedHashMap<>();
1416
public final InsertSettings settings = new InsertSettings();
1517
public SelectContext selectContext;
18+
public OnDuplicateContext onDuplicateContext = new OnDuplicateContext();
1619

1720
public InsertContext(EntityMetamodel<?> entityMetamodel) {
1821
this.entityMetamodel = Objects.requireNonNull(entityMetamodel);
1922
this.entityMetamodels = Collections.singletonList(entityMetamodel);
2023
}
2124

25+
public class OnDuplicateContext {
26+
public DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
27+
public List<PropertyMetamodel<?>> keys = Collections.emptyList();
28+
public final Map<Operand.Prop, Operand> setValues = new LinkedHashMap<>();
29+
}
30+
2231
@Override
2332
public List<EntityMetamodel<?>> getEntityMetamodels() {
2433
return entityMetamodels;

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/declaration/InsertDeclaration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ public void values(Consumer<ValuesDeclaration> block) {
2424
block.accept(declaration);
2525
}
2626

27+
public void upsertSetValues(Consumer<InsertOnDuplicateKeyUpdateSetValuesDeclaration> block) {
28+
Objects.requireNonNull(block);
29+
InsertOnDuplicateKeyUpdateSetValuesDeclaration declaration =
30+
new InsertOnDuplicateKeyUpdateSetValuesDeclaration(context);
31+
block.accept(declaration);
32+
if (context.onDuplicateContext.setValues.isEmpty()) {
33+
context.onDuplicateContext.setValues.putAll(context.values);
34+
}
35+
}
36+
2737
public void select(Function<InsertSelectDeclaration, SubSelectContext<?>> block) {
2838
InsertSelectDeclaration declaration = new InsertSelectDeclaration();
2939
SubSelectContext<?> subSelectContext = block.apply(declaration);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.seasar.doma.jdbc.criteria.declaration;
2+
3+
import java.util.Objects;
4+
import org.seasar.doma.jdbc.criteria.context.InsertContext;
5+
import org.seasar.doma.jdbc.criteria.context.Operand;
6+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
7+
8+
public class InsertOnDuplicateKeyUpdateSetValuesDeclaration {
9+
10+
private final InsertContext context;
11+
12+
public InsertOnDuplicateKeyUpdateSetValuesDeclaration(InsertContext context) {
13+
this.context = Objects.requireNonNull(context);
14+
}
15+
16+
/**
17+
* Adds a value to the upsert set clause.
18+
*
19+
* @param left The left property metamodel.
20+
* @param right The right value.
21+
* @param <PROPERTY> The type of property.
22+
*/
23+
public <PROPERTY> void value(PropertyMetamodel<PROPERTY> left, PROPERTY right) {
24+
Objects.requireNonNull(left);
25+
this.context.onDuplicateContext.setValues.put(
26+
new Operand.Prop(left), new Operand.Param(left, right));
27+
}
28+
29+
/**
30+
* Adds a value to the upsert set clause.
31+
*
32+
* @param left The left property metamodel.
33+
* @param right The right operand.
34+
* @param <PROPERTY> The type of property.
35+
*/
36+
public <PROPERTY> void value(PropertyMetamodel<PROPERTY> left, Operand.Prop right) {
37+
Objects.requireNonNull(left);
38+
this.context.onDuplicateContext.setValues.put(new Operand.Prop(left), right);
39+
}
40+
41+
/**
42+
* References the records that excluded from insertion into a table.
43+
*
44+
* @param excludedPropertyMeta Specifies columns excluded from the insert record.specify what is
45+
* in PropertyMetamodel set in `values`.
46+
* @param <PROPERTY> The type of property.
47+
* @return set clause operand
48+
*/
49+
public <PROPERTY> Operand.Prop excluded(PropertyMetamodel<PROPERTY> excludedPropertyMeta) {
50+
Objects.requireNonNull(excludedPropertyMeta);
51+
return new Operand.Prop(excludedPropertyMeta);
52+
}
53+
}

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/statement/EntityqlBatchInsertStatement.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
1313
import org.seasar.doma.jdbc.entity.EntityType;
1414
import org.seasar.doma.jdbc.query.AutoBatchInsertQuery;
15+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1516
import org.seasar.doma.jdbc.query.Query;
1617

1718
public class EntityqlBatchInsertStatement<ENTITY>
@@ -21,6 +22,7 @@ public class EntityqlBatchInsertStatement<ENTITY>
2122
private final EntityMetamodel<ENTITY> entityMetamodel;
2223
private final List<ENTITY> entities;
2324
private final InsertSettings settings;
25+
private DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
2426

2527
public EntityqlBatchInsertStatement(
2628
Config config,
@@ -33,6 +35,16 @@ public EntityqlBatchInsertStatement(
3335
this.settings = Objects.requireNonNull(settings);
3436
}
3537

38+
public Statement<BatchResult<ENTITY>> onDuplicateKeyUpdate() {
39+
this.duplicateKeyType = DuplicateKeyType.UPDATE;
40+
return this;
41+
}
42+
43+
public Statement<BatchResult<ENTITY>> onDuplicateKeyIgnore() {
44+
this.duplicateKeyType = DuplicateKeyType.IGNORE;
45+
return this;
46+
}
47+
3648
/**
3749
* {@inheritDoc}
3850
*
@@ -62,6 +74,7 @@ protected Command<BatchResult<ENTITY>> createCommand() {
6274
query.setExcludedPropertyNames();
6375
query.setGeneratedKeysIgnored(settings.getIgnoreGeneratedKeys());
6476
query.setMessage(settings.getComment());
77+
query.setDuplicateKeyType(this.duplicateKeyType);
6578
query.prepare();
6679
BatchInsertCommand command =
6780
config.getCommandImplementors().createBatchInsertCommand(EXECUTE_METHOD, query);

doma-core/src/main/java/org/seasar/doma/jdbc/criteria/statement/EntityqlInsertStatement.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
1111
import org.seasar.doma.jdbc.entity.EntityType;
1212
import org.seasar.doma.jdbc.query.AutoInsertQuery;
13+
import org.seasar.doma.jdbc.query.DuplicateKeyType;
1314
import org.seasar.doma.jdbc.query.Query;
1415

1516
public class EntityqlInsertStatement<ENTITY>
@@ -18,6 +19,7 @@ public class EntityqlInsertStatement<ENTITY>
1819
private final EntityMetamodel<ENTITY> entityMetamodel;
1920
private final ENTITY entity;
2021
private final InsertSettings settings;
22+
private DuplicateKeyType duplicateKeyType = DuplicateKeyType.EXCEPTION;
2123

2224
public EntityqlInsertStatement(
2325
Config config,
@@ -30,6 +32,26 @@ public EntityqlInsertStatement(
3032
this.settings = Objects.requireNonNull(settings);
3133
}
3234

35+
/**
36+
* Create statement that inserts or updates
37+
*
38+
* @return statement
39+
*/
40+
public Statement<Result<ENTITY>> onDuplicateKeyUpdate() {
41+
this.duplicateKeyType = DuplicateKeyType.UPDATE;
42+
return this;
43+
}
44+
45+
/**
46+
* Create statement that inserts or ignore
47+
*
48+
* @return statement
49+
*/
50+
public Statement<Result<ENTITY>> onDuplicateKeyIgnore() {
51+
this.duplicateKeyType = DuplicateKeyType.IGNORE;
52+
return this;
53+
}
54+
3355
/**
3456
* {@inheritDoc}
3557
*
@@ -60,6 +82,7 @@ protected Command<Result<ENTITY>> createCommand() {
6082
query.setExcludedPropertyNames(
6183
settings.exclude().stream().map(PropertyMetamodel::getName).toArray(String[]::new));
6284
query.setMessage(settings.getComment());
85+
query.setDuplicateKeyType(duplicateKeyType);
6386
query.prepare();
6487
InsertCommand command =
6588
config.getCommandImplementors().createInsertCommand(EXECUTE_METHOD, query);

0 commit comments

Comments
 (0)