批量插入

       经常有海量插入数据的需求,几十万,几千万甚至更多。常用的框架如mybatis、hibernate都支持批量插入,其基本原理都是重写insert语句(讲多个汇聚成一个)。

从sql语句比较。

非批量插入

INSERT INTO `t` (`name`) VALUES ("name_1");
INSERT INTO `t` (`name`) VALUES ("name_2");

批量插入

INSERT INTO `t` (`name`) VALUES ("name_1"),("name_2");

         对海量数据插入,选择批量插入是必要的。

前置条件:

  • 数据库mysql
  • 驱动jbbc
  • 模型带自增长主键

      选择自增主键的原因:使用数据库自身的内部轻量级锁机制,更高效;索引优化;使用方便。

JDBC批量插入

       mybatis或hibernate的批量插入是以JDBC的批量插入为为为基础的。原生的jdbc批量插入,如上,手动拼接sql,但此种带来的sql注入等问题不可避免,因此,开发中极不推荐此种方法。

       对于大批量的数据插入,讲究效率,首推JDBC,并不推荐mybatis或hibernate。

         代码执行JDBC批量插入

		long start = System.currentTimeMillis();
		Class.forName("com.mysql.jdbc.Driver");
		Connection connection = DriverManager.getConnection(
				"jdbc:mysql://127.0.0.1:3306/test?useSSL=true&rewriteBatchedStatements=true", "root", "1234");
		connection.setAutoCommit(false);
		PreparedStatement cmd = connection.prepareStatement("insert into `t` (`name`) values(?)");
		for (int i = 1; i <= 999999; i++) {
			cmd.setString(1, "name_" + i);
			cmd.addBatch();
			// 一次想insert 5000条
			if (i % 5000 == 0 && i != 0) {
				cmd.executeBatch();
				cmd.clearBatch();
			}
			//每20000条提交一次事物
			if (i % 20000 == 0 && i != 0) {
				cmd.executeBatch();
				cmd.clearBatch();
				connection.commit();
			}
		}

		cmd.executeBatch();
		cmd.clearBatch();
		connection.commit();
		cmd.close();
		connection.close();
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	
       只有配置了rewriteBatchedStatements,批量才会生效


Hibernate批量插入

           hibernate有个hibernate.jdbc.batch_size属性,配置批量insert的size,但是hibernate基于transactional write-behind的策略,对主键自增(strategy=GenerationType.IDENTITY)策略不予支持批量操作。不使用主键自增。

	@Autowired
	LocalSessionFactoryBean factory;
	@Test
	public void contextLoads() throws Exception {
	    long start = System.currentTimeMillis();
	    /**hibernate.jdbc.batch_size不会设置很大,在30-50左右,但是大批量的插入,显然此值偏小。
	     * 大批量的插入也不推荐使用hibernate的批量插入(多余的封装处理环节,建议jdbc)
	     * */
	    factory.getHibernateProperties().setProperty("hibernate.jdbc.batch_size", "5000");
	    factory.afterPropertiesSet();
		Session session = factory.getObject().openSession();
		Transaction tx = session.beginTransaction();
		for  ( int i=1; i<=200000; i++ ) {
			T t = new T();
			t.setId(i);
		    t.setName("name-"+i);
		    session.save(t);
		  //手动控制强刷,一般取值<=hibernate.jdbc.batch_size,大于没意义
		    if ( i % 5000 == 0 && i != 0) {
		    	session.flush();
		        session.clear();
		    }
		    if ( i % 50000 == 0 && i != 0) {
		    	session.flush();
		        session.clear();
		        tx.commit();
		        tx = session.beginTransaction();
		    }
		}
		
    	session.flush();
        session.clear();
        tx.commit();
		session.close();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
	}
         为了查看批量插入的数据,可使用小工具 datasource-proxy

Mybatis批量插入

       mybatis有两者批量插入

	@Autowired
	SqlSessionFactory sqlSessionFactory;
	@Test
	public void contextLoads() {
		long begin= System.currentTimeMillis();
		SqlSession session =null;
		TeeMapper mapper = null;
		session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		mapper = session.getMapper(TeeMapper.class);
		for (int i = 1; i < 200000; i++) {
			mapper.insert(new Tee("name-"+i,i));
			if (i%50000==0) {
				session.flushStatements();
				session.clearCache();
			}
		}
		
		session.commit();
		System.out.println(System.currentTimeMillis()-begin);
	}
	
	@Test
	public void contextLoads1() {
		long begin= System.currentTimeMillis();
		SqlSession session =null;
		TeMapper mapper = null;
		session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
		mapper = session.getMapper(TeMapper.class);
		List<Te> list = new ArrayList<>();
		for (int i = 1; i < 200000; i++) {
			list.add(new Te("name-"+i));
			if (i%50000==0) {
				mapper.batchinsert(list);
				list.clear();
				session.clearCache();
			}
		}
		
		mapper.batchinsert(list);
		list.clear();
		session.clearCache();
		session.commit();
		System.out.println(System.currentTimeMillis()-begin);
	}

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="zl.example.mapper.TeMapper">
    <insert id="insert" parameterType="Te" useGeneratedKeys="true"
    keyProperty="id">
    	insert into `te` (name) values (#{name})
    </insert>
    
    <insert id="batchinsert" parameterType="java.util.List">  
    insert into `te` (name)  
    VALUES  
    <foreach collection="list" item="c" index="index" separator=",">  
       (#{c.name})  
    </foreach>  
    </insert> 
</mapper>
      测试时batchinsert效率要远高于insert(insert中rewriteBatchedStatements没有生效)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值