Mybatis使用Map<String, Object>作为statement方法的参数
看到有人吐槽mybatis难用,修改了DB的字段,或者是实体类的字段。都需要去修改xml映射文件。极其麻烦。抛开很多优秀的插件可以解决这个问题不说,其实原生MyBatis其实也是可以解决这个问题。那就是使用Map<String, Object>
作为statement方法的参数
使用 Map<String, Object> 参数 Insert
实体类
import java.time.LocalDateTime;
public class FooEntity {
private Integer id;
private String name;
private LocalDateTime createDate;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDateTime getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDateTime createDate) {
this.createDate = createDate;
}
}
Mapper接口
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface FooMapper {
int insert(Map<String, Object> condition);
}
Mapper映射文件
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO `foo`
<foreach collection="condition.entrySet()" index="key" item="value" open="(" separator="," close=")">
<if test="key != null and value != null">`${key}`</if>
</foreach>
<foreach collection="condition.entrySet()" index="key" item="value" open=" VALUES(" separator="," close=")">
<if test="key != null and value != null">#{value}</if>
</foreach>
</insert>
BeanUtils ,通过内省把 JavaBean 转换为Map的工具类
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class BeanUtils {
/**
* JavaBean To Map
* @param bean
* @param humpToUnderline 驼峰转换为下划线
* @return
*/
public static Map<String, Object> beanToMap(Object bean, boolean humpToUnderline){
Objects.requireNonNull(bean);
Map<String, Object> retVal = new HashMap<>();
try {
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
Method method = propertyDescriptor.getReadMethod();
if(Modifier.isNative(method.getModifiers())) {
// 忽略 getClass();
continue;
}
String name = propertyDescriptor.getName();
Object val = method.invoke(bean);
retVal.put(humpToUnderline ? humpToUnderline(name) : name, val);
}
} catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
return retVal;
}
// 驼峰转换为下划线
private static String humpToUnderline(String value) {
StringBuilder stringBuilder = new StringBuilder();
char[] chars = value.toCharArray();
for (char charactor : chars) {
if (Character.isUpperCase(charactor)) {
stringBuilder.append("_");
charactor = Character.toLowerCase(charactor);
}
stringBuilder.append(charactor);
}
return stringBuilder.toString();
}
}
Test
@Autowired
private FooMapper fooMapper;
@Test
public void test(){
FooEntity fooEntity = new FooEntity();
fooEntity.setName("SpringBoot中文社区");
fooEntity.setCreateDate(LocalDateTime.now());
Map<String, Object> condition = BeanUtils.beanToMap(fooEntity, true);
int retVal = this.fooMapper.insert(condition);
Integer id = ((BigInteger)condition.get("id")).intValue();
LOGGER.debug("受到影响的行数:{},自增的id:{}", retVal, id);
}
// log
FooMapper.insert : ==> Preparing: INSERT INTO `foo` ( `name` , `create_date` ) VALUES( ? , ? )
FooMapper.insert : ==> Parameters: SpringBoot中文社区(String), 2019-11-27T10:49:45.917(LocalDateTime)
FooMapper.insert : <== Updates: 1
FooMapperTest : 受到影响的行数:1,自增的id:1
通过Map<String, Object>
作为参数,插入数据。自增id也可以回写到Map
,是以BigInteger
的形式写入,需要自己转换为 Integer
或者 Long
。
End
不仅仅是insert
,其他的操作使用Map
作为参数,配合foreach
。就算是不使用第三方插件,也可以很方便的完成需要的功能。并且在修改了DB
或者实体类的字段后,不需要修改xml文件。甚至可以单独的吧Map作为参数的crud-mapper
抽离出来,其他mapper
通过include
导入。这也是一种通用的方案。
需要注意的问题就是,foerach遍历的过程中,${key},不是预编译语句。可能导致SQL注入的问题。只要保证Map<String, Object> 不是由前端参数直接构建的,而是通过实体类转换的。那就没问题