Skip to content

Commit 4dec29f

Browse files
committed
Add LanguageDriver to ProviderSqlSource
1 parent fa95122 commit 4dec29f

File tree

6 files changed

+201
-6
lines changed

6 files changed

+201
-6
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
@@ -484,7 +484,7 @@ private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterT
484484
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
485485
} else if (sqlProviderAnnotationType != null) {
486486
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
487-
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
487+
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method, languageDriver);
488488
}
489489
return null;
490490
} catch (Exception e) {

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
import java.util.Map;
2222

2323
import org.apache.ibatis.builder.BuilderException;
24-
import org.apache.ibatis.builder.SqlSourceBuilder;
2524
import org.apache.ibatis.mapping.BoundSql;
2625
import org.apache.ibatis.mapping.SqlSource;
2726
import org.apache.ibatis.parsing.PropertyParser;
2827
import org.apache.ibatis.reflection.ParamNameResolver;
28+
import org.apache.ibatis.scripting.LanguageDriver;
2929
import org.apache.ibatis.session.Configuration;
3030

3131
/**
@@ -35,8 +35,8 @@
3535
public class ProviderSqlSource implements SqlSource {
3636

3737
private final Configuration configuration;
38-
private final SqlSourceBuilder sqlSourceParser;
3938
private final Class<?> providerType;
39+
private final LanguageDriver languageDriver;
4040
private Method providerMethod;
4141
private String[] providerMethodArgumentNames;
4242
private Class<?>[] providerMethodParameterTypes;
@@ -55,10 +55,17 @@ public ProviderSqlSource(Configuration configuration, Object provider) {
5555
* @since 3.4.5
5656
*/
5757
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
58+
this(configuration, provider, mapperType, mapperMethod, configuration.getDefaultScriptingLanguageInstance());
59+
}
60+
61+
/**
62+
* @since 3.4.6
63+
*/
64+
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod, LanguageDriver languageDriver) {
5865
String providerMethodName;
5966
try {
6067
this.configuration = configuration;
61-
this.sqlSourceParser = new SqlSourceBuilder(configuration);
68+
this.languageDriver = languageDriver;
6269
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
6370
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
6471

@@ -126,7 +133,7 @@ private SqlSource createSqlSource(Object parameterObject) {
126133
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
127134
}
128135
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
129-
return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap<String, Object>());
136+
return languageDriver.createSqlSource(configuration, sql, parameterType);
130137
} catch (BuilderException e) {
131138
throw e;
132139
} catch (Exception e) {

src/test/java/org/apache/ibatis/submitted/sqlprovider/BaseMapper.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
*/
1616
package org.apache.ibatis.submitted.sqlprovider;
1717

18+
import org.apache.ibatis.annotations.Lang;
1819
import org.apache.ibatis.annotations.Param;
20+
import org.apache.ibatis.annotations.InsertProvider;
1921
import org.apache.ibatis.annotations.SelectProvider;
22+
import org.apache.ibatis.annotations.UpdateProvider;
23+
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
2024

2125
import java.lang.annotation.ElementType;
2226
import java.lang.annotation.Retention;
@@ -54,6 +58,16 @@ public interface BaseMapper<T> {
5458
@SelectProvider(type= OurSqlBuilder.class, method= "buildSelectByIdAndNameMultipleParamAndProviderContext")
5559
List<T> selectActiveByIdAndName(Integer id, String name);
5660

61+
@Lang(XMLLanguageDriver.class)
62+
@InsertProvider(type = OurSqlBuilder.class, method = "buildInsertSelective")
63+
void insertSelective(T entity);
64+
65+
@UpdateProvider(type= OurSqlBuilder.class, method= "buildUpdateSelective")
66+
void updateSelective(T entity);
67+
68+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetByEntityQuery")
69+
List<T> getByEntity(T entity);
70+
5771
@Retention(RetentionPolicy.RUNTIME)
5872
@Target(ElementType.METHOD)
5973
@interface ContainsLogicalDelete {
@@ -66,4 +80,10 @@ public interface BaseMapper<T> {
6680
String tableName();
6781
}
6882

83+
@Retention(RetentionPolicy.RUNTIME)
84+
@Target(ElementType.FIELD)
85+
@interface Column {
86+
String value() default "";
87+
}
88+
6989
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/OurSqlBuilder.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
import org.apache.ibatis.builder.annotation.ProviderContext;
2020
import org.apache.ibatis.jdbc.SQL;
2121

22+
import java.lang.reflect.Field;
23+
import java.lang.reflect.Method;
24+
import java.lang.reflect.ParameterizedType;
25+
import java.lang.reflect.Type;
26+
import java.util.LinkedHashMap;
2227
import java.util.List;
2328
import java.util.Map;
2429

@@ -213,4 +218,107 @@ public String buildSelectByIdAndNameMultipleParamAndProviderContext(final Intege
213218
}}.toString();
214219
}
215220

221+
private Class getEntityClass(ProviderContext providerContext){
222+
Method mapperMethod = providerContext.getMapperMethod();
223+
Class<?> declaringClass = mapperMethod.getDeclaringClass();
224+
Class<?> mapperClass = providerContext.getMapperType();
225+
226+
Type[] types = mapperClass.getGenericInterfaces();
227+
for (Type type : types) {
228+
if (type instanceof ParameterizedType) {
229+
ParameterizedType t = (ParameterizedType) type;
230+
if (t.getRawType() == declaringClass || mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
231+
Class<?> returnType = (Class<?>) t.getActualTypeArguments()[0];
232+
return returnType;
233+
}
234+
}
235+
}
236+
throw new RuntimeException("The interface ["
237+
+ mapperClass.getCanonicalName() + "] must specify a generic type.");
238+
}
239+
240+
private Map<String, String> getColumnMap(ProviderContext context){
241+
Class entityClass = getEntityClass(context);
242+
Field[] fields = entityClass.getDeclaredFields();
243+
Map<String, String> columnMap = new LinkedHashMap<String, String>();
244+
for (Field field : fields) {
245+
BaseMapper.Column column = field.getAnnotation(BaseMapper.Column.class);
246+
if(column != null){
247+
String columnName = column.value();
248+
if(columnName == null || columnName.length() == 0){
249+
columnName = field.getName();
250+
}
251+
columnMap.put(columnName, field.getName());
252+
}
253+
}
254+
if(columnMap.size() == 0){
255+
throw new RuntimeException("There is no field in the class ["
256+
+ entityClass.getCanonicalName() + "] that specifies the @BaseMapper.Column annotation.");
257+
}
258+
return columnMap;
259+
}
260+
261+
public String buildInsertSelective(ProviderContext context) {
262+
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
263+
Map<String, String> columnMap = getColumnMap(context);
264+
StringBuffer sqlBuffer = new StringBuffer();
265+
sqlBuffer.append("<script>");
266+
sqlBuffer.append("insert into ");
267+
sqlBuffer.append(tableName);
268+
sqlBuffer.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
269+
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
270+
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
271+
sqlBuffer.append(entry.getKey()).append(",");
272+
sqlBuffer.append("</if>");
273+
}
274+
sqlBuffer.append("</trim>");
275+
sqlBuffer.append("<trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
276+
for (String field : columnMap.values()) {
277+
sqlBuffer.append("<if test=\"").append(field).append(" != null\">");
278+
sqlBuffer.append("#{").append(field).append("} ,");
279+
sqlBuffer.append("</if>");
280+
}
281+
sqlBuffer.append("</trim>");
282+
sqlBuffer.append("</script>");
283+
return sqlBuffer.toString();
284+
}
285+
286+
public String buildUpdateSelective(ProviderContext context) {
287+
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
288+
Map<String, String> columnMap = getColumnMap(context);
289+
StringBuffer sqlBuffer = new StringBuffer();
290+
sqlBuffer.append("<script>");
291+
sqlBuffer.append("update ");
292+
sqlBuffer.append(tableName);
293+
sqlBuffer.append("<set>");
294+
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
295+
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
296+
sqlBuffer.append(entry.getKey()).append(" = #{").append(entry.getValue()).append("} ,");
297+
sqlBuffer.append("</if>");
298+
}
299+
sqlBuffer.append("</set>");
300+
//For simplicity, there is no @Id annotation here, using default id directly
301+
sqlBuffer.append("where id = #{id}");
302+
sqlBuffer.append("</script>");
303+
return sqlBuffer.toString();
304+
}
305+
306+
public String buildGetByEntityQuery(ProviderContext context) {
307+
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
308+
Map<String, String> columnMap = getColumnMap(context);
309+
StringBuffer sqlBuffer = new StringBuffer();
310+
sqlBuffer.append("<script>");
311+
sqlBuffer.append("select * from ");
312+
sqlBuffer.append(tableName);
313+
sqlBuffer.append("<where>");
314+
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
315+
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
316+
sqlBuffer.append("and ").append(entry.getKey()).append(" = #{").append(entry.getValue()).append("}");
317+
sqlBuffer.append("</if>");
318+
}
319+
sqlBuffer.append("</where>");
320+
sqlBuffer.append("</script>");
321+
return sqlBuffer.toString();
322+
}
323+
216324
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/SqlProviderTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,4 +538,63 @@ public static String oneArgumentAndProviderContext(Integer value, ProviderContex
538538

539539
}
540540

541+
@Test
542+
public void shouldInsertUserSelective() {
543+
SqlSession sqlSession = sqlSessionFactory.openSession();
544+
try {
545+
Mapper mapper = sqlSession.getMapper(Mapper.class);
546+
User user = new User();
547+
user.setId(999);
548+
mapper.insertSelective(user);
549+
550+
User loadedUser = mapper.getUser(999);
551+
assertEquals(null, loadedUser.getName());
552+
553+
} finally {
554+
sqlSession.close();
555+
}
556+
}
557+
558+
559+
@Test
560+
public void shouldUpdateUserSelective() {
561+
SqlSession sqlSession = sqlSessionFactory.openSession();
562+
try {
563+
Mapper mapper = sqlSession.getMapper(Mapper.class);
564+
User user = new User();
565+
user.setId(999);
566+
user.setName("MyBatis");
567+
mapper.insert(user);
568+
569+
user.setName(null);
570+
mapper.updateSelective(user);
571+
572+
User loadedUser = mapper.getUser(999);
573+
assertEquals("MyBatis", loadedUser.getName());
574+
575+
} finally {
576+
sqlSession.close();
577+
}
578+
}
579+
580+
@Test
581+
public void mapperGetByEntity() {
582+
SqlSession sqlSession = sqlSessionFactory.openSession();
583+
try {
584+
Mapper mapper = sqlSession.getMapper(Mapper.class);
585+
User query = new User();
586+
query.setName("User4");
587+
assertEquals(1, mapper.getByEntity(query).size());
588+
query = new User();
589+
query.setId(1);
590+
assertEquals(1, mapper.getByEntity(query).size());
591+
query = new User();
592+
query.setId(1);
593+
query.setName("User4");
594+
assertEquals(0, mapper.getByEntity(query).size());
595+
} finally {
596+
sqlSession.close();
597+
}
598+
}
599+
541600
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/User.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@
1616
package org.apache.ibatis.submitted.sqlprovider;
1717

1818
public class User {
19-
19+
@BaseMapper.Column
2020
private Integer id;
21+
@BaseMapper.Column
2122
private String name;
2223

2324
public Integer getId() {

0 commit comments

Comments
 (0)