**
以后谁在用Mybatis的foreach进行大批量操作,谁就辞职
**
最近进行数据库大批量插入操作,有人竟然使用Mybatis的foreach去执行,我大呼失策,为什么要用foreach进行数据库大批量操作呢,就连Mybatis官方也不建议使用foreach去进行大批量数据的操作,并且给出了解决方案.
有兴趣的可以查看,找到Batch Insert Support 标签里的内容.
哎,还要优化同事的代码,烦死了!!!****
package com.eeetuo.Foreach;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.IService;
import com.eeetuo.Foreach.config.MyBatisJoinApplication;
import com.eeetuo.entity.TagLastValueEntity;
import com.eeetuo.entity.TagValueEntity;
import com.eeetuo.Foreach.enums.SqlForeachEnums;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class ForeachByMyBatis {
@Resource
MyBatisJoinApplication myBatisJoinApplication;
@Resource
DataSourceProperties properties;
@Resource
IService<TagLastValueEntity> iService;
public void BatchInsertSupport(List<TagValueEntity> records, Integer sqlCode) {
Connection connection = null;
try {
connection = DriverManager.getConnection(myBatisJoinApplication.getConnection(),properties.getUsername(),properties.getPassword());
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement(
SqlForeachEnums.getSqlByCode(sqlCode));
for (int i = 0; i < records.size(); i++) {
ps.setString(1,"1");
ps.setString(2, String.valueOf(LocalDateTime.now()));
ps.setString(3, records.get(i).getTagValue());
ps.setLong(4,records.get(i).getTagId());
ps.setLong(5,records.get(i).getDevinceId());
//如果选用第二条sql,则添加当天时间和小时时间
if (sqlCode ==2){
ps.setString(6, String.valueOf(LocalDate.now()));
ps.setString(7, String.valueOf(LocalDateTime.now().getHour()));
}
ps.addBatch();
}
ps.executeBatch();
connection.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//true:代表添加 false:代表修改
public boolean saveOrUpdate(TagLastValueEntity entity){
TableInfo tableInfo = TableInfoHelper.getTableInfo(TagLastValueEntity.class);
Object idVal = ReflectionKit.getFieldValue(entity, tableInfo.getKeyProperty());
return ObjectUtils.isEmpty(idVal);
}
}
我们看到新建一个进行操作批量的类,核心的方法就是BatchInsertSupport,其中DriverManager.getConnection()中的值其实是数据库连接url和用户名密码,在这里我是用的yml配置文件的方式来进行操作的,因为我懒,不想到时候如果更换数据库地址而再次改代码.
package com.eeetuo.Foreach.config;
import lombok.Data;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import static org.apache.commons.lang3.StringUtils.isEmpty;
@Data
@Component
@ConfigurationProperties(prefix = "join")
public class MyBatisJoinApplication {
/** 数据库类型*/
private String dbType;
/** 数据库url*/
private String dbUrl;
/** 数据库名字*/
private String dbName;
/** 数据库连接*/
private String Connection;
public String getConnection(){
if (isAllEmpty(getDbType(),getDbUrl(),getDbName())){
return "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true";
}else {
return "jdbc:"+getDbType()+"://"+getDbUrl()+"/"+getDbName()+"?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true";
}
}
public static boolean isAllEmpty(CharSequence... css) {
if (ArrayUtils.isEmpty(css)) {
return true;
} else {
CharSequence[] var1 = css;
int var2 = css.length;
for(int var3 = 0; var3 < var2; ++var3) {
CharSequence cs = var1[var3];
if (isEmpty(cs)) {
return true;
}
}
return false;
}
}
}
需要注意的一点是PreparedStatement 在进行set方法中的参数所代表的意思!!!
需要注意的是jdbc连接必须要有 rewriteBatchedStatements=true
关于rewriteBatchedStatements这个参数介绍:
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效
参数也很明了,records就是需要我们所需要的参数集,sqlCode就是具体的sql语句,那具体是需要哪条sql语句就需要我们创建一个枚举来判断一下了,接下来就是创建枚举类了.
package com.eeetuo.Foreach.enums;
import lombok.Getter;
@Getter
public enum SqlForeachEnums {
TAG_VALUE_SQL(1,"insert into tb_tag_value (state,created_time,tag_value,tag_id,devince_id) values(?,?,?,?,?)"),
TAG_HOUR_VALUE_SQL(2,"insert into tb_tag_hour_value (state,created_time,tag_value,tag_id,devince_id,day_time,hour_time) values(?,?,?,?,?,?,?)"),
TAG_LAST_VALUE_ADD_SQL(3,"insert into tb_tag_last_value (state,created_time,tag_value,tag_id,devince_id) values(?,?,?,?,?)"),
TAG_LAST_VALUE_UPDATE_SQL(4,"update tb_tag_last_value set state=?,created_time=?,tag_value=?,tag_id=?,devince_id=? where id=?");
private Integer sqlCode;
private String sql;
SqlForeachEnums(Integer sqlCode,String sql){
this.sqlCode=sqlCode;
this.sql=sql;
}
public static String getSqlByCode(Integer sqlCode){
String sql="";
//判断如果code相同,获取对应的sql语句
for (SqlForeachEnums value : SqlForeachEnums.values()) {
if (value.sqlCode.equals(sqlCode)){
sql= value.sql;
break;
}
}
return sql;
}
}
在这个枚举类中核心的就是getSqlByCode这个方法,根据参数sqlCode判断选择哪条sql语句
进行到这里,批量操作的工作就已经完成了,接下来就是业务上面的工作了,累呀!!!
希望各位能多给提点意见,小弟好规避bug,拜拜儿!!!