Skip to content

Commit 928de86

Browse files
committed
Merge pull request #99 from domaframework/identity-reservation
バッチ更新においてパフォーマンスが悪くなる問題をデータベースのIDENTITYを事前に予約することで解決
2 parents 013b07b + 49ecabb commit 928de86

File tree

10 files changed

+355
-7
lines changed

10 files changed

+355
-7
lines changed

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ boolean supportsSelectForUpdate(SelectForUpdateType type,
142142
*/
143143
boolean supportsResultSetReturningAsOutParameter();
144144

145+
boolean supportsIdentityReservation();
146+
145147
/**
146148
* データベースで生成されたIDENTITYを取得するためのSQLを返します。
147149
* <p>
@@ -165,6 +167,32 @@ boolean supportsSelectForUpdate(SelectForUpdateType type,
165167
Sql<?> getIdentitySelectSql(String catalogName, String schemaName,
166168
String tableName, String columnName, boolean isQuoteRequired);
167169

170+
/**
171+
* データベースのIDENTITYを予約するためのSQLを返します。
172+
* <p>
173+
* {@link #supportsIdentityReservation()} が {@code true} を返す場合にのみ呼び出し可能です。
174+
*
175+
* @param catalogName
176+
* カタログの名前
177+
* @param schemaName
178+
* スキーマの名前
179+
* @param tableName
180+
* テーブルの名前
181+
* @param columnName
182+
* IDENTITYカラムの名前
183+
* @param isQuoteRequired
184+
* 引用符が必要かどうか
185+
* @param reservationSize
186+
* 予約サイズ
187+
* @return IDENTITYを予約するためのSQL
188+
* @throws DomaNullPointerException
189+
* {@code tableName} と {@code columnName} のいずれかが {@code null}
190+
* の場合
191+
*/
192+
Sql<?> getIdentityReservationSql(String catalogName, String schemaName,
193+
String tableName, String columnName, boolean isQuoteRequired,
194+
int reservationSize);
195+
168196
/**
169197
* シーケンスの次の値を取得するためのSQLを返します。
170198
* <p>

src/main/java/org/seasar/doma/jdbc/dialect/PostgresDialect.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.seasar.doma.jdbc.JdbcMappingVisitor;
3232
import org.seasar.doma.jdbc.ScriptBlockContext;
3333
import org.seasar.doma.jdbc.SelectForUpdateType;
34+
import org.seasar.doma.jdbc.Sql;
3435
import org.seasar.doma.jdbc.SqlKind;
3536
import org.seasar.doma.jdbc.SqlLogFormattingVisitor;
3637
import org.seasar.doma.jdbc.SqlLogType;
@@ -170,18 +171,49 @@ public PreparedSql getIdentitySelectSql(String catalogName,
170171
if (columnName == null) {
171172
throw new DomaNullPointerException("columnName");
172173
}
173-
String qualifiedTableName = DatabaseObjectUtil.getQualifiedName(
174-
isQuoteRequired ? this::applyQuote : Function.identity(),
175-
catalogName, schemaName, tableName + "_" + columnName + "_seq");
174+
String identityTableName = createIdentityTableName(catalogName,
175+
schemaName, tableName, columnName, isQuoteRequired);
176176
StringBuilder buf = new StringBuilder(64);
177177
buf.append("select currval('");
178-
buf.append(qualifiedTableName);
178+
buf.append(identityTableName);
179179
buf.append("')");
180180
String rawSql = buf.toString();
181181
return new PreparedSql(SqlKind.SELECT, rawSql, rawSql, null,
182182
Collections.<InParameter<?>> emptyList(), SqlLogType.FORMATTED);
183183
}
184184

185+
@Override
186+
public Sql<?> getIdentityReservationSql(String catalogName,
187+
String schemaName, String tableName, String columnName,
188+
boolean isQuoteRequired, int reservationSize) {
189+
if (tableName == null) {
190+
throw new DomaNullPointerException("tableName");
191+
}
192+
if (columnName == null) {
193+
throw new DomaNullPointerException("columnName");
194+
}
195+
String identityTableName = createIdentityTableName(catalogName,
196+
schemaName, tableName, columnName, isQuoteRequired);
197+
StringBuilder buf = new StringBuilder(64);
198+
buf.append("select nextval('");
199+
buf.append(identityTableName);
200+
buf.append("') from generate_series(1, ");
201+
buf.append(reservationSize);
202+
buf.append(")");
203+
String rawSql = buf.toString();
204+
return new PreparedSql(SqlKind.SELECT, rawSql, rawSql, null,
205+
Collections.<InParameter<?>> emptyList(), SqlLogType.FORMATTED);
206+
}
207+
208+
protected String createIdentityTableName(String catalogName,
209+
String schemaName, String tableName, String columnName,
210+
boolean isQuoteRequired) {
211+
String qualifiedTableName = DatabaseObjectUtil.getQualifiedName(
212+
isQuoteRequired ? this::applyQuote : Function.identity(),
213+
catalogName, schemaName, tableName + "_" + columnName + "_seq");
214+
return qualifiedTableName;
215+
}
216+
185217
@Override
186218
public PreparedSql getSequenceNextValSql(String qualifiedSequenceName,
187219
long allocationSize) {
@@ -203,6 +235,11 @@ public boolean supportsSequence() {
203235
return true;
204236
}
205237

238+
@Override
239+
public boolean supportsIdentityReservation() {
240+
return true;
241+
}
242+
206243
@Override
207244
public boolean supportsSelectForUpdate(SelectForUpdateType type,
208245
boolean withTargets) {

src/main/java/org/seasar/doma/jdbc/dialect/StandardDialect.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ public boolean supportsSequence() {
410410
return false;
411411
}
412412

413+
@Override
414+
public boolean supportsIdentityReservation() {
415+
return false;
416+
}
417+
413418
@Override
414419
public boolean includesIdentityColumn() {
415420
return false;
@@ -439,6 +444,14 @@ public Sql<?> getIdentitySelectSql(String catalogName, String schemaName,
439444
"getIdentitySelectSql");
440445
}
441446

447+
@Override
448+
public Sql<?> getIdentityReservationSql(String catalogName,
449+
String schemaName, String tableName, String columnName,
450+
boolean isQuoteRequired, int reservationSize) {
451+
throw new JdbcUnsupportedOperationException(getClass().getName(),
452+
"getIdentityReservationSql");
453+
}
454+
442455
@Override
443456
public PreparedSql getSequenceNextValSql(String qualifiedSequenceName,
444457
long allocationSize) {

src/main/java/org/seasar/doma/jdbc/id/BuiltinIdentityIdGenerator.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,40 @@ public class BuiltinIdentityIdGenerator extends AbstractIdGenerator implements
3737

3838
@Override
3939
public boolean supportsBatch(IdGenerationConfig config) {
40-
return false;
40+
return config.getIdProvider().isAvailable();
4141
}
4242

4343
@Override
4444
public boolean includesIdentityColumn(IdGenerationConfig config) {
45+
if (config.getIdProvider().isAvailable()) {
46+
return true;
47+
}
4548
return config.getDialect().includesIdentityColumn();
4649
}
4750

4851
@Override
4952
public boolean supportsAutoGeneratedKeys(IdGenerationConfig config) {
53+
if (config.getIdProvider().isAvailable()) {
54+
return false;
55+
}
5056
return config.getDialect().supportsAutoGeneratedKeys();
5157
}
5258

5359
@Override
5460
public Long generatePreInsert(IdGenerationConfig config) {
61+
IdProvider idProvider = config.getIdProvider();
62+
if (idProvider.isAvailable()) {
63+
return idProvider.get();
64+
}
5565
return null;
5666
}
5767

5868
@Override
5969
public Long generatePostInsert(IdGenerationConfig config,
6070
Statement statement) {
71+
if (config.getIdProvider().isAvailable()) {
72+
return null;
73+
}
6174
if (config.getDialect().supportsAutoGeneratedKeys()) {
6275
return getGeneratedValue(config, statement);
6376
}

src/main/java/org/seasar/doma/jdbc/id/IdGenerationConfig.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ public class IdGenerationConfig {
4040
/** 識別子が属するエンティティ */
4141
protected final EntityType<?> entityType;
4242

43+
/** 識別子プロバイダ */
44+
protected final IdProvider idProvider;
45+
4346
/**
4447
* インスタンスを構築します。
4548
*
@@ -49,9 +52,25 @@ public class IdGenerationConfig {
4952
* 識別子が属するエンティティ
5053
*/
5154
public IdGenerationConfig(Config config, EntityType<?> entityType) {
52-
assertNotNull(config, entityType);
55+
this(config, entityType, new UnavailableIdProvider());
56+
}
57+
58+
/**
59+
* インスタンスを構築します。
60+
*
61+
* @param config
62+
* JDBCの設定
63+
* @param entityType
64+
* 識別子が属するエンティティ
65+
* @param idProvider
66+
* 識別子プロバイダ
67+
*/
68+
public IdGenerationConfig(Config config, EntityType<?> entityType,
69+
IdProvider idProvider) {
70+
assertNotNull(config, entityType, idProvider);
5371
this.config = config;
5472
this.entityType = entityType;
73+
this.idProvider = idProvider;
5574
}
5675

5776
public DataSource getDataSource() {
@@ -94,4 +113,20 @@ public EntityType<?> getEntityType() {
94113
return entityType;
95114
}
96115

116+
public IdProvider getIdProvider() {
117+
return idProvider;
118+
}
119+
120+
protected static class UnavailableIdProvider implements IdProvider {
121+
@Override
122+
public boolean isAvailable() {
123+
return false;
124+
}
125+
126+
@Override
127+
public long get() {
128+
throw new UnsupportedOperationException();
129+
}
130+
131+
}
97132
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2004-2010 the Seasar Foundation and the Others.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13+
* either express or implied. See the License for the specific language
14+
* governing permissions and limitations under the License.
15+
*/
16+
package org.seasar.doma.jdbc.id;
17+
18+
/**
19+
* 識別子プロバイダです。
20+
*
21+
* @author nakamura-to
22+
* @since 2.5.0
23+
*/
24+
public interface IdProvider {
25+
26+
/**
27+
* 識別子を返せるならば {@literal true}
28+
*
29+
* @return 識別子を返せるかどうか
30+
*/
31+
boolean isAvailable();
32+
33+
/**
34+
* 識別子を返します。
35+
*
36+
* @return 識別子
37+
* @throws IllegalStateException
38+
* 不正な内部状態により識別子を返せない場合
39+
* @throws UnsupportedOperationException
40+
* このメソッドの呼び出しをサポートしていない場合
41+
*/
42+
long get();
43+
}

0 commit comments

Comments
 (0)