关于持久化对象经过 Session 处理后的流程图
进一步测试session的方法
class TestHibernate2 {
private SessionFactory sessionFactory = null;
private Session session = null;
private Transaction transaction = null;
/**
* init 和 destroy 方法略
*/
/*
* 通过调用原生的Connection对象来调用数据库的存储过程
* Work 接口: 直接通过 JDBC API 来访问数据库的操作
* Session 的 doWork(Work) 方法用于执行 Work 对象指定的操作,
* 即调用 Work 对象的 execute() 方法.
* Session 会把当前使用的数据库连接传递给 execute() 方法.
*
*/
@Test
void testDoWork() {
this.session.doWork(new Work() {
@Override
public void execute(Connection arg0) throws SQLException {
System.out.println(arg0); //com.mysql.jdbc.JDBC4Connection@33a053d
//调用存储过程(同jdbc方式调用存储过程)
//还可以进行批量操作(使用原生的connection)
}
});
}
/*
* evict:从session缓存中把指定的持久化对象移除
*/
@Test
void testEvict() {
News news = this.session.get(News.class, 3);
News news2 = this.session.get(News.class, 4);
news.setAuthor("123");
news2.setAuthor("123");
//调用evict方法后无法发送update语句进行更新news对象对应的数据
this.session.evict(news);
}
/*
* delete:执行删除操作,
* 只要OID和数据表中的一条记录对应(无论是游离对象还是持久化对象)
* 就会准备执行delete操作,将数据库中对应的记录删除掉
* 如果OID在数据表中没有对应的记录,那么抛出异常
* 注意:
* 可以通过设置hibernate 配置文件的hibernate.use_identifier_rollback为true,
* 使得被删除的对象的OID设置为null
*/
@Test
void testDelete() {
News news = this.session.get(News.class, 2);
//准备执行删除数据表记录操作,等到事务提交的时候才进行真正的删除操作
this.session.delete(news);
//news对象依然存在,并且这条语句优先delete语句执行
System.out.println(news);
}
/*
* 注意:
* 1. 若OID不为空,但是数据表中没有这个OID对应的记录,那么会抛出异常
* 2. 若OID为null,本来应该是游离对象,
* 但是如果OID等于id的unsaved-value属性值的对象,也会被认为是游离对象(了解)
*/
@Test
void testSaveOrUpdate() {
News news = new News("qq", "jj", new Date());
// news.setId(111);
this.session.saveOrUpdate(news);
}
/*
* update:
* 1. 若更新一个持久化对象,不需要显示的调用session的update方法,
* 因为在调用Transaction的commit方法之前就已经执行了
*
* 2. 若更新一个游离的对象,就需要显示的调用session的update方法,
* 可以把一个游离的对象变为持久化对象
*
* 注意:
* 1. 无论要更新的游离状态的对象和数据表的记录是否一致,
* 都会发送update语句(因为新开的session并不能确定游离状态的对象和
* 数据库中的对应的记录是否一致
* 那么如何才能让session的update方法不盲目的发送update语句呢?
* 在.hbm.xml文件的class节点设置select-before-update=true
* (默认为false),但是通常不要设置这个属性,因为效率较低,
* 一般都是使用hibernate的触发器才可能使用这个属性
* 2. 若数据表中没有对应的记录,但还是调用了update方法,那么会抛出异常
*
* 3. 当update方法关联一个游离的对象时,如果在session的缓存中已经有了相同OID的对象,
* 那么调用update方法将会抛出异常,
* 因为在session的缓存中是不允许有两个OID相同的对象!
*/
@Test
void testUpdate() {
News news = this.session.get(News.class, 1);
this.transaction.commit();
this.session.close();
// news.setId(1000);
//之前的session已经被关闭,存储在session缓存中的对象成为了游离状态,
//此时新开的session缓存中没有之前查询的这个处于
//游离状态的对象,如果想要通过新开的session对象将游离状态的对象持久化,
//就需要手工调用session的update方法了
this.session = this.sessionFactory.openSession();
this.transaction = this.session.beginTransaction();
News news2 = this.session.get(News.class, 1);
//news.setAuthor("老二爹");
this.session.update(news);
}
/*
* get vs load
*
* 1. 执行get方法会立即加载对象(立即检索)
* 执行load方法,若不使用该对象,则不会立即执行查询操作,
* 而是返回一个代理对象(延迟加载)
*
* 2. load方法可能会抛出懒加载异常 lazyInitiallizationException 异常:
* 需要初始化代理对象之前已经关闭了session
*
* 3. 若数据表中没有对应的记录,并且session没有关闭的情况下
* get返回null
* load方法如果不使用对象的任何属性,则没有问题;
* 若需要初始化代理对象时,就抛出异常
*/
@Test
void testLoad() {
News news = this.session.load(News.class, 10);
System.out.println(news.getClass().getName());
// this.session.close();
System.out.println(news);
}
@Test
void testGet() {
News news = this.session.get(News.class, 10);
// this.session.close();
System.out.println(news);
}
/*
* persist方法:也可以保存数据,执行insert语句
*
* 和save方法的区别:
* 在调用persist方法之前,若对象已经有id主键了,
* 将抛出异常并不会执行insert语句
*/
@Test
void testPersist() {
News news = new News("cc", "张三", new Date());
System.out.println(news);
// news.setId(1001);
this.session.persist(news);;
}
/*
* save方法详解:
* 1. 使一个临时对象变为持久化对象
* 2. 为对象分配id(使用数据库自己的主键生成策略)
* 3. 在flush缓存时会发送一条insert语句
* 4. 在save方法之前设置的id可以设置,但是无效
* 5. 持久化后的对象的id是不可以改变的!
*/
@Test
public void testSave() {
News news = new News("aa", "张三", new Date());
System.out.println(news);
news.setId(1001);
this.session.save(news);
news.setId(222);
System.out.println(news);
}
}
拓展:Hibernate 与触发器协同工作
- Hibernate 与数据库中的触发器协同工作时, 会造成两类问题
- 触发器使 Session 的缓存中的持久化对象与数据库中对应的数据不一致:触发器运行在数据库中, 它执行的操作对 Session 是透明的
- Session 的 update() 方法盲目地激发触发器: 无论游离对象的属性是否发生变化, 都会执行 update 语句, 而 update 语句会激发数据库中相应的触发器
解决方案:
1.在执行完 Session 的相关操作后, 立即调用 Session 的 flush() 和 refresh() 方法, 迫使 Session 的缓存与数据库同步(refresh() 方法重新从数据库中加载对象)2.在映射文件的的 元素中设置 select-before-update 属性: 当 Session 的 update 或 saveOrUpdate() 方法更新一个游离对象时, 会先执行 Select 语句, 获得当前游离对象在数据库中的最新数据, 只有在不一致的情况下才会执行 update 语句,当然带来的问题就是影响数据库的效率
获取源代码请点击我