只有当一个单元测试类在执行session.save()才会分配id,即将一个对象从transient状态转换为persistent状态。但是如果这个对象的id号是如果uuid算法分配的,(uuid是由hibernate生成的,而native则是相应的数据库生成的),则数据库中目前还没有存在与之相对应的记录(此时也没有向数据库中发送sql语句),此时hibernate把这个对象放一个临时集合和一个persistencecontext(hibernate用map实现的)中,直到执行commit()方法才会将对象放在数据库中,其实在commit()执行之前会隐含的执行flush()方法,flush()方法会清理缓存,即把临时集合清空,并由此形成一条insert语句,到persistencecontext中的entityentries中寻找相应的对象信息,并把existsindatebase变为true。最后再发送相应的sql语句,将对象存储在数据库中。即执行flush()方法之后就已经发送sql了,最后进行commit方法用于提交事务,但是如果利用单元测试的debug,在执行完flush()方法之后,利用sql select语句查找,则还找不到相应的记录,原因是由于数据库的隔离级别,通常数据库中存在四个隔离级别,read uncommited ,read commited,repeatable read,serialiable read。mysql使用的是第三个级别,oracle使用的是第二个级别,级别越高对其并发性要求越严格,在mysql下可以使用:select @@tx_isolation;查看相应的级别。set transaction isolation level read uncommited,更改其级别为read uncommited,这样就能够查看到相应的数据。其实在flush()方法之后发送了sql就将对象放在数据库中,由于隔离级别的影响直到mysql提交之事务之后才可以读。其实事务就是封装一些连续性的操作,可以在未提交时错误发生的时候进行回滚,保证其原子性。
用代码段示例:
user user = new user();
user.setname("kevin");
user.setpassword("123");
// 因为user的主键生成侧路采用的是uuid,所以调用完成save后,只是将user纳入到了session的管理
// 不会发出insert语句,但是id已经生成,session中existsindatebase状态为false
session.save(user);
// 调用flush,hibernate会清理缓存,执行sql
// 如果数据库的隔离级别设置为为提交读,那么我们可以看到flush过的数据
// 并且session中existsindatebase状态为true
session.flush();
// 提交事务
// 默认情况下commit操作会先执行flush清理缓存,所以不用显示的调用flush
// commit后数据是无法回滚的
tx.commit();
hibernate中native主键生成策略
因为user的主键生成策略是native,调用session.save()后将生成id,并向数据库中发送sql语句,此时在read uncommited级别下能够读到相应的数据。相当于在native策略下save()方法之后会自动调用flush(),清理了缓存,并向数据库发送sql。
示例代码段:
user2 user = new user2();
user.setname("kevin");
// 因为user的主键生成策略为native,所以调用session.save后,将执行insert语句,返回有数据库生成的id
// 纳入了session的管理,修改了session中existsindatebase状态为true
// 如果数据库的隔离级别设置为为提交读,那么我们可以看到save过的数据
session.save(user);
tx.commit();