场景
编写数据迁移的小工具,需要将大量整理好的对象数据插入不同的表中,按照原有的方式,工作量很大,因此考虑利用反射机制动态 + mybaits 实现动态生成Sql并批量入库
代码
利用 mybatis 注解 @InsertProvider 指定方法生成SQL语句
package com.company.cutover.provider.mapper.jydp;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author ryzhang5
* @Package com.company.cutover.provider.mapper.jydp
* @date 2020/7/19 17:30
* @Copyright
*/
public interface InsertDataMapper {
@InsertProvider(type = DynamicSqlProvider.class, method = "genCreateTableSql")
Integer insertData(@Param("list") List<?> list, @Param("bean") Class bean);
}
package com.company.cutover.provider.mapper.jydp;
import org.springframework.util.ReflectionUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
/**
* @author ryzhang5
* @Package com.company.cutover.provider.mapper.jydp
* @date 2020/7/19 16:41
* @Copyright
*/
public class DynamicSqlProvider {
/**
* 生成插入sql
* @param data
* @return
*/
public static String genCreateTableSql(Map<String, Object> data){
Class bean = (Class) data.get("bean");
List<Object> list = (List<Object>) data.get("list");
String filesList = getBeanFilesList(bean);
String valueList = getBeanValueList(bean, list);
String sql = "insert into " + getBeanName(bean)+" ("+filesList+") values "+ valueList;
return sql;
}
/**
* 获取表名
* @param bean
* @return
*/
public static String getBeanName(Class bean){
try {
String clzStr = bean.toString();
//得到类名 (类名即表名)
String beanName = clzStr.substring(clzStr.lastIndexOf(".")+1);
return beanName;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* 生成字段
* @param bean
* @return
*/
public static String getBeanFilesList(Class bean){
try {
Field[] strs = bean.getDeclaredFields();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < strs.length; i++) {
if (!strs[i].getName().equals("tableName")&&!strs[i].getType().equals("List")) {
String str = strs[i].getName().substring(0,1).toUpperCase() + strs[i].getName().substring(1);
sb.append("`" + str +"`,");
}
}
sb.deleteCharAt(sb.toString().lastIndexOf(","));
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getBeanValueList(Class bean, List<Object> objList){
StringBuilder sb = new StringBuilder();
try {
Field[] tableFields = bean.getDeclaredFields();
for (Object obj : objList) {
sb.append("(");
for (int i = 0; i < tableFields.length; i++) {
PropertyDescriptor pd = new PropertyDescriptor(tableFields[i].getName(), bean);
Method getMethod = pd.getReadMethod();//获得get方法
Object fieldValue = ReflectionUtils.invokeMethod(getMethod, obj);
if(fieldValue == null){
continue;
}
if(fieldValue instanceof String){
//判断字符串类型是否含有'
if(((String) fieldValue).contains("'")){
fieldValue = ((String) fieldValue).replaceAll("'","\\\\'");
}
}
sb.append("'" + fieldValue.toString() + "',");
}
sb.deleteCharAt(sb.toString().lastIndexOf(","));
sb.append(")");
sb.append(",");
}
sb.deleteCharAt(sb.toString().lastIndexOf(","));
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
注意
传入的类名、字段名 需同 表明 保持一致
补充:
考虑到大批量数据插入的问题,可以数据分批插入
public void insertData(List<?> list, Class bean){
//每次数据插入的数量,可做成配置
int pageSize = 1000;
int pageIndex = 0;
while(pageIndex * pageSize < list.size()){
int startIndex = pageIndex * pageSize;
int endIndex = (pageIndex + 1) * pageSize;
endIndex = endIndex > list.size() ? list.size() : endIndex;
List<?> subList = list.subList(startIndex, endIndex);
insertDataMapper.insertData(subList, bean);
try {
Thread.sleep(1);
}catch (Exception e){
e.printStackTrace();
}
pageIndex++;
}
}