Skip to content

Commit 45712f3

Browse files
authored
Merge pull request #1055 from kazuki43zoo/gh-1013_sqlprovider
Support ProviderContext on sql provider method
2 parents 6f9105e + eeed7a4 commit 45712f3

File tree

13 files changed

+356
-29
lines changed

13 files changed

+356
-29
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: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,22 @@ public class ProviderSqlSource implements SqlSource {
3636
private final Class<?> providerType;
3737
private Method providerMethod;
3838
private String[] providerMethodArgumentNames;
39+
private Class<?>[] providerMethodParameterTypes;
40+
private ProviderContext providerContext;
41+
private Integer providerContextIndex;
3942

43+
/**
44+
* @deprecated Please use the {@link #ProviderSqlSource(Configuration, Object, Class, Method)} instead of this.
45+
*/
46+
@Deprecated
4047
public ProviderSqlSource(Configuration config, Object provider) {
48+
this(config, provider, null, null);
49+
}
50+
51+
/**
52+
* @since 3.4.5
53+
*/
54+
public ProviderSqlSource(Configuration config, Object provider, Class<?> mapperType, Method mapperMethod) {
4155
String providerMethodName;
4256
try {
4357
this.sqlSourceParser = new SqlSourceBuilder(config);
@@ -54,6 +68,7 @@ public ProviderSqlSource(Configuration config, Object provider) {
5468
}
5569
this.providerMethod = m;
5670
this.providerMethodArgumentNames = new ParamNameResolver(config, m).getNames();
71+
this.providerMethodParameterTypes = m.getParameterTypes();
5772
}
5873
}
5974
}
@@ -66,6 +81,18 @@ public ProviderSqlSource(Configuration config, Object provider) {
6681
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
6782
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
6883
}
84+
for (int i = 0; i< this.providerMethodParameterTypes.length; i++) {
85+
Class<?> parameterType = this.providerMethodParameterTypes[i];
86+
if (parameterType == ProviderContext.class) {
87+
if (this.providerContext != null){
88+
throw new BuilderException("Error creating SqlSource for SqlProvider. ProviderContext found multiple in SqlProvider method ("
89+
+ this.providerType.getName() + "." + providerMethod.getName()
90+
+ "). ProviderContext can not define multiple in SqlProvider method argument.");
91+
}
92+
this.providerContext = new ProviderContext(mapperType, mapperMethod);
93+
this.providerContextIndex = i;
94+
}
95+
}
6996
}
7097

7198
@Override
@@ -77,12 +104,15 @@ public BoundSql getBoundSql(Object parameterObject) {
77104
private SqlSource createSqlSource(Object parameterObject) {
78105
try {
79106
Class<?>[] parameterTypes = providerMethod.getParameterTypes();
107+
int bindParameterCount = parameterTypes.length - (providerContext == null ? 0 : 1);
80108
String sql;
81109
if (parameterTypes.length == 0) {
82110
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);
111+
} else if (bindParameterCount == 0) {
112+
sql = (String) providerMethod.invoke(providerType.newInstance(), providerContext);
113+
} else if (bindParameterCount == 1 &&
114+
(parameterObject == null || parameterTypes[(providerContextIndex == null || providerContextIndex == 1) ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
115+
sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(parameterObject));
86116
} else if (parameterObject instanceof Map) {
87117
@SuppressWarnings("unchecked")
88118
Map<String, Object> params = (Map<String, Object>) parameterObject;
@@ -91,7 +121,7 @@ private SqlSource createSqlSource(Object parameterObject) {
91121
throw new BuilderException("Error invoking SqlProvider method ("
92122
+ providerType.getName() + "." + providerMethod.getName()
93123
+ "). Cannot invoke a method that holds "
94-
+ (parameterTypes.length == 1 ? "named argument(@Param)": "multiple arguments")
124+
+ (bindParameterCount == 1 ? "named argument(@Param)": "multiple arguments")
95125
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
96126
}
97127
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
@@ -105,10 +135,25 @@ private SqlSource createSqlSource(Object parameterObject) {
105135
}
106136
}
107137

138+
private Object[] extractProviderMethodArguments(Object parameterObject) {
139+
if (providerContext != null) {
140+
Object[] args = new Object[2];
141+
args[providerContextIndex == 0 ? 1 : 0] = parameterObject;
142+
args[providerContextIndex] = providerContext;
143+
return args;
144+
} else {
145+
return new Object[] { parameterObject };
146+
}
147+
}
148+
108149
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
109150
Object[] args = new Object[argumentNames.length];
110151
for (int i = 0; i < args.length; i++) {
111-
args[i] = params.get(argumentNames[i]);
152+
if (providerContextIndex != null && providerContextIndex == i) {
153+
args[i] = providerContext;
154+
} else {
155+
args[i] = params.get(argumentNames[i]);
156+
}
112157
}
113158
return args;
114159
}

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+
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/CreateDB.sql

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--
2-
-- Copyright 2009-2016 the original author or authors.
2+
-- Copyright 2009-2017 the original author or authors.
33
--
44
-- Licensed under the Apache License, Version 2.0 (the "License");
55
-- you may not use this file except in compliance with the License.
@@ -18,11 +18,12 @@ drop table users if exists;
1818

1919
create table users (
2020
id int,
21-
name varchar(20)
21+
name varchar(20),
22+
logical_delete boolean default false
2223
);
2324

2425
insert into users (id, name) values(1, 'User1');
2526
insert into users (id, name) values(2, 'User2');
2627
insert into users (id, name) values(3, 'User3');
27-
insert into users (id, name) values(4, 'User4');
28+
insert into users (id, name, logical_delete) values(4, 'User4', true);
2829

0 commit comments

Comments
 (0)