Skip to content
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

Add LanguageDriver to ProviderSqlSource #1391

Merged
merged 9 commits into from
Mar 19, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterT
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
} else if (sqlProviderAnnotationType != null) {
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method, languageDriver);
}
return null;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
import java.util.Map;

import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.PropertyParser;
import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
Expand All @@ -35,8 +35,8 @@
public class ProviderSqlSource implements SqlSource {

private final Configuration configuration;
private final SqlSourceBuilder sqlSourceParser;
private final Class<?> providerType;
private final LanguageDriver languageDriver;
private Method providerMethod;
private String[] providerMethodArgumentNames;
private Class<?>[] providerMethodParameterTypes;
Expand All @@ -55,10 +55,17 @@ public ProviderSqlSource(Configuration configuration, Object provider) {
* @since 3.4.5
*/
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
this(configuration, provider, mapperType, mapperMethod, configuration.getDefaultScriptingLanguageInstance());
}

/**
* @since 3.4.6
*/
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod, LanguageDriver languageDriver) {
harawata marked this conversation as resolved.
Show resolved Hide resolved
String providerMethodName;
try {
this.configuration = configuration;
this.sqlSourceParser = new SqlSourceBuilder(configuration);
this.languageDriver = languageDriver;
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);

Expand Down Expand Up @@ -126,7 +133,7 @@ private SqlSource createSqlSource(Object parameterObject) {
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
}
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
return sqlSourceParser.parse(replacePlaceholder(sql), parameterType, new HashMap<>());
kazuki43zoo marked this conversation as resolved.
Show resolved Hide resolved
return languageDriver.createSqlSource(configuration, sql, parameterType);
} catch (BuilderException e) {
throw e;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@
*/
package org.apache.ibatis.submitted.sqlprovider;

import org.apache.ibatis.annotations.Lang;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.annotations.UpdateProvider;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;

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

@Lang(XMLLanguageDriver.class)
@InsertProvider(type = OurSqlBuilder.class, method = "buildInsertSelective")
void insertSelective(T entity);

@UpdateProvider(type= OurSqlBuilder.class, method= "buildUpdateSelective")
void updateSelective(T entity);

@SelectProvider(type = OurSqlBuilder.class, method = "buildGetByEntityQuery")
List<T> getByEntity(T entity);

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ContainsLogicalDelete {
Expand All @@ -66,4 +80,10 @@ public interface BaseMapper<T> {
String tableName();
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String value() default "";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.jdbc.SQL;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -214,4 +219,107 @@ public String buildSelectByIdAndNameMultipleParamAndProviderContext(final Intege
}}.toString();
}

private Class getEntityClass(ProviderContext providerContext){
Method mapperMethod = providerContext.getMapperMethod();
Class<?> declaringClass = mapperMethod.getDeclaringClass();
Class<?> mapperClass = providerContext.getMapperType();

Type[] types = mapperClass.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType) type;
if (t.getRawType() == declaringClass || mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
Class<?> returnType = (Class<?>) t.getActualTypeArguments()[0];
return returnType;
}
}
}
throw new RuntimeException("The interface ["
+ mapperClass.getCanonicalName() + "] must specify a generic type.");
}

private Map<String, String> getColumnMap(ProviderContext context){
Class entityClass = getEntityClass(context);
Field[] fields = entityClass.getDeclaredFields();
Map<String, String> columnMap = new LinkedHashMap<String, String>();
for (Field field : fields) {
BaseMapper.Column column = field.getAnnotation(BaseMapper.Column.class);
if(column != null){
harawata marked this conversation as resolved.
Show resolved Hide resolved
String columnName = column.value();
if(columnName == null || columnName.length() == 0){
columnName = field.getName();
}
columnMap.put(columnName, field.getName());
}
}
if(columnMap.size() == 0){
throw new RuntimeException("There is no field in the class ["
+ entityClass.getCanonicalName() + "] that specifies the @BaseMapper.Column annotation.");
}
return columnMap;
}

public String buildInsertSelective(ProviderContext context) {
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
Map<String, String> columnMap = getColumnMap(context);
StringBuffer sqlBuffer = new StringBuffer();
harawata marked this conversation as resolved.
Show resolved Hide resolved
sqlBuffer.append("<script>");
sqlBuffer.append("insert into ");
sqlBuffer.append(tableName);
sqlBuffer.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
sqlBuffer.append(entry.getKey()).append(",");
sqlBuffer.append("</if>");
}
sqlBuffer.append("</trim>");
sqlBuffer.append("<trim prefix=\"VALUES (\" suffix=\")\" suffixOverrides=\",\">");
for (String field : columnMap.values()) {
sqlBuffer.append("<if test=\"").append(field).append(" != null\">");
sqlBuffer.append("#{").append(field).append("} ,");
sqlBuffer.append("</if>");
}
sqlBuffer.append("</trim>");
sqlBuffer.append("</script>");
return sqlBuffer.toString();
}

public String buildUpdateSelective(ProviderContext context) {
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
Map<String, String> columnMap = getColumnMap(context);
StringBuffer sqlBuffer = new StringBuffer();
harawata marked this conversation as resolved.
Show resolved Hide resolved
sqlBuffer.append("<script>");
sqlBuffer.append("update ");
sqlBuffer.append(tableName);
sqlBuffer.append("<set>");
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
sqlBuffer.append(entry.getKey()).append(" = #{").append(entry.getValue()).append("} ,");
sqlBuffer.append("</if>");
}
sqlBuffer.append("</set>");
//For simplicity, there is no @Id annotation here, using default id directly
sqlBuffer.append("where id = #{id}");
sqlBuffer.append("</script>");
return sqlBuffer.toString();
}

public String buildGetByEntityQuery(ProviderContext context) {
final String tableName = context.getMapperType().getAnnotation(BaseMapper.Meta.class).tableName();
Map<String, String> columnMap = getColumnMap(context);
StringBuffer sqlBuffer = new StringBuffer();
sqlBuffer.append("<script>");
sqlBuffer.append("select * from ");
sqlBuffer.append(tableName);
sqlBuffer.append("<where>");
for (Map.Entry<String, String> entry : columnMap.entrySet()) {
sqlBuffer.append("<if test=\"").append(entry.getValue()).append(" != null\">");
sqlBuffer.append("and ").append(entry.getKey()).append(" = #{").append(entry.getValue()).append("}");
sqlBuffer.append("</if>");
}
sqlBuffer.append("</where>");
sqlBuffer.append("</script>");
return sqlBuffer.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -538,4 +538,63 @@ public static String oneArgumentAndProviderContext(Integer value, ProviderContex

}

@Test
public void shouldInsertUserSelective() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
User user = new User();
user.setId(999);
mapper.insertSelective(user);

User loadedUser = mapper.getUser(999);
assertEquals(null, loadedUser.getName());

} finally {
sqlSession.close();
}
}


@Test
public void shouldUpdateUserSelective() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
User user = new User();
user.setId(999);
user.setName("MyBatis");
mapper.insert(user);

user.setName(null);
mapper.updateSelective(user);

User loadedUser = mapper.getUser(999);
assertEquals("MyBatis", loadedUser.getName());

} finally {
sqlSession.close();
}
}

@Test
public void mapperGetByEntity() {
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
Mapper mapper = sqlSession.getMapper(Mapper.class);
User query = new User();
query.setName("User4");
assertEquals(1, mapper.getByEntity(query).size());
query = new User();
query.setId(1);
assertEquals(1, mapper.getByEntity(query).size());
query = new User();
query.setId(1);
query.setName("User4");
assertEquals(0, mapper.getByEntity(query).size());
} finally {
sqlSession.close();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
package org.apache.ibatis.submitted.sqlprovider;

public class User {

@BaseMapper.Column
private Integer id;
@BaseMapper.Column
private String name;

public Integer getId() {
Expand Down