Java生成大量不重复的主键_Hibernate中的不同主键生成策略下flush()方法的妙用

packagecom.jadyer.hibernate;

importjava.util.Date;

importorg.hibernate.Session;

importorg.hibernate.Transaction;

importcom.jadyer.hibernate.HibernateSessionUtils;

importcom.jadyer.hibernate.User11;

importcom.jadyer.hibernate.User22;

importcom.jadyer.hibernate.User33;

importjunit.framework.TestCase;

publicclassSessionFlushTestextendsTestCase {

/**

* 向数据库中批量录入1000条数据

* @see 执行save()方法时,同样会向一级缓存中存放数据

* @see 所以要考虑到一次性大量的实体数据入库导致内存溢出

* @see 这时便可使用到session的flush()和clear()方法

*/

publicvoidtestCachea() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();//开启事务

for(inti=0; i<1000; i++) {

User22 user =newUser22();

user.setName("u_"+ i);

session.save(user);

if(0== i%20) {

session.flush();//每20条数据就强制session持久化数据

session.clear();//同时清空缓存,避免大量数据造成内存溢出

}

}

//如果数据量特别大,最好不要再使用Hibernate录入数据了,可以考虑采用JDBC实现

//如果JDBC也不能满足要求,那么还可以考虑采用数据库本身的特定导入工具

//比如相对于Oracle来说,就可以使用它的特定工具【SQL*Loader】

session.getTransaction().commit();//提交事务

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();//发生异常则将事务回滚

}finally{

HibernateSessionUtils.closeSession(session);//最终不要忘记关闭org.hibernate.Session

}

}

/**

* 测试主键生成策略为uuid时

* 监测:执行save()之后以及再显式执行flush()时的SQL语句发送情况

* 结果:调用save()后,不会立即发出insert语句。调用flush()后,才会发送并执行SQL语句

* @see 【以下是关于session中的existsInDatebase属性的说明】

* @see 重点关注Debug透视图中的右上角的Variables视图中数据的变化

* @see 首先以Debug As---JUnit Test方式执行单元测试方法,然后当执行完save()而未执行到flush()之前

* @see 在Variables视图中展开到session---actionQueue---insertions---elementData---[0]里面

* @see 我们可以把[0]里面的数据理解为形成insert的对象的集合,以后就遍历该集合,用以生成insert语句

* @see 在Variables视图中session下面的actionQueue是一个临时的集合,用来形成insert或其它的语句

* @see 在Variables视图中session下面的persistenceContext就可以理解成是它的缓存

* @see 展开到persistenceContext---entityEntries---map---entries---table---[0]---value---value

* @see 注意:展开到table中时,未必每次都是[0],而此时我们应该展开的是存在HashMap$Entry值的[i]就对了

* @see 接着在第二层value下面,发现这里面还有一份数据。我们可以粗略的理解成是它缓存里的数据

* @see 这时我们就会发现existsInDatebase属性,该属性就是用来判断当前数据在数据库中是否存在,值为false或true

* @see 在执行完save()而没有执行flush()之前,existsInDatebase属性的值是false

* @see 在执行完flush()而没有执行commit()之前,existsInDatebase的值就变为true,并且会马上发出SQL语句

* @see 当existsInDatebase的值就变为true时,就说明数据库里面已经存在这条数据了

* @see 这时再回到elementData---[0]临时集合查找,发现临时集合里面的数据已经没有了

* @see 因为它要遍历该临时集合,再把临时集合里的数据拿出来形成insert语句。在执行insert后,临时集合就被清掉了

* @see 所以说该临时集合是用来做临时交换的。当清掉临时集合之后,就会更新缓存中existsInDatebase的状态为true

* @see 这时是在执行完flush()之后而没有执行commit()之前,此时还没有提交事务,但是已经执行了SQL语句

* @see 而此时在MySQL Command Line Client中执行select查询的话,是查看不到数据的,这涉及到数据库的隔离级别

* @see 我们可以使用select @@tx_isolation;命令查看MySQL默认隔离级别,结果为REPEATABLE-READ:即可重复读

* @see 如果我们执行set transaction isolation level read uncommitted;命令,此时的隔离级别就是未提交读

*/

publicvoidtestSave11() {

Session session =null;

Transaction tx =null;

try{

session = HibernateSessionUtils.getSession();

tx = session.beginTransaction();

User11 user =newUser11();

user.setName("张三");

user.setPassword("123");

user.setCreateTime(newDate());

//由于User11采用的是uuid的主键生成策略

//所以调用save()后,不会发出insert语句,而只是将user纳入了session的管理中

//但此时id已经生成。而且这个时候session中的existsInDatebase的状态为false

session.save(user);

//调用flush()后,Hibernate会清理缓存,并执行SQL语句,会将user对象保存到数据库中

//如果数据库的隔离级别设置为READ-UNCOMMITTED的话,即未提交读,那么我们就可以看到flush()过的数据

//并且将session中insertions临时集合里user对象清除,此时session中existsInDatebase也被设置为true

//session.flush();

//默认情况下commit()操作会先执行flush()清理缓存,所以不需要显式的调用flush()方法

tx.commit();

}catch(Exception e) {

e.printStackTrace();

tx.rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为native时

* @see 监测:执行save()之后的SQL语句发送情况

* @see 结果:调用save()之后,将立即发送并执行insert语句。因为需要返回由数据库生成的id值

*/

publicvoidtestSave22() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User22 user =newUser22();

user.setName("李四");

user.setPassword("123");

user.setCreateTime(newDate());

//由于User22采用的是native的主键生成策略

//所以调用save()方法之后,将发送并执行insert语句,然后返回由数据库生成的id值

//并纳入了session的管理,也修改了session中的existsInDatebase的状态为true

//如果数据库的隔离级别设置为未提交读,那么我们就可以看到save()过的数据

session.save(user);

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为uuid时

* @see 监测:执行save()和evict()之后,缓存中数据的变化

* @see 结果:执行commit()后无法成功提交,并报告possible nonthreadsafe access to session

*/

publicvoidtestSave33() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User11 user =newUser11();

user.setName("王五");

user.setPassword("123");

user.setCreateTime(newDate());

session.save(user);

//执行evict()后,会将user对象从session中逐出。即从session的entityEntries属性中逐出user对象

session.evict(user);

//执行commit()后无法成功提交

//因为hibernate在清理缓存时,会在session的临时集合insertions中取出user对象进行insert操作

//接下来就会去更新persistenceContext里面entityEntries中的existsInDatabase属性的值为true

//而我们已经采用evict()将user对象从session的entityEntries中逐出了,故找不到相关数据,无法更新

//抛出异常:org.hibernate.AssertionFailure: possible nonthreadsafe access to session

//翻译结果:该线程不安全。也就是说当清除user对象,回来更新数据的时候,它认为是其它线程把这个数据删掉了

//其实给我们的理解:应该说不会出现这个问题。而网上有的人说这可能是Hibernate的一个BUG,其实不然

//通过这些可以看到:Hibernate在缓存方面确实下足了一番功夫

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为uuid时

* @see 监测:在evict()之前先执行flush()方法,用以解决调用evict()之后无法提交数据的问题

* @see 结果:执行commit()后成功提交

*/

publicvoidtestSave44() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User11 user =newUser11();

user.setName("赵六");

user.setPassword("123");

user.setCreateTime(newDate());

session.save(user);

session.flush();//此时会发出insert语句

session.evict(user);

//执行commit()后可以成功提交

//因为hibernate在清理缓存时,在session的insertions临时集合中无法找到user对象

//所以就不会发出insert语句,也就不会更新session中的existsInDatabase属性的状态

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为native时

* @ses 监测:执行save()和evict()之后,缓存中数据的变化

* @ses 结果:执行commit()后成功提交

*/

publicvoidtestSave55() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User22 user =newUser22();

user.setName("马七");

user.setPassword("123");

user.setCreateTime(newDate());

session.save(user);//此时会发送insert语句

session.evict(user);

//执行commit()后可以成功提交

//因为hibernate在清理缓存时,在session的insertions临时集合中无法找到user对象

//所以就不会发出insert语句,也就不会更新session中的existsInDatabase属性的状态

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为assigned时

* @see 监测:批量执行save、update、delete操作的顺序

* @see 结果:Hibernate会按照save、update、delete顺序提交相关操作

*/

publicvoidtestSave66() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User33 user =newUser33();

user.setId("001");

user.setName("王八");

session.save(user);

user.setName("夙瑶");

//session.update(user); //也可以不显式的调用update()。此时的user正处于持久态,它会自动更新的

User33 user3 =newUser33();

user3.setId("002");

user3.setName("玄宵");

session.save(user3);

//Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)

//Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)

//Hibernate: update t_user33 set name=?, password=?, create_time=? where user_id=?

//Hibernate会按照save、update、delete顺序提交相关操作

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

/**

* 测试主键生成策略为assigned时

* @see 监测:利用flush()实现自定义的save、update、delete执行顺序

* @see 结果:SQL会按照我们的意愿执行

*/

publicvoidtestSave77() {

Session session =null;

try{

session = HibernateSessionUtils.getSession();

session.beginTransaction();

User33 user =newUser33();

user.setId("003");

user.setName("蔡依林");

session.save(user);

user.setName("菜10");

//session.update(user); //也可以不显式的调用update()。此时的user正处于持久态,它会自动更新的

session.flush();//利用flush()实现自定义的save、update、delete执行顺序

User33 user33 =newUser33();

user33.setId("004");

user33.setName("郑伊健");

session.save(user33);

//Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)

//Hibernate: update t_user33 set name=?, password=?, create_time=? where user_id=?

//Hibernate: insert into t_user33 (name, password, create_time, user_id) values (?, ?, ?, ?)

//由于在udpate()后面执行了flush()方法,所以在commit()清理缓存时,只会生成session.save(user33)的SQL语句

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally{

HibernateSessionUtils.closeSession(session);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值