Skip to content

Commit 89870bf

Browse files
authored
Support multi-row insert (#1132)
* Support multi-row insert * Format
1 parent a804914 commit 89870bf

File tree

58 files changed

+2447
-3
lines changed

Some content is hidden

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

58 files changed

+2447
-3
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package org.seasar.doma;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import java.sql.Statement;
8+
import org.seasar.doma.jdbc.Config;
9+
import org.seasar.doma.jdbc.JdbcException;
10+
import org.seasar.doma.jdbc.SqlLogType;
11+
import org.seasar.doma.jdbc.UniqueConstraintException;
12+
13+
/**
14+
* Indicates a multi-row insert.
15+
*
16+
* <p>The annotated method must be a member of a {@link Dao} annotated interface.
17+
*
18+
* <pre>
19+
* &#064;Entity
20+
* public class Employee {
21+
* ...
22+
* }
23+
*
24+
* &#064;Dao
25+
* public interface EmployeeDao {
26+
*
27+
* &#064;MultiInsert
28+
* int insert(List&lt;Employee&gt; employees);
29+
* }
30+
* </pre>
31+
*
32+
* The method may throw following exceptions:
33+
*
34+
* <ul>
35+
* <li>{@link DomaNullPointerException} if any of the method parameters are {@code null}
36+
* <li>{@link UniqueConstraintException} if an unique constraint is violated
37+
* <li>{@link JdbcException} if a JDBC related error occurs
38+
* </ul>
39+
*/
40+
@Target(ElementType.METHOD)
41+
@Retention(RetentionPolicy.RUNTIME)
42+
@DaoMethod
43+
public @interface MultiInsert {
44+
45+
/**
46+
* The query timeout in seconds.
47+
*
48+
* <p>If not specified, {@link Config#getQueryTimeout()} is used.
49+
*
50+
* @return the query timeout
51+
* @see Statement#setQueryTimeout(int)
52+
*/
53+
int queryTimeout() default -1;
54+
55+
/**
56+
* The properties whose mapped columns are included in SQL INSERT statements.
57+
*
58+
* @return the included properties
59+
*/
60+
String[] include() default {};
61+
62+
/**
63+
* The properties whose mapped columns are excluded from SQL INSERT statements.
64+
*
65+
* @return the excluded properties
66+
*/
67+
String[] exclude() default {};
68+
69+
/**
70+
* @return the output format of SQL logs.
71+
*/
72+
SqlLogType sqlLog() default SqlLogType.FORMATTED;
73+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.seasar.doma.jdbc;
2+
3+
import java.util.List;
4+
5+
/**
6+
* A processing result for an immutable entities.
7+
*
8+
* @param <ENTITY> the entity type
9+
*/
10+
public class MultiResult<ENTITY> {
11+
12+
private final int count;
13+
14+
private final List<ENTITY> entities;
15+
16+
/**
17+
* Creates an instance.
18+
*
19+
* @param count the affected row count
20+
* @param entities the entities
21+
*/
22+
public MultiResult(int count, List<ENTITY> entities) {
23+
this.count = count;
24+
this.entities = entities;
25+
}
26+
27+
/**
28+
* Returns the affected row count.
29+
*
30+
* @return the affected row count
31+
*/
32+
public int getCount() {
33+
return count;
34+
}
35+
36+
/**
37+
* Returns the entities.
38+
*
39+
* @return the entities
40+
*/
41+
public List<ENTITY> getEntities() {
42+
return entities;
43+
}
44+
45+
/**
46+
* Returns the entities.
47+
*
48+
* @return the entities
49+
* @see <a href= "https://kotlinlang.org/docs/reference/multi-declarations.html">Destructuring
50+
* Declarations</a>
51+
*/
52+
public List<ENTITY> component1() {
53+
return entities;
54+
}
55+
56+
/**
57+
* Returns the affected row count.
58+
*
59+
* @return the affected row count
60+
* @see <a href= "https://kotlinlang.org/docs/reference/multi-declarations.html">Destructuring
61+
* Declarations</a>
62+
*/
63+
public int component2() {
64+
return count;
65+
}
66+
67+
@Override
68+
public String toString() {
69+
return "MultiResult(entities=" + entities + ", count=" + count + ")";
70+
}
71+
}

doma-core/src/main/java/org/seasar/doma/jdbc/QueryImplementors.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.seasar.doma.jdbc.query.AutoDeleteQuery;
1010
import org.seasar.doma.jdbc.query.AutoFunctionQuery;
1111
import org.seasar.doma.jdbc.query.AutoInsertQuery;
12+
import org.seasar.doma.jdbc.query.AutoMultiInsertQuery;
1213
import org.seasar.doma.jdbc.query.AutoProcedureQuery;
1314
import org.seasar.doma.jdbc.query.AutoUpdateQuery;
1415
import org.seasar.doma.jdbc.query.BlobCreateQuery;
@@ -73,6 +74,11 @@ default <ENTITY> AutoInsertQuery<ENTITY> createAutoInsertQuery(
7374
return new AutoInsertQuery<>(entityType);
7475
}
7576

77+
default <ENTITY> AutoMultiInsertQuery<ENTITY> createAutoMultiInsertQuery(
78+
Method method, EntityType<ENTITY> entityType) {
79+
return new AutoMultiInsertQuery<>(entityType);
80+
}
81+
7682
default <ENTITY> AutoUpdateQuery<ENTITY> createAutoUpdateQuery(
7783
Method method, EntityType<ENTITY> entityType) {
7884
return new AutoUpdateQuery<>(entityType);

doma-core/src/main/java/org/seasar/doma/jdbc/SqlExecutionSkipCause.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ public enum SqlExecutionSkipCause {
77
STATE_UNCHANGED,
88

99
/** there is no entity in the batch process */
10-
BATCH_TARGET_NONEXISTENT
10+
BATCH_TARGET_NONEXISTENT,
11+
12+
/** there is no entity in the multi-row insert process */
13+
MULTI_INSERT_TARGET_NONEXISTENT
1114
}

doma-core/src/main/java/org/seasar/doma/jdbc/SqlKind.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public enum SqlKind {
2424
/** batch delete */
2525
BATCH_DELETE,
2626

27+
/** multi-row insert */
28+
MULTI_INSERT,
29+
2730
/** stored procedure */
2831
PROCEDURE,
2932

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.seasar.doma.jdbc.criteria.statement.EntityqlBatchUpdateStatement;
2121
import org.seasar.doma.jdbc.criteria.statement.EntityqlDeleteStatement;
2222
import org.seasar.doma.jdbc.criteria.statement.EntityqlInsertStatement;
23+
import org.seasar.doma.jdbc.criteria.statement.EntityqlMultiInsertStatement;
2324
import org.seasar.doma.jdbc.criteria.statement.EntityqlSelectStarting;
2425
import org.seasar.doma.jdbc.criteria.statement.EntityqlUpdateStatement;
2526
import org.seasar.doma.jdbc.criteria.statement.SetOperand;
@@ -180,4 +181,23 @@ public <ENTITY> EntityqlBatchInsertStatement<ENTITY> insert(
180181
settingsConsumer.accept(settings);
181182
return new EntityqlBatchInsertStatement<>(config, entityMetamodel, entities, settings);
182183
}
184+
185+
public <ENTITY> EntityqlMultiInsertStatement<ENTITY> insertMulti(
186+
EntityMetamodel<ENTITY> entityMetamodel, List<ENTITY> entities) {
187+
Objects.requireNonNull(entityMetamodel);
188+
Objects.requireNonNull(entities);
189+
return insertMulti(entityMetamodel, entities, settings -> {});
190+
}
191+
192+
public <ENTITY> EntityqlMultiInsertStatement<ENTITY> insertMulti(
193+
EntityMetamodel<ENTITY> entityMetamodel,
194+
List<ENTITY> entities,
195+
Consumer<InsertSettings> settingsConsumer) {
196+
Objects.requireNonNull(entityMetamodel);
197+
Objects.requireNonNull(entities);
198+
Objects.requireNonNull(settingsConsumer);
199+
InsertSettings settings = new InsertSettings();
200+
settingsConsumer.accept(settings);
201+
return new EntityqlMultiInsertStatement<>(config, entityMetamodel, entities, settings);
202+
}
183203
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package org.seasar.doma.jdbc.criteria.statement;
2+
3+
import java.util.List;
4+
import java.util.Objects;
5+
import org.seasar.doma.jdbc.Config;
6+
import org.seasar.doma.jdbc.MultiResult;
7+
import org.seasar.doma.jdbc.Sql;
8+
import org.seasar.doma.jdbc.SqlKind;
9+
import org.seasar.doma.jdbc.command.Command;
10+
import org.seasar.doma.jdbc.command.InsertCommand;
11+
import org.seasar.doma.jdbc.criteria.context.InsertSettings;
12+
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
13+
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
14+
import org.seasar.doma.jdbc.entity.EntityType;
15+
import org.seasar.doma.jdbc.query.AutoMultiInsertQuery;
16+
import org.seasar.doma.jdbc.query.Query;
17+
18+
public class EntityqlMultiInsertStatement<ENTITY>
19+
extends AbstractStatement<EntityqlMultiInsertStatement<ENTITY>, MultiResult<ENTITY>> {
20+
21+
private static final EmptySql EMPTY_SQL = new EmptySql(SqlKind.MULTI_INSERT);
22+
23+
private final EntityMetamodel<ENTITY> entityMetamodel;
24+
private final List<ENTITY> entities;
25+
private final InsertSettings settings;
26+
27+
public EntityqlMultiInsertStatement(
28+
Config config,
29+
EntityMetamodel<ENTITY> entityMetamodel,
30+
List<ENTITY> entities,
31+
InsertSettings settings) {
32+
super(Objects.requireNonNull(config));
33+
this.entityMetamodel = Objects.requireNonNull(entityMetamodel);
34+
this.entities = Objects.requireNonNull(entities);
35+
this.settings = Objects.requireNonNull(settings);
36+
}
37+
38+
/**
39+
* {@inheritDoc}
40+
*
41+
* @throws org.seasar.doma.jdbc.UniqueConstraintException if an unique constraint is violated
42+
* @throws org.seasar.doma.jdbc.JdbcException if a JDBC related error occurs
43+
*/
44+
@SuppressWarnings("EmptyMethod")
45+
@Override
46+
public MultiResult<ENTITY> execute() {
47+
return super.execute();
48+
}
49+
50+
@Override
51+
protected Command<MultiResult<ENTITY>> createCommand() {
52+
EntityType<ENTITY> entityType = entityMetamodel.asType();
53+
AutoMultiInsertQuery<ENTITY> query =
54+
config.getQueryImplementors().createAutoMultiInsertQuery(EXECUTE_METHOD, entityType);
55+
query.setMethod(EXECUTE_METHOD);
56+
query.setConfig(config);
57+
query.setEntities(entities);
58+
query.setCallerClassName(getClass().getName());
59+
query.setCallerMethodName(EXECUTE_METHOD_NAME);
60+
query.setQueryTimeout(settings.getQueryTimeout());
61+
query.setSqlLogType(settings.getSqlLogType());
62+
query.setIncludedPropertyNames(
63+
settings.include().stream().map(PropertyMetamodel::getName).toArray(String[]::new));
64+
query.setExcludedPropertyNames(
65+
settings.exclude().stream().map(PropertyMetamodel::getName).toArray(String[]::new));
66+
query.setMessage(settings.getComment());
67+
query.prepare();
68+
InsertCommand command =
69+
config.getCommandImplementors().createInsertCommand(EXECUTE_METHOD, query);
70+
return new Command<MultiResult<ENTITY>>() {
71+
@Override
72+
public Query getQuery() {
73+
return query;
74+
}
75+
76+
@Override
77+
public MultiResult<ENTITY> execute() {
78+
int count = command.execute();
79+
query.complete();
80+
return new MultiResult<>(count, query.getEntities());
81+
}
82+
};
83+
}
84+
85+
@Override
86+
public Sql<?> asSql() {
87+
if (entities.isEmpty()) {
88+
return EMPTY_SQL;
89+
}
90+
return super.asSql();
91+
}
92+
}

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/Dialect.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ default boolean supportsModOperator() {
164164
return true;
165165
}
166166

167+
default boolean supportsMultiRowInsertStatement() {
168+
return true;
169+
}
170+
171+
default boolean supportsAutoIncrementWhenInsertingMultipleRows() {
172+
return true;
173+
}
174+
167175
/**
168176
* Returns an SQL object to get IDENTITY values that are generated in the database.
169177
*

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/MssqlDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ public boolean supportsSequence() {
110110
return true;
111111
}
112112

113+
@Override
114+
public boolean supportsAutoIncrementWhenInsertingMultipleRows() {
115+
return false;
116+
}
117+
113118
@Override
114119
public ScriptBlockContext createScriptBlockContext() {
115120
return new MssqlScriptBlockContext();

doma-core/src/main/java/org/seasar/doma/jdbc/dialect/Oracle11Dialect.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ public boolean supportsModOperator() {
164164
return false;
165165
}
166166

167+
@Override
168+
public boolean supportsMultiRowInsertStatement() {
169+
return false;
170+
}
171+
172+
@Override
173+
public boolean supportsAutoIncrementWhenInsertingMultipleRows() {
174+
return false;
175+
}
176+
167177
@Override
168178
public JdbcType<ResultSet> getResultSetType() {
169179
return RESULT_SET;

0 commit comments

Comments
 (0)