Skip to content

Add the @Options#timeoutString #2886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/java/org/apache/ibatis/annotations/Options.java
Original file line number Diff line number Diff line change
@@ -96,9 +96,26 @@ enum FlushCachePolicy {
* Returns the statement timeout.
*
* @return the statement timeout
*
* @see #timeoutString()
*/
int timeout() default -1;

/**
* Returns the statement timeout string.
* <p>
* Can specify configuration's variables such as {@code ${timeout.select}}. If not resolve variable value, fallback
* the {@link #timeout()} value.
* </p>
*
* @return the statement timeout string
*
* @see #timeout()
*
* @since 3.5.14
*/
String timeoutString() default "";

/**
* Returns whether use the generated keys feature supported by JDBC 3.0
*
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@@ -80,6 +81,7 @@
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.reflection.TypeParameterResolver;
import org.apache.ibatis.scripting.LanguageDriver;
@@ -101,6 +103,8 @@ public class MapperAnnotationBuilder {
InsertProvider.class, DeleteProvider.class)
.collect(Collectors.toSet());

private static final GenericTokenParser TOKEN_PARSER = new GenericTokenParser("${", "}", t -> t);

private final Configuration configuration;
private final MapperBuilderAssistant assistant;
private final Class<?> type;
@@ -345,7 +349,8 @@ void parseStatement(Method method) {
useCache = options.useCache();
// issue #348
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null;
timeout = options.timeout() > -1 ? options.timeout() : null;
Integer fallbackTimeout = options.timeout() > -1 ? options.timeout() : null;
timeout = parseStringValue(options.timeoutString(), fallbackTimeout, Integer::parseInt);
statementType = options.statementType();
if (options.resultSetType() != ResultSetType.DEFAULT) {
resultSetType = options.resultSetType();
@@ -372,6 +377,19 @@ null, parameterTypeClass, resultMapId, getReturnType(method, type), resultSetTyp
});
}

private <T> T parseStringValue(String valueString, T fallbackValue, Function<String, T> valueTypeConverter) {
if (valueString.isEmpty()) {
return fallbackValue;
} else {
Properties variables = new Properties();
Optional.ofNullable(fallbackValue).map(String::valueOf)
.ifPresent(x -> variables.setProperty(TOKEN_PARSER.parse(valueString), x));
variables.putAll(configuration.getVariables());
return Optional.ofNullable(PropertyParser.parse(valueString, variables)).map(valueTypeConverter)
.orElse(fallbackValue);
}
}

private LanguageDriver getLanguageDriver(Method method) {
Lang lang = method.getAnnotation(Lang.class);
Class<? extends LanguageDriver> langClass = null;
2 changes: 1 addition & 1 deletion src/site/es/xdoc/java-api.xml
Original file line number Diff line number Diff line change
@@ -418,7 +418,7 @@ void rollback(boolean force)</source>
<td>
Esta anotación proporciona acceso a un gran conjunto de opciones de configuración que normalmente aparecen como atributos en los mapped statements.
En lugar de complicar cada anotación existente la anotación Options proporciona una forma sencilla y concisa de acceder a estas opciones.
Atributos: useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=DEFAULT, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty=“”, keyColumn=“”, resultSets=“”, databaseId="".
Atributos: useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=DEFAULT, statementType=PREPARED, fetchSize=-1, timeout=-1, timeoutString="", useGeneratedKeys=false, keyProperty=“”, keyColumn=“”, resultSets=“”, databaseId="".
Es importante comprender que las anotaciones en Java no permiten indicar un valor nulo.
Por lo tanto, cuando usas la anotación Options el statement usará todos los valores por defecto.
Presta atención a estos valores pro defecto para evitar comportamientos inesperados.
2 changes: 1 addition & 1 deletion src/site/ja/xdoc/java-api.xml
Original file line number Diff line number Diff line change
@@ -430,7 +430,7 @@ void rollback(boolean force)</source>
<td>マップドステートメントの属性</td>
<td>このアノテーションを使うと、通常マップドステートメントの属性として指定される多様なスイッチや設定オプションにアクセスすることができます。<code>Options</code> アノテーションによって、各ステートメントのアノテーションを複雑化することなく、一貫したクリーンな方法で設定にアクセスできるよう工夫されています。キー: Attributes:
<code>useCache=true</code>, <code>flushCache=FlushCachePolicy.DEFAULT</code>, <code>resultSetType=DEFAULT</code>,
<code>statementType=PREPARED</code>, <code>fetchSize=-1</code>, <code>timeout=-1</code>,
<code>statementType=PREPARED</code>, <code>fetchSize=-1</code>, <code>timeout=-1</code>, <code>timeoutString=""</code>,
<code>useGeneratedKeys=false</code>, <code>keyProperty=""</code>, <code>keyColumn=""</code>, <code>resultSets=""</code>,
<code>databaseId=""</code>.
Java アノテーションを使う場合、値として <code>null</code> を指定することはできないという制限があります。これはどういうことかというと、<code>Options</code> アノテーションを付加したステートメントにはデフォルトのオプションが適用されるということです。予期しない動作を防ぐため、各オプションのデフォルト値を把握しておくようにしてください。
1 change: 1 addition & 0 deletions src/site/ko/xdoc/java-api.xml
Original file line number Diff line number Diff line change
@@ -541,6 +541,7 @@ void rollback(boolean force)</source>
statementType=PREPARED,
fetchSize=-1,
timeout=-1,
timeoutString="",
useGeneratedKeys=false,
keyProperty=“”,
keyColumn=“”,
42 changes: 21 additions & 21 deletions src/site/markdown/java-api.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/site/zh/xdoc/java-api.xml
Original file line number Diff line number Diff line change
@@ -413,7 +413,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
<td><code>@Options</code></td>
<td><code>方法</code></td>
<td>映射语句的属性</td>
       <td>该注解允许你指定大部分开关和配置选项,它们通常在映射语句上作为属性出现。与在注解上提供大量的属性相比,<code>Options</code> 注解提供了一致、清晰的方式来指定选项。属性:<code>useCache=true</code>、<code>flushCache=FlushCachePolicy.DEFAULT</code>、<code>resultSetType=DEFAULT</code>、<code>statementType=PREPARED</code>、<code>fetchSize=-1</code>、<code>timeout=-1</code>、<code>useGeneratedKeys=false</code>、<code>keyProperty=""</code>、<code>keyColumn=""</code>、<code>resultSets=""</code>, <code>databaseId=""</code>。注意,Java 注解无法指定 <code>null</code> 值。因此,一旦你使用了 <code>Options</code> 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的非预期行为。
       <td>该注解允许你指定大部分开关和配置选项,它们通常在映射语句上作为属性出现。与在注解上提供大量的属性相比,<code>Options</code> 注解提供了一致、清晰的方式来指定选项。属性:<code>useCache=true</code>、<code>flushCache=FlushCachePolicy.DEFAULT</code>、<code>resultSetType=DEFAULT</code>、<code>statementType=PREPARED</code>、<code>fetchSize=-1</code>、<code>timeout=-1</code>、<code>timeoutString=""</code>、<code>useGeneratedKeys=false</code>、<code>keyProperty=""</code>、<code>keyColumn=""</code>、<code>resultSets=""</code>, <code>databaseId=""</code>。注意,Java 注解无法指定 <code>null</code> 值。因此,一旦你使用了 <code>Options</code> 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的非预期行为。
<code>databaseId</code>(3.5.5以上可用), 如果有一个配置好的 <code>DatabaseIdProvider</code>,
MyBatis 会加载不带 <code>databaseId</code> 属性和带有匹配当前数据库 <code>databaseId</code> 属性的所有语句。如果同时存在带 <code>databaseId</code> 和不带 <code>databaseId</code> 属性的相同语句,则后者会被舍弃。<br/><br/>

Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Properties;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Select;
@@ -25,6 +27,7 @@
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.session.Configuration;
import org.junit.jupiter.api.Test;

@@ -93,6 +96,26 @@ void withoutOptionsWhenNotSpecifyDefaultValue() {
assertThat(mappedStatement.getResultSetType()).isEqualTo(ResultSetType.DEFAULT);
}

@Test
void timeout() {
Configuration configuration = new Configuration();
Properties variables = new Properties();
variables.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
variables.setProperty("timeout.select1", "200");
configuration.setVariables(variables);
MapperAnnotationBuilder builder = new MapperAnnotationBuilder(configuration, TimeoutMapper.class);
builder.parse();

assertThat(configuration.getMappedStatement("selectWithTimeoutStringByNumber").getTimeout()).isEqualTo(10);
assertThat(configuration.getMappedStatement("selectWithTimeoutStringByVariable").getTimeout()).isEqualTo(200);
assertThat(configuration.getMappedStatement("selectWithTimeoutStringByVariableDefaultValue").getTimeout())
.isEqualTo(30);
assertThat(configuration.getMappedStatement("selectWithTimeoutAndTimeoutStringVariableFound").getTimeout())
.isEqualTo(200);
assertThat(configuration.getMappedStatement("selectWithTimeoutAndTimeoutStringVariableNotFound").getTimeout())
.isEqualTo(40);
}

interface Mapper {

@Insert("insert into test (name) values(#{name})")
@@ -112,4 +135,26 @@ interface Mapper {

}

interface TimeoutMapper {
@Select("select * from test")
@Options(timeoutString = "10")
String selectWithTimeoutStringByNumber(Integer id);

@Select("select * from test")
@Options(timeoutString = "${timeout.select1}")
String selectWithTimeoutStringByVariable(Integer id);

@Select("select * from test")
@Options(timeoutString = "${timeout.select2:30}")
String selectWithTimeoutStringByVariableDefaultValue(Integer id);

@Select("select * from test")
@Options(timeout = 20, timeoutString = "${timeout.select1}")
String selectWithTimeoutAndTimeoutStringVariableFound(Integer id);

@Select("select * from test")
@Options(timeout = 40, timeoutString = "${timeout.select3}")
String selectWithTimeoutAndTimeoutStringVariableNotFound(Integer id);
}

}