Skip to content

Commit 0b59460

Browse files
authored
Merge pull request #2741 from harawata/dirty-select
Add `affectData` attribute to `@Select`, `@SelectProvider` and `<select />`
2 parents 92ea0e7 + 9e786dc commit 0b59460

File tree

23 files changed

+475
-18
lines changed

23 files changed

+475
-18
lines changed

src/main/java/org/apache/ibatis/annotations/Select.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@
5555
*/
5656
String databaseId() default "";
5757

58+
/**
59+
* Returns whether this select affects DB data.<br>
60+
* e.g. RETURNING of PostgreSQL or OUTPUT of MS SQL Server.
61+
*
62+
* @return {@code true} if this select affects DB data; {@code false} if otherwise
63+
* @since 3.5.12
64+
*/
65+
boolean affectData() default false;
66+
5867
/**
5968
* The container annotation for {@link Select}.
6069
* @author Kazuki Shimizu

src/main/java/org/apache/ibatis/annotations/SelectProvider.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,15 @@
9999
*/
100100
String databaseId() default "";
101101

102+
/**
103+
* Returns whether this select affects DB data.<br>
104+
* e.g. RETURNING of PostgreSQL or OUTPUT of MS SQL Server.
105+
*
106+
* @return {@code true} if this select affects DB data; {@code false} if otherwise
107+
* @since 3.5.12
108+
*/
109+
boolean affectData() default false;
110+
102111
/**
103112
* The container annotation for {@link SelectProvider}.
104113
* @author Kazuki Shimizu

src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ public MappedStatement addMappedStatement(
261261
String keyColumn,
262262
String databaseId,
263263
LanguageDriver lang,
264-
String resultSets) {
264+
String resultSets,
265+
boolean dirtySelect) {
265266

266267
if (unresolvedCacheRef) {
267268
throw new IncompleteElementException("Cache-ref not yet resolved");
@@ -285,7 +286,8 @@ public MappedStatement addMappedStatement(
285286
.resultSetType(resultSetType)
286287
.flushCacheRequired(flushCache)
287288
.useCache(useCache)
288-
.cache(currentCache);
289+
.cache(currentCache)
290+
.dirtySelect(dirtySelect);
289291

290292
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
291293
if (statementParameterMap != null) {
@@ -344,12 +346,24 @@ public MappedStatement addMappedStatement(String id, SqlSource sqlSource, Statem
344346
SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType,
345347
String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache,
346348
boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId,
347-
LanguageDriver lang) {
349+
LanguageDriver lang, String resultSets) {
348350
return addMappedStatement(
349351
id, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
350352
parameterMap, parameterType, resultMap, resultType, resultSetType,
351353
flushCache, useCache, resultOrdered, keyGenerator, keyProperty,
352-
keyColumn, databaseId, lang, null);
354+
keyColumn, databaseId, lang, null, false);
355+
}
356+
357+
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType,
358+
SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType,
359+
String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache,
360+
boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId,
361+
LanguageDriver lang) {
362+
return addMappedStatement(
363+
id, sqlSource, statementType, sqlCommandType, fetchSize, timeout,
364+
parameterMap, parameterType, resultMap, resultType, resultSetType,
365+
flushCache, useCache, resultOrdered, keyGenerator, keyProperty,
366+
keyColumn, databaseId, lang, null);
353367
}
354368

355369
private <T> T valueOrDefault(T value, T defaultValue) {

src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ void parseStatement(Method method) {
378378
statementAnnotation.getDatabaseId(),
379379
languageDriver,
380380
// ResultSets
381-
options != null ? nullOrEmpty(options.resultSets()) : null);
381+
options != null ? nullOrEmpty(options.resultSets()) : null,
382+
statementAnnotation.isDirtySelect());
382383
});
383384
}
384385

@@ -604,7 +605,7 @@ private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, St
604605

605606
assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
606607
flushCache, useCache, false,
607-
keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null);
608+
keyGenerator, keyProperty, keyColumn, databaseId, languageDriver, null, false);
608609

609610
id = assistant.applyCurrentNamespace(id, false);
610611

@@ -672,13 +673,15 @@ private class AnnotationWrapper {
672673
private final Annotation annotation;
673674
private final String databaseId;
674675
private final SqlCommandType sqlCommandType;
676+
private boolean dirtySelect;
675677

676678
AnnotationWrapper(Annotation annotation) {
677679
super();
678680
this.annotation = annotation;
679681
if (annotation instanceof Select) {
680682
databaseId = ((Select) annotation).databaseId();
681683
sqlCommandType = SqlCommandType.SELECT;
684+
dirtySelect = ((Select) annotation).affectData();
682685
} else if (annotation instanceof Update) {
683686
databaseId = ((Update) annotation).databaseId();
684687
sqlCommandType = SqlCommandType.UPDATE;
@@ -691,6 +694,7 @@ private class AnnotationWrapper {
691694
} else if (annotation instanceof SelectProvider) {
692695
databaseId = ((SelectProvider) annotation).databaseId();
693696
sqlCommandType = SqlCommandType.SELECT;
697+
dirtySelect = ((SelectProvider) annotation).affectData();
694698
} else if (annotation instanceof UpdateProvider) {
695699
databaseId = ((UpdateProvider) annotation).databaseId();
696700
sqlCommandType = SqlCommandType.UPDATE;
@@ -723,5 +727,9 @@ SqlCommandType getSqlCommandType() {
723727
String getDatabaseId() {
724728
return databaseId;
725729
}
730+
731+
boolean isDirtySelect() {
732+
return dirtySelect;
733+
}
726734
}
727735
}

src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,12 @@ public void parseStatementNode() {
109109
String keyProperty = context.getStringAttribute("keyProperty");
110110
String keyColumn = context.getStringAttribute("keyColumn");
111111
String resultSets = context.getStringAttribute("resultSets");
112+
boolean dirtySelect = context.getBooleanAttribute("affectData", Boolean.FALSE);
112113

113114
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
114115
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
115116
resultSetTypeEnum, flushCache, useCache, resultOrdered,
116-
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
117+
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
117118
}
118119

119120
private void processSelectKeyNodes(String id, Class<?> parameterTypeClass, LanguageDriver langDriver) {
@@ -160,7 +161,7 @@ private void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> paramete
160161
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
161162
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
162163
resultSetTypeEnum, flushCache, useCache, resultOrdered,
163-
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null);
164+
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, null, false);
164165

165166
id = builderAssistant.applyCurrentNamespace(id, false);
166167

src/main/java/org/apache/ibatis/mapping/MappedStatement.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public final class MappedStatement {
5656
private Log statementLog;
5757
private LanguageDriver lang;
5858
private String[] resultSets;
59+
private boolean dirtySelect;
5960

6061
MappedStatement() {
6162
// constructor disabled
@@ -174,6 +175,11 @@ public Builder resultSets(String resultSet) {
174175
return this;
175176
}
176177

178+
public Builder dirtySelect(boolean dirtySelect) {
179+
mappedStatement.dirtySelect = dirtySelect;
180+
return this;
181+
}
182+
177183
/**
178184
* Resul sets.
179185
*
@@ -290,6 +296,10 @@ public String[] getResultSets() {
290296
return resultSets;
291297
}
292298

299+
public boolean isDirtySelect() {
300+
return dirtySelect;
301+
}
302+
293303
/**
294304
* Gets the resul sets.
295305
*

src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public <T> Cursor<T> selectCursor(String statement, Object parameter) {
120120
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
121121
try {
122122
MappedStatement ms = configuration.getMappedStatement(statement);
123+
dirty |= ms.isDirtySelect();
123124
Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
124125
registerCursor(cursor);
125126
return cursor;
@@ -148,6 +149,7 @@ public <E> List<E> selectList(String statement, Object parameter, RowBounds rowB
148149
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
149150
try {
150151
MappedStatement ms = configuration.getMappedStatement(statement);
152+
dirty |= ms.isDirtySelect();
151153
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
152154
} catch (Exception e) {
153155
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);

src/main/resources/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ databaseId CDATA #IMPLIED
184184
lang CDATA #IMPLIED
185185
resultOrdered (true|false) #IMPLIED
186186
resultSets CDATA #IMPLIED
187+
affectData (true|false) #IMPLIED
187188
>
188189

189190
<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind)*>

src/site/es/xdoc/java-api.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,13 @@ public interface ResultHandler<T> {
245245
<p>There is method for flushing(executing) batch update statements that stored in a JDBC driver class at any timing. This method can be used when you use the <code>ExecutorType.BATCH</code> as <code>ExecutorType</code>.</p>
246246
<source><![CDATA[List<BatchResult> flushStatements()]]></source>
247247

248-
<h5>Métodos de control de transacción</h5>
248+
<h5 id="transaction-control-methods">Métodos de control de transacción</h5>
249249
<p>El parámetro ResultContext te da acceso al objeto resultado en sí mismo, un contador del número de objetos creados y un método booleano stop() que te permite indicar a MyBatis que pare la carga de datos.</p>
250250
<source>void commit()
251251
void commit(boolean force)
252252
void rollback()
253253
void rollback(boolean force)</source>
254-
<p>Por defecto MyBatis no hace un commit a no ser que haya detectado que la base de datos ha sido modificada por una insert, update o delete. Si has realizado cambios sin llamar a estos métodos, entonces puedes pasar true en al método de commit() y rollback() para asegurar que se realiza el commit (ten en cuenta que aun así no puedes forzar el commit() en modo auto-commit o cuando se usa un gestor de transacciones externo). La mayoría de las veces no tendrás que llamar a rollback() dado que MyBatis lo hará por ti en caso de que no hayas llamado a commit(). Sin embargo, si necesitas un control más fino sobre la sesión, donde puede que haya varios commits, tienes esta opción para hacerlo posible.</p>
254+
<p>Por defecto MyBatis no hace un commit a no ser que haya detectado que la base de datos ha sido modificada por una insert, update, delete o select con <code>affectData</code> habilitado. Si has realizado cambios sin llamar a estos métodos, entonces puedes pasar true en al método de commit() y rollback() para asegurar que se realiza el commit (ten en cuenta que aun así no puedes forzar el commit() en modo auto-commit o cuando se usa un gestor de transacciones externo). La mayoría de las veces no tendrás que llamar a rollback() dado que MyBatis lo hará por ti en caso de que no hayas llamado a commit(). Sin embargo, si necesitas un control más fino sobre la sesión, donde puede que haya varios commits, tienes esta opción para hacerlo posible.</p>
255255
<p><span class="label important">NOTA</span> MyBatis-Spring y MyBatis-Guice proporcionan gestión de transacción declarativa. Por tanto si estás usando MyBatis con Spring o Guice consulta sus manuales específicos.</p>
256256

257257
<h5>Local Cache</h5>

src/site/es/xdoc/sqlmap-xml.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ ps.setInt(1,id);]]></source>
201201
<code>false</code>.
202202
</td>
203203
</tr>
204+
<tr>
205+
<td><code>affectData</code></td>
206+
<td>Set this to true when writing a INSERT, UPDATE or DELETE statement that returns data so that the transaction is controlled properly. Also see <a href="./java-api.html#transaction-control-methods">Transaction Control Method</a>. Default: <code>false</code> (since 3.5.12)
207+
</td>
208+
</tr>
204209
</tbody>
205210
</table>
206211
</subsection>
@@ -405,6 +410,18 @@ Por ejemplo, si la columna id de la tabla Author del ejemplo siguiente fuera aut
405410
</tr>
406411
</tbody>
407412
</table>
413+
414+
<p>
415+
As an irregular case, some databases allow INSERT, UPDATE or DELETE statement to return result set (e.g. <code>RETURNING</code> clause of PostgreSQL and MariaDB or <code>OUTPUT</code> clause of MS SQL Server). This type of statement must be written as <code><![CDATA[<select>]]></code> to map the returned data.
416+
</p>
417+
418+
<source><![CDATA[<select id="insertAndGetAuthor" resultType="domain.blog.Author"
419+
affectData="true" flushCache="true">
420+
insert into Author (username, password, email, bio)
421+
values (#{username}, #{password}, #{email}, #{bio})
422+
returning id, username, password, email, bio
423+
</select>]]></source>
424+
408425
</subsection>
409426

410427
<subsection name="sql">

0 commit comments

Comments
 (0)