Skip to content

Commit eac0d3b

Browse files
committed
Merge branch 'officialmaster'
2 parents a7508c1 + 1af8f68 commit eac0d3b

File tree

15 files changed

+375
-36
lines changed

15 files changed

+375
-36
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterT
465465
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
466466
} else if (sqlProviderAnnotationType != null) {
467467
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
468-
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
468+
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
469469
}
470470
return null;
471471
} catch (Exception e) {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2009-2017 the original author or authors.
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, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.builder.annotation;
17+
18+
import java.lang.reflect.Method;
19+
20+
/**
21+
* The context object for sql provider method.
22+
*
23+
* @author Kazuki Shimizu
24+
* @since 3.4.5
25+
*/
26+
public final class ProviderContext {
27+
28+
private final Class<?> mapperType;
29+
private final Method mapperMethod;
30+
31+
/**
32+
* Constructor.
33+
*
34+
* @param mapperType A mapper interface type that specified provider
35+
* @param mapperMethod A mapper method that specified provider
36+
*/
37+
ProviderContext(Class<?> mapperType, Method mapperMethod) {
38+
this.mapperType = mapperType;
39+
this.mapperMethod = mapperMethod;
40+
}
41+
42+
/**
43+
* Get a mapper interface type that specified provider.
44+
*
45+
* @return A mapper interface type that specified provider
46+
*/
47+
public Class<?> getMapperType() {
48+
return mapperType;
49+
}
50+
51+
/**
52+
* Get a mapper method that specified provider.
53+
*
54+
* @return A mapper method that specified provider
55+
*/
56+
public Method getMapperMethod() {
57+
return mapperMethod;
58+
}
59+
60+
}

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

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.ibatis.builder.SqlSourceBuilder;
2424
import org.apache.ibatis.mapping.BoundSql;
2525
import org.apache.ibatis.mapping.SqlSource;
26+
import org.apache.ibatis.parsing.PropertyParser;
2627
import org.apache.ibatis.reflection.ParamNameResolver;
2728
import org.apache.ibatis.session.Configuration;
2829

@@ -32,15 +33,31 @@
3233
*/
3334
public class ProviderSqlSource implements SqlSource {
3435

36+
private final Configuration configuration;
3537
private final SqlSourceBuilder sqlSourceParser;
3638
private final Class<?> providerType;
3739
private Method providerMethod;
3840
private String[] providerMethodArgumentNames;
41+
private Class<?>[] providerMethodParameterTypes;
42+
private ProviderContext providerContext;
43+
private Integer providerContextIndex;
3944

40-
public ProviderSqlSource(Configuration config, Object provider) {
45+
/**
46+
* @deprecated Please use the {@link #ProviderSqlSource(Configuration, Object, Class, Method)} instead of this.
47+
*/
48+
@Deprecated
49+
public ProviderSqlSource(Configuration configuration, Object provider) {
50+
this(configuration, provider, null, null);
51+
}
52+
53+
/**
54+
* @since 3.4.5
55+
*/
56+
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
4157
String providerMethodName;
4258
try {
43-
this.sqlSourceParser = new SqlSourceBuilder(config);
59+
this.configuration = configuration;
60+
this.sqlSourceParser = new SqlSourceBuilder(configuration);
4461
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
4562
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
4663

@@ -53,7 +70,8 @@ public ProviderSqlSource(Configuration config, Object provider) {
5370
+ "'. Sql provider method can not overload.");
5471
}
5572
this.providerMethod = m;
56-
this.providerMethodArgumentNames = new ParamNameResolver(config, m).getNames();
73+
this.providerMethodArgumentNames = new ParamNameResolver(configuration, m).getNames();
74+
this.providerMethodParameterTypes = m.getParameterTypes();
5775
}
5876
}
5977
}
@@ -66,6 +84,18 @@ public ProviderSqlSource(Configuration config, Object provider) {
6684
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
6785
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
6886
}
87+
for (int i = 0; i< this.providerMethodParameterTypes.length; i++) {
88+
Class<?> parameterType = this.providerMethodParameterTypes[i];
89+
if (parameterType == ProviderContext.class) {
90+
if (this.providerContext != null){
91+
throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method ("
92+
+ this.providerType.getName() + "." + providerMethod.getName()
93+
+ "). ProviderContext can not define multiple in SqlProvider method argument.");
94+
}
95+
this.providerContext = new ProviderContext(mapperType, mapperMethod);
96+
this.providerContextIndex = i;
97+
}
98+
}
6999
}
70100

71101
@Override
@@ -77,12 +107,15 @@ public BoundSql getBoundSql(Object parameterObject) {
77107
private SqlSource createSqlSource(Object parameterObject) {
78108
try {
79109
Class<?>[] parameterTypes = providerMethod.getParameterTypes();
110+
int bindParameterCount = parameterTypes.length - (providerContext == null ? 0 : 1);
80111
String sql;
81112
if (parameterTypes.length == 0) {
82113
sql = (String) providerMethod.invoke(providerType.newInstance());
83-
} else if (parameterTypes.length == 1 &&
84-
(parameterObject == null || parameterTypes[0].isAssignableFrom(parameterObject.getClass()))) {
85-
sql = (String) providerMethod.invoke(providerType.newInstance(), parameterObject);
114+
} else if (bindParameterCount == 0) {
115+
sql = (String) providerMethod.invoke(providerType.newInstance(), providerContext);
116+
} else if (bindParameterCount == 1 &&
117+
(parameterObject == null || parameterTypes[(providerContextIndex == null || providerContextIndex == 1) ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
118+
sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(parameterObject));
86119
} else if (parameterObject instanceof Map) {
87120
@SuppressWarnings("unchecked")
88121
Map<String, Object> params = (Map<String, Object>) parameterObject;
@@ -91,11 +124,11 @@ private SqlSource createSqlSource(Object parameterObject) {
91124
throw new BuilderException("Error invoking SqlProvider method ("
92125
+ providerType.getName() + "." + providerMethod.getName()
93126
+ "). Cannot invoke a method that holds "
94-
+ (parameterTypes.length == 1 ? "named argument(@Param)": "multiple arguments")
127+
+ (bindParameterCount == 1 ? "named argument(@Param)": "multiple arguments")
95128
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
96129
}
97130
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
98-
return sqlSourceParser.parse(sql, parameterType, new HashMap<String, Object>());
131+
return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap<String, Object>());
99132
} catch (BuilderException e) {
100133
throw e;
101134
} catch (Exception e) {
@@ -105,12 +138,31 @@ private SqlSource createSqlSource(Object parameterObject) {
105138
}
106139
}
107140

141+
private Object[] extractProviderMethodArguments(Object parameterObject) {
142+
if (providerContext != null) {
143+
Object[] args = new Object[2];
144+
args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
145+
args[providerContextIndex] = providerContext;
146+
return args;
147+
} else {
148+
return new Object[] { parameterObject };
149+
}
150+
}
151+
108152
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
109153
Object[] args = new Object[argumentNames.length];
110154
for (int i = 0; i < args.length; i++) {
111-
args[i] = params.get(argumentNames[i]);
155+
if (providerContextIndex != null && providerContextIndex == i) {
156+
args[i] = providerContext;
157+
} else {
158+
args[i] = params.get(argumentNames[i]);
159+
}
112160
}
113161
return args;
114162
}
115163

164+
private String replacePlaceholder(String sql) {
165+
return PropertyParser.parse(sql, configuration.getVariables());
166+
}
167+
116168
}

src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ public String parse(String text) {
3434
if (text == null || text.isEmpty()) {
3535
return "";
3636
}
37-
char[] src = text.toCharArray();
38-
int offset = 0;
3937
// search open token
40-
int start = text.indexOf(openToken, offset);
38+
int start = text.indexOf(openToken, 0);
4139
if (start == -1) {
4240
return text;
4341
}
42+
char[] src = text.toCharArray();
43+
int offset = 0;
4444
final StringBuilder builder = new StringBuilder();
4545
StringBuilder expression = null;
4646
while (start > -1) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2009-2016 the original author or authors.
4+
Copyright 2009-2017 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -445,7 +445,8 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
445445
<li><code>&lt;select&gt;</code></li>
446446
</ul>
447447
</td>
448-
<td>Estas anotaciones SQL alternativas te permiten especificar un nombre de clases y un método que devolverán la SQL que debe ejecutarse. Cuando se ejecute el método MyBatis instanciará la clase y ejecutará el método especificados en el provider. El método puede opcionalmente recibir el objeto parámetro.(In MyBatis 3.4 or later, it's allow multiple parameters) Atributos: type, method. El atributo type es el nombre completamente cualificado de una clase. El method es el nombre un método de dicha clase. Nota: A continuación hay una sección sobre la clase, que puede ayudar a construir SQL dinámico de una forma más clara y sencilla de leer.</td>
448+
<td>Estas anotaciones SQL alternativas te permiten especificar un nombre de clases y un método que devolverán la SQL que debe ejecutarse. Cuando se ejecute el método MyBatis instanciará la clase y ejecutará el método especificados en el provider. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method"
449+
via the <code>ProviderContext</code>(available since MyBatis 3.4.5 or later) as method argument.(In MyBatis 3.4 or later, it's allow multiple parameters) Atributos: type, method. El atributo type es el nombre completamente cualificado de una clase. El method es el nombre un método de dicha clase. Nota: A continuación hay una sección sobre la clase, que puede ayudar a construir SQL dinámico de una forma más clara y sencilla de leer.</td>
449450
</tr>
450451
<tr>
451452
<td><code>@Param</code></td>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2009-2016 the original author or authors.
4+
Copyright 2009-2017 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -465,7 +465,7 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
465465
</ul>
466466
</td>
467467
<td>これらのアノテーションは動的 SQL を生成するためのものです。実行時に指定されたメソッドが呼び出され、メソッドから返された SQL ステートメントが実行されます。マップドステートメントを実行する際、プロバイダーによって指定したクラスのインスタンスが作成され、指定されたメソッドが実行されます。
468-
メソッドに引数を渡すことができます。(MyBatis 3.4以降では、複数の引数を渡すことができます)
468+
なお、メソッド引数にはMapperメソッドの引数に渡したオブジェクトに加え、<code>ProviderContext</code>(MyBatis 3.4.5以降で利用可能)を介して「Mapperインタフェースの型」と「Mapperメソッド」を渡すことができます。(MyBatis 3.4以降では、複数の引数を渡すことができます)
469469
キー: <code>type</code>, <code>method</code>. <code>type</code> にはクラスオブジェクト、<code>method</code> にはメソッド名を指定します。 <span class="label important">NOTE</span> 次の章で、クリーンで可読性の高いコードで動的 SQL を構築するためのクラスについて説明します。
470470
</td>
471471
</tr>

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2009-2016 the original author or authors.
4+
Copyright 2009-2017 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -580,8 +580,9 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
580580
</ul>
581581
</td>
582582
<td>실행시 SQL 을 리턴할 클래스 과 메소드명을 명시하도록 해주는 대체수단의 애노테이션이다.
583-
매핑된 구문을 실행할 때 마이바티스는 클래스의 인스턴스를 만들고 메소드를 실행한다.
584-
메소드는 파라미터 객체를 받을 수도 있다.(마이바티스 3.4이상에서는 복수 파라미터를 허용한다.)
583+
매핑된 구문을 실행할 때 마이바티스는 클래스의 인스턴스를 만들고 메소드를 실행한다.
584+
You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method"
585+
via the <code>ProviderContext</code>(available since MyBatis 3.4.5 or later) as method argument.(마이바티스 3.4이상에서는 복수 파라미터를 허용한다.)
585586
사용가능한 속성들 : type, method.
586587
type 속성은 클래스.
587588
method 속성은 메소드명이다.

src/site/xdoc/java-api.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2009-2016 the original author or authors.
4+
Copyright 2009-2017 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -498,7 +498,9 @@ try (SqlSession session = sqlSessionFactory.openSession()) {
498498
<td>Allows for creation of dynamic SQL. These alternative SQL annotations allow you to specify a class and
499499
a method name that will return the SQL to run at execution time. Upon executing the mapped statement, MyBatis will
500500
instantiate the class, and execute the method, as specified by the provider.
501-
The method can optionally accept parameter objects.(In MyBatis 3.4 or later, it's allow multiple parameters)
501+
You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method"
502+
via the <code>ProviderContext</code>(available since MyBatis 3.4.5 or later) as method argument.
503+
(In MyBatis 3.4 or later, it's allow multiple parameters)
502504
Attributes: <code>type</code>, <code>method</code>. The <code>type</code> attribute is a class.
503505
The <code>method</code> is the name of the method on that class. <span class="label important">NOTE</span>
504506
Following this section is a discussion about the class, which can help build dynamic SQL in a cleaner, easier to read way.</td>

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
33
4-
Copyright 2009-2016 the original author or authors.
4+
Copyright 2009-2017 the original author or authors.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -752,7 +752,9 @@ resultSets=””。
752752
MyBatis
753753
会实例化这个类,然后执行由 provider
754754
指定的方法.
755-
该方法可以有选择地接受参数对象.(In MyBatis 3.4 or later, it's allow multiple parameters)
755+
You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method"
756+
via the <code>ProviderContext</code>(available since MyBatis 3.4.5 or later) as method argument.
757+
(In MyBatis 3.4 or later, it's allow multiple parameters)
756758
属性: type,method。type 属性是类。method 属性是方法名。
757759
注意:
758760
这节之后是对 类的
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Copyright 2009-2017 the original author or authors.
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, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.sqlprovider;
17+
18+
import org.apache.ibatis.annotations.Param;
19+
import org.apache.ibatis.annotations.SelectProvider;
20+
21+
import java.lang.annotation.ElementType;
22+
import java.lang.annotation.Retention;
23+
import java.lang.annotation.RetentionPolicy;
24+
import java.lang.annotation.Target;
25+
import java.util.List;
26+
27+
public interface BaseMapper<T> {
28+
29+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdProviderContextOnly")
30+
@ContainsLogicalDelete
31+
T selectById(Integer id);
32+
33+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdProviderContextOnly")
34+
T selectActiveById(Integer id);
35+
36+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByNameOneParamAndProviderContext")
37+
@ContainsLogicalDelete
38+
List<T> selectByName(String name);
39+
40+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByNameOneParamAndProviderContext")
41+
List<T> selectActiveByName(String name);
42+
43+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdAndNameMultipleParamAndProviderContextWithAtParam")
44+
@ContainsLogicalDelete
45+
List<T> selectByIdAndNameWithAtParam(@Param("id") Integer id, @Param("name") String name);
46+
47+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdAndNameMultipleParamAndProviderContextWithAtParam")
48+
List<T> selectActiveByIdAndNameWithAtParam(@Param("id") Integer id, @Param("name") String name);
49+
50+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdAndNameMultipleParamAndProviderContext")
51+
@ContainsLogicalDelete
52+
List<T> selectByIdAndName(Integer id, String name);
53+
54+
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdAndNameMultipleParamAndProviderContext")
55+
List<T> selectActiveByIdAndName(Integer id, String name);
56+
57+
@Retention(RetentionPolicy.RUNTIME)
58+
@Target(ElementType.METHOD)
59+
@interface ContainsLogicalDelete {
60+
boolean value() default false;
61+
}
62+
63+
@Retention(RetentionPolicy.RUNTIME)
64+
@Target(ElementType.TYPE)
65+
@interface Meta {
66+
String tableName();
67+
}
68+
69+
}

0 commit comments

Comments
 (0)