Hibernate4-2 通过Session操纵对象

2/22/2017来源:ASP.NET技巧人气:1084

  session接口是Hibernate向应用程序提供的操纵数据库的最主要接口,其提供有基本的保存、更新、删除和加载java对象的方法。   Session具有一个缓存,位于缓存中的对象称为持久化对象,其与数据表中相关记录相对应;其中,Session对象能够在某些时间点,按照缓存中对象的变化来执行对应SQL语句,以同步更新数据库,该过程被称为刷新缓存(flush)。   站在持久化的角度来看,Hibernate将对象分为四种状态:持久化状态、临时状态、游离状态和删除状态,Session的特定方法能使对象从一个状态转换到另一个状态。


1. Session缓存

  概念:在Session接口的实现中包含一系列的Java集合,这些集合构成Session缓存。   **作用:**Session缓存可减少Hibernate应用程序访问数据库的频率,因为只要Session实例没有结束生命周期且没有清理缓存,则存放在其缓存中的对象也不会消失。   其中,Session缓存的基本原理测试代码如下所示:

// 注意:只会向数据库发送一条SQL语句 News news1 = (News) session.get(News.class, 1); System.out.PRintln(news1); News news2 = (News) session.get(News.class, 1); System.out.println(news2); System.out.println(news1 == news2); // true

1.1 flush缓存

  作用: Session按照缓存中对象的属性变化来同步更新数据库中的表记录。   实现:刷新缓存的时间点为session.flush();transaction.commit()。   比较: Session对象的flush()方法可能会执行SQL语句,但不提交事务;而Transaction对象的commit()方法先调用flush()方法,再提交事务(将对数据库的操作永久保存)。   设定刷新缓存的时间点:可以调用Session对象的setFlushMode()方法显式设定刷新缓存的时间点,具体模式如下图所示: 这里写图片描述   注意:在未提交事务或显式调用Session对象flush()方法,也可能会进行缓存刷新操作,具体如下:

执行HQL或QBC查询时:若缓存中持久化对象的属性发生变化,则会先刷新缓存,以保证查询结果能够反映持久化对象的最新状态; 执行save()方法保存对象时:若对象使用native生成器生成OID,则执行保存操作时,会立即执行刷新操作以保证对象的ID是存在的。

1.2 refresh缓存

  作用:强制发送SELECT语句,以使Session缓存中对象的状态和数据表中对应的记录保持一致。   注意:MySQL数据库的默认事务隔离级别为“REPEATABLE READ”,需要手动修改为“READ COMMITED”。

1.3 clear缓存

  作用:Session对象的clear()方法可以清除Session缓存中的所有缓存对象。


2. 数据库的隔离级别

  数据库的隔离性是指隔离并发运行各个事务的能力,而隔离级别是指一个事务与其他事务的隔离程度;隔离级别越高,数据一致性就越好,但并发性越弱。

2.1 并发问题概述

  对于同时运行的多个事务,当这些事务访问数据库中相同数据时,如果没有采取必要的隔离机制,会导致各种并发问题,如下:

脏读:对于两个事务T1和T2,T1读取了已经被T2更新但还没有被提交的字段后,若T2回滚,T1读取的内容就是临时且无效的; 不可重复读:对于两个事务T1和T2,T1读取一个字段后,T2更新了该字段;之后T1再次读取同一个字段时值发生变化; 幻读:对于两个事务T1和T2,T1从一个表中读取了一个字段后,T2在该表中插入一些新的行;之后,如果T1再次读取同一个表,就会多出几行。

2.2 事务隔离级别

  数据库提供的四种事务隔离级别,分别是: 这里写图片描述   其中,Oracle 支持“READ COMMITED”(默认)和“SERIALIZABLE”两种事务隔离级别,而MySQL支持四种事务隔离级别,默认为“READ COMMITED”。   

2.2 在MySQL中设置隔离级别

  在MySQL数据库中,每启动一个程序就会获得一个单独的数据库连接,每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。

查看当前的隔离级别:SELECT @@tx_isolation; 设置当前MySQL连接的隔离级别:set transaction isolation level read committed; 设置当前MySQL数据库的隔离级别:set global transaction isolation level read committed

2.3 在Hibernate中设置隔离级别

  JDBC数据库连接使用数据库系统默认的隔离级别。在Hibernate中可通过配置其hibernate.connection.isolation属性的方式来设置事务的隔离级别,具体说明如下:

<!-- 1). 配置数据库的事务隔离级别为:READ UNCOMMITED --> <property name="hibernate.connection.isolation">1</property> <!-- 2). 配置数据库的事务隔离级别为:READ COMMITED --> <property name="hibernate.connection.isolation">2</property> <!-- 3). 配置数据库的事务隔离级别为:REPEATABLE READ --> <property name="hibernate.connection.isolation">4</property> <!-- 4). 配置数据库的事务隔离级别为:SERIALIZEABLE --> <property name="hibernate.connection.isolation">8</property>

3. 持久化对象的状态

  站在持久化的角度,Hibernate把对象分为四种状态:持久化状态、临时状态、游离状态、删除状态;Session的特定方法能使对象从一个状态转换到另一个状态。

3.1 临时对象(Transient)

在使用代理主键的情况下,OID通常为null; 不处于Session缓存中; 在数据库中没有对应的记录。

3.2 持久化对象(Persist)

OID不为null; 位于Session缓存中; 若在数据库中已经有与其对应的记录,持久化对象和数据库中的相关记录对应; Session 在flush缓存时,会根据持久化对象的属性变化,来同步更新数据库; 在同一个Session实例的缓存中, 数据库表中每条记录只对应唯一的持久化对象。

3.3 删除对象(Removed)

在数据库中没有和其OID对应的记录; 不再处于Session缓存中; 一般情况下,应用程序不该再使用被删除的对象。

3.4 游离对象(Detached)

OID不为null; 不再处于Session缓存中; 一般情况下,游离对象是由持久化对象转变而来,在数据库中可能还存在与其对应的记录。

3.5 对象状态转换图

这里写图片描述


4. Session核心方法

4.1 save()与persist()方法

/** * Session对象的save()方法: * 1). 将临时对象加入Session缓存中,使其转变为持久化对象; * 2). 选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID; * 3). 在flush缓存时,会发送一条INSERT语句; * 4). 在使用代理主键的情况下,通过setId()方法为临时对象设置OID是无效的; * 5). 持久化对象的OID不能被随意修改,因其维持着持久化对象与数据库记录的对应关系。 * * Session对象的persist()方法:也会执行INSERT操作; * 与save()方法的不同之处:当临时对象的OID不为空时,该方法将抛出异常。 */ @Test public void testSaveAndPersist() { News news = new News(null, "qiaobc", "qiaobei", new Date()); news.setId(1001); // 为临时对象设置OID是无效的 System.out.println(news); // session.persist(news); session.save(news); System.out.println(news); // news.setId(1002); // 持久化对象的OID不能修改 }

4.2 get()与load()方法

/** * Session对象的get()与load()方法:均可根据OID从数据库中加载一个持久化对象 * 1). 执行get()方法时,立即加载对象; * 而执行load()方法时,延迟加载对象,即若不使用该对象,则不会立即查询,而是返回一个代理对象; * 2). load()方法可能会抛出LazyInitializationException异常:在需要初始化代理对象前关闭Session对象; * 3). 若数据表中没有与OID对应的记录,则get()方法返回null; * 而load()方法,若不使用该对象的任何属性则没问题,若需要初始化则抛出ObjectNotFoundException异常。 */ @Test public void testGet() { News news1 = (News) session.get(News.class, 10); System.out.println(news1); // 持久化状态 session.close(); // 游离状态 System.out.println(news1); } @Test public void testLoad() { News news2 = (News) session.load(News.class, 1); // 若不使用,则返回代理对象com.qiaobc.hibernate.entities.News_$$_javassist_0 System.out.println(news2.getClass().getName()); session.close(); // 游离状态 System.out.println(news2); // 抛出LazyInitializationException异常 }

4.3 update()方法

/** * Session对象的update()方法: * 1). 更新持久化对象不需要显式调用update()方法,因为事务提交时会flush缓存; * 2). 更新游离对象需要显式调用update()方法,将其转变为持久化对象; * * 注意: * 1). 无论要更新的游离对象和数据表的记录是否一致,均会发送UPDATE语句; * 当Hibernate与触发器协同工作时,可在*.hbm.xml文件的class节点 * 设置select-before-update=true以确保不盲目发送UPDATE语句; * 2). 当 update()方法关联一个游离对象时,如果在数据库中不存在相应的记录,抛出异常; * 3). 当 update()方法关联一个游离对象时,如果在Session缓存中已经存在相同OID的持久化对象,抛出异常; */ @Test public void update1() { News news = (News) session.get(News.class, 1); news.setAuthor("SUN"); session.update(news); // 若更新持久化对象不需要显式调用update()方法 } @Test public void update2() { News news = (News) session.get(News.class, 1); transaction.commit(); session.close(); session = sessionFactory.openSession(); transaction = session.beginTransaction(); news.setAuthor("SUN"); // 此时news为游离对象 session.update(news); }

4.4 saveOrUpdate()方法

/** * Session对象的saveOrUpdate()方法:临时对象执行保存操作,游离对象执行更新操作。 * 1). 判定对象为临时对象的标准:Java对象的OID是否为null; * 2). 了解:若Java对象的OID取值等于映射文件中id节点unsaved-value属性值,则其也为临时对象。 */ @Test public void testSaveOrUpdate() { // OID不为空,执行更新操作;若OID对应的记录不存在,则抛出StaleStateException异常。 News news1 = new News(10, "qiaobc1", "qiaobei1", new Date()); session.saveOrUpdate(news1); // OID为空,执行保存操作 News news2 = new News(null, "qiaobc", "qiaobei", new Date()); session.saveOrUpdate(news2); }

4.5 merge()方法

这里写图片描述

4.6 delete()方法

/** * Session对象的delete()方法:既可以删除一个游离对象,也可以删除一个持久化对象 * 1). 只要OID与数据表中记录对应,即执行删除操作;无对应记录则抛出异常; * 2). 通过设置hibernate.use_identifier_rollback=true,使删除对象时置OID=null; */ @Test public void testDelete() { News news = new News(); news.setId(5); session.delete(news); // 删除游离对象 News news2 = (News) session.get(News.class, 6); session.delete(news2); // 删除持久化对象 // 配置前:News [id=6, title=qiaobc, author=qiaobei, date=2017-02-20 17:10:35.0] // 配置后:News [id=null, title=qiaobc, author=qiaobei, date=2017-02-20 17:10:35.0] System.out.println(news2); }

4.7 evict()方法

/** * Session对象的evict()方法:从缓存中将持久化对象移除 */ @Test public void testEvict() { News news1 = (News) session.get(News.class, 1); News news2 = (News) session.get(News.class, 2); news1.setAuthor("No.1"); news2.setAuthor("No.2"); session.evict(news2); // 移除news2对象,不再更新该对象 }

5. Hibernate调用存储过程

这里写图片描述


6. Hibernate与触发器协同工作

这里写图片描述