前几天在学习Hibernate批量插入数据时遇到了一个问题:原定插入100条数据,但是数据库中只有81条数据。
今天查了一下资料,发现了问题所在,总结如下
在实际应用中,有时需要同时向数据库中插入多条记录,每一条记录对应的持久化对象在保存到数据库之前,暂时存储在Session缓存中,但是如果存储的对象过多,程序可能会运行失败,并抛出OutOfMemoryException(内存溢出异常);为了解决这个问题,可以对要存储的数据进行分批存储。
例如:每插入20条数据,就提交一次事务,然后清空Session缓存。演示如下:
持久化类:Customer.class 主键生成方式:native
package pojo;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Customer implements Serializable{
private Integer id;
private String userName;
private String password;
private String realName;
private String address;
private String mobile;
private IdCard idCard;
private Set<Order> orders=new HashSet<Order>(0);
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
public Customer(String userName,String password,String realName,
String address,String mobile)
{
this.userName=userName;
this.password=password;
this.realName=realName;
this.address=address;
this.mobile=mobile;
}
public Customer()
{
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
}
<span style="font-size:24px;">测试类:InsertTest.class</span>
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class InsertTest {
/**
* @param args
*/
public static void main(String[] args) {
//Configuration对象实例化
Configuration configuration=new Configuration();
//解析并加载配置文件
configuration.configure("hibernate.cfg.xml");
//创建SessionFactory对象
SessionFactory sessionFactory=configuration.buildSessionFactory();
//创建Session对象
Session session=sessionFactory.openSession();
//打开事务
Transaction trans=session.beginTransaction();
//进行插入操作
for(int i=0;i<100;i++)
{
Customer customer=new Customer();
customer.setUserName(""+(i));
customer.setPassword(""+i);
session.save(customer);
if(i%20==0)
{
session.flush();
session.clear();
trans.commit();
trans=session.beginTransaction();
}
}
//关闭Session对象
session.close();
}
}
运行结果如图:
数据库如下图:
可以从我标出的箭头看到,数据库里只有81条数据,而不是100条。
为什么会出现这种结果呢?
原因是for循环从0开始计数,数据库主键从1开始生成,当i=80时,存储的是第81个对象,此时i=80,80能被20 整除,因此提交了一次事务,Session缓存中的数据被插入到数据库中,for循环进入下一次循环,而i=81到i=99之间没有能被20整除的数,所以,虽然编号在82--99之间的18个对象虽然存储在Session缓存中,却没有被持久化到数据库中。
同时,因为Customer实体的主键生成方式为native,所以调用Session对象的save方法时Hibernate向数据库发送并执行了与对象相对应的insert语句,但是编号在82-99之间的对象的Insert操作完成后没有提交事务,所以数据没有保存到数据库中,但是主键依然增长了。
所以如果此时向数据库中插入数据的话,主键值会从101开始
再次向数据库插入10条数据,结果如图:
可以看到第二次插入的10条数据的第一条数据的ID值是101,这表明虽然没有插入ID值在82-100之间的数据,主键依然增长了。
这就验证了上面所说的,Hibernate向数据库发送并执行了编号在82-100之间的这18条数据的Insert语句,但是没有提交事务,所以数据没有被保存,而主键增长了。
至此就是出现插入100条数据,数据库中却只有81条数据的原因分析。
解决这个问题的办法很简单,就是在For循环执行结束后再提交一次事务。