jdbc,mybatis中fetchsize使用,批处理方案
- 简介
- jdbc获取大量数据
- mybatis获取大量数据
- mapper文件
- 自定义ResultHandler来分批处理结果集
- 使用
- 批处理方案
- for循环一条条插入,或者 去实现的批量操作
- 使用创建SqlSession
- 总结
简介
在操作数据库或者使用框架的时候避免不了批处理的使用场景,本文就这些场景讲解对应的方案
jdbc获取大量数据
场景,多用于导出一次性拉取大量数据,出现卡死,内存溢出问题,可以进行分批拉取数据然后最后统一导出或者分批导出
public class testMain {
public static long importData(String sql){
String url = "jdbc:mysql://127.0.0.1:3306/gsong?characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8";
try {
("");
} catch (ClassNotFoundException e1) {
();
}
long allStart = ();
long count =0;
Connection con = null;
PreparedStatement ps = null;
Statement st = null;
ResultSet rs = null;
try {
con = (url,"root","123456");
ps = (PreparedStatement) (sql,ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY);
(Integer.MIN_VALUE);
(ResultSet.FETCH_REVERSE);
rs = ();
while (()) {
//此处处理业务逻辑
count++;
if(count%600000==0){
long end = ();
(" 写入到第 "+(count/600000)+" 个文件中!+++"+(end-allStart));
allStart=end;
}
}
("取回数据量为 "+count+" 行!");
} catch (SQLException e) {
();
} finally {
try {
if(rs!=null){
();
}
} catch (SQLException e) {
();
}
try {
if(ps!=null){
();
}
} catch (SQLException e) {
();
}
try {
if(con!=null){
();
}
} catch (SQLException e) {
();
}
}
return count;
}
public static void main(String[] args) throws InterruptedException {
String sql = "select * from user limit 0,4800000 ";
importData(sql);
}
}
mybatis获取大量数据
mapper文件
<select resultType="" fetchSize="1000">
SELECT gxid from t_gxid
</select>
自定义ResultHandler来分批处理结果集
public class GxidResultHandler implements ResultHandler<String> {
// 这是每批处理的大小
private final static int BATCH_SIZE = 1000;
private int size;
// 存储每批数据的临时容器
private Set<String> gxids;
public void handleResult(ResultContext<? extends String> resultContext) {
// 这里获取流式查询每次返回的单条结果
String gxid = ();
// 你可以看自己的项目需要分批进行处理或者单个处理,这里以分批处理为例
(gxid);
size++;
if (size == BATCH_SIZE) {
handle();
}
}
private void handle() {
try {
// 在这里可以对你获取到的批量结果数据进行需要的业务处理
} finally {
// 处理完每批数据后后将临时清空
size = 0;
();
}
}
// 这个方法给外面调用,用来完成最后一批数据处理
public void end(){
handle();// 处理最后一批不到BATCH_SIZE的数据
}
}
使用
@Service
public class ServiceImpl implements Service {
@Autowired
SqlSessionTemplate sqlSessionTemplate;
public void method(){
GxidResultHandler gxidResultHandler = new GxidResultHandler();
("", gxidResultHandler);
();
}
}
总结
流式查询:内存会保持稳定,不会随着记录的增长而增长。其内存大小取决于批处理
大 小 BATCH_SIZE的设置,该尺寸越大,内存会越大。所以BATCH_SIZE应该根据业务
情况设置合适的大小。另外要切记每次处理完一批结果要记得释放存储每批数据的
临时容器,即上文中的();
批处理方案
for循环一条条插入,或者 去实现的批量操作
<insert >
insert into user(name,age,is_delete)
<foreach collection="users" item="user" open="VALUES" close=";" separator=",">
(#{},#{},#{})
</foreach>
</insert>
List<User> users=new ArrayList<>();
for(int i=0;i<10000;i++){
(new User(null,"赵敏"+i,i,"镇雄"+i,false));
}
long begin=();
int insertBatch = (users);
long end=();
(end-begin);
(insertBatch);
();
总结:
这种方式数据量大时将会卡死
改进后!!!直接for循环,50次提交
public void batchTest() {
SqlSession sqlSession = ();
CountryMapper mapper = ();
List<Country> countries = ();
for (int i = 0; i < (); i++) {
Country country = (i);
(() + "Test");
(country);
//每 50 条提交一次
if((i + 1) % 50 == 0){
();
}
}
();
}
总结:
每50次提交一次,是一种可行的方式
使用创建SqlSession
public void test09(){
SqlSession sqlSession = ();
IUserMapper mapper = ();
long begin=();
for(int i=0;i<10000;i++){
(new User(null,"赵敏"+i,i,"镇雄"+i,false));
}
long end=();
(end-begin);
();
}
总结:不会被卡死,但实际运用中,选择改进后的自定义多少次提交更为友好
总结
批量提交只能应用于 insert, update, delete。
并且在批量提交使用时,如果在操作同一SQL时中间插入了其他数据库操作,就失去了其意义,所以在使用批量提交时,要控制好 SQL 执行顺序。