前言
hibernate 拿起来用简单,但是用起来很扎心,经常报一些不能理解的错误。很大因素是我不理解 hibernate。hibernate 中的实体类有三大状态:瞬时状态(transient),持久化状态(persistent),游离状态(detached)。理解了这三个状态,就可知道调用一个方法,究竟发了几条 sql ,干了些什么。
瞬时状态: 不在 session 缓存中,没有保存在数据库中
持久化状态: 在 session 缓存中(被 session 托管),保存在数据库中
游离状态:不在 session 缓存中,保存在数据库中(离线状态)
PS: hibernate 二级缓存(session 缓存)是默认开启的,在向数据库查询时先查看会缓存中有没有。
各种状态转换:
结论
1.瞬时状态,游离状态,相当于一个普通对象,没有被 session 托管,操作不影响数据库。
2.持久化状态,在 session 缓存中,被 session 托管,相关操作会影响数据库
测试
实体类
@Entity
@Table(name = "t_user")
public class UserEntity {
private Long id;
private String name;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setId(Long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
1.如何维护 GenerationType.IDENTITY 策略 (主键自增ID)
插入一个 user ,不设置主键ID值,发出sql: Hibernate: insert into t_user (name) values (?)
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xioamo");
session.save(userEntity);
session.getTransaction().commit();
插入一个 user , 设置主键ID值,发出 sql: Hibernate: insert into t_user (name) values (?)
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaoming");
userEntity.setId(100);
session.save(userEntity);
session.getTransaction().commit();
通过测试可以知道,无论是都指定主键ID的值,都不会把id 值拼接到 sql 中,也就是说主键自增ID策略,完全交给 mysql 自己来处理。你无法指定特定的主键ID。
2.瞬时状态,离线状态对象通过 setters 方法修改对象属性,不会更新到数据库。持久化状态通过通过 setters 方法修改对象属性,会更新到数据库。
userEntity 为瞬时状态,调用setters 控制台没有发出 sql
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaomo");
session.getTransaction().commit();
save(userEntity) 后 userEntity 转为持久化状态。发出sql: Hibernate: insert into t_user (name) values (?)
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaomo");
session.save(userEntity);
session.getTransaction().commit();
在 save 后 userEntity 是持久状态,修改属性,发现发出一条 update sql
sql:
Hibernate: insert into t_user (name) values (?)
Hibernate: update t_user set name=? where id=?
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaomo");
session.save(userEntity);
userEntity.setName("xiaoming");
session.getTransaction().commit();
相对于上面的例子在事务提交前调用 clear 清除缓存,转换为游离状态,发现没有再发出 update sql。
sql:
Hibernate: insert into t_user (address, email, name) values (?, ?, ?)
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaomo");
session.save(userEntity);
userEntity.setName("xiaoming");
session.clear();
session.getTransaction().commit();
通过对比上面例子可以说明,在事务提交前 hibernate 会比对持久化状态的对象,发现当前对象内容反生改变(实际上当前对象和 session 缓存的对象是同一个,至于如何发现对象内容发生改变还没有理解清楚),则会以当前对象为准更新到数据库。而对于瞬时状态和游离状态的对象则不作处理。
3.在会话中多次save 或 update ,其运行结果:
Hibernate: insert into t_user (name) values (?)
UserEntity{id=15, name=’xiaomo’}
UserEntity{id=15, name=’xiaohong’}
Hibernate: update t_user set name=? where id=?
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setName("xiaomo");
session.save(userEntity);
System.out.println(session.get(UserEntity.class,userEntity.getId()));
userEntity.setName("xiaoming");
session.save(userEntity);
userEntity.setName("xiaohong");
session.update(userEntity);
System.out.println(session.get(UserEntity.class,userEntity.getId()));
session.getTransaction().commit();
从上面的测试可以发现只发出了两条 sql ,对于持久化状态的对象,调用 save 或 update 是不起作用的,而是在事务提交前检查对象是否改变,若改变则更新到数据库,通过这样来维护数据库与 session 缓存对象的一致性,大概更高效吧。
4.执行 update(),只会把对象由非持久状态(如果是非持久状态)转换为持久化状态,真正的 update sql 是在事务提交时发出的。
结果:
before update run
after update run
Hibernate: update t_user set name=? where id=?
Session session = HibernateUtil.openSession();
session.beginTransaction();
UserEntity userEntity = new UserEntity();
userEntity.setId(2l);
System.out.println("before update run");
session.update(userEntity);
System.out.println("after update run");
session.getTransaction().commit();
小结:当前被操纵的 java 实体类对象,如果是瞬时状态或者游离状态,则此时没有与数据库关联,如果是持久化状态,该对象(其实就是session缓存的对象)与session 相关联,改变对象属性会更新到数据库。更新到数据库是发生在事务提交的时候。
以上是单表的测试,可以合理猜测在关联表中,实体类,关联实体类的都几个状态转换与含义都一样的。