Session的save()方法使一个临时对象转变为持久化对象。例如以下代码保存一个Customer对象:
Customer customer = new Customer(); customer.setId(new Long(9)); // 为Customer临时对象设置OID是无效的 customer.setName("Tom"); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.save(customer); session.close();
Session的save()方法完成以下的操作:
(1)把Customer对象加入到缓存中,使它变为持久化对象。
(2)选用映射文件指定的标识符生成器为持久化对象分配惟一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定标识符生成器:(此配置文件跟Customer类放在一起,generator的class还有另外一个取值“native”)
<id name="id" column="ID"> <generator class="increment"/> </id>
以上程序试图通过setId()方法为Customer临时对象设置OID是无效的。假如起初CUSTOMERS表中没有记录,那么执行完save()方法后,Customer对象的ID为1。如果希望由程序来为Customer对象指定OID,可以调用save()的另一个重载方法:
save(customer, new Long(9));
以上save()方法的第二参数显示指定Customer对象的OID。这种形式的save()方法不推荐使用,尤其在使用代理主键的场合,不应该由程序为持久化对象指定OID。
(3)计划执行一个insert语句,把Customer对象当前的属性值组装到insert语句中:
insert into CUSTOMERS(ID, NAME, ......) values(1, 'Tom', ......);
值得注意的是,save()方法并不立即执行SQL insert语句。只有当Session清理缓存(事务提交时先清理缓存,transaction.commit())时,才会执行SQL insert语句。如果在save()方法之后,又修改了持久化对象的属性,这会使得Session在清理缓存时,额外执行SQL update语句。以下两段代码尽管都能完成相同的功能,但是左边代码仅执行一条SQL insert语句,而右边代码执行一条SQL insert和一条SQL update语句。上边代码减少了操作数据库的次数,具有更好的运行性能。
Customer customer = new Customer(); Customer customer = new Customer(); // 先设置Customer对象的属性,再保存它 session.save(customer); customer.setName("Tom"); // 先保存Customer对象,再修改它的属性 session.save(customer); customer.setName("Tom"); transaction.commit(); transaction.commit();//此时清理缓存
Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系。当Customer对象处于持久化状态时,不允许程序随意修改它的OID,例如:
Customer customer = new Customer(); session.save(customer); customer.setId(new Long(100)); // 抛出HibernateException transaction.commit();
以上代码会导致Session在清理缓存时抛出异常。
注意:无论Java对象处于临时状态、持久化状态还是游离状态,应用程序都不应该修改它的OID。因此,比较安全的做法是,在定义持久化类时,把它的setId()方法设为为private类型,禁止外部程序访问该方法。
Session的save()方法是用来持久化一个临时对象的。在应用程序中不应该把持久化对象或游离对象传给save()方法。例如以下代码两次调用了Session的save()方法,第二次传给save()方法的Customer对角处于持久化状态,这步操作其它是多余的:
Customer customer = new Customer(); session.save(customer); customer.setName("Tom"); session.save(customer); // 这步操作是多余的 transaction.commit();
在例如以下代码把Customer游离对象传给session2的save()方法,session2会把它当做临时对象处理,再次向数据库中插入一条Customer记录:
Customer customer = new Customer(); customer.setName("Tom"); Session session1 = sessionFactory.openSession(); Transaction tx1 = session1.beginTransaction(); session1.save(customer); // 此时Customer对象的ID变为1 tx1.commit(); session1.close(); // 此时Customer对象变为游离对象 Session session2 = sessionFactory.openSession(); Transaction tx2 = session2.beginTransaction(); session2.save(cutomer); // 此时Customer对象的ID变为2 tx2.commit(); session2.close();
尽管以上程序代码能正常运行,但是会导致CUSTOMERS表中有两条代表相同业务实体的记录,因此不符合业务逻辑。
------------------------------------------------------------------------------------------------------
在默认情况下,session会在下面的时间点清理缓存
1 当应用程序调用net.sf.hibernate.Transaction的commit()方法时,commit()方法先清理缓存,然后再看数据库提交事务
2 当调用session的find()或者iterator()时,如果缓存中持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。
3 当应用程序显式调用session的flush()方时。
Session的setFlushMode()方法用于设定清理缓存的时间点。FlushMode类定义了三种不同的清理模式:FlushMode.AUTO、FlushMode.COMMIT和FlushMode.NEVER。例如,以下代码显示把清理模式设为FlushModo.Commit:
session.setFlushMode(FlushMode.COMMIT);
三种清理模式
清理缓存的模式 |
Session的查询方法 |
Session的commit()方法 |
Session的flush()方法 |
FlushMode.AUTO | 清理 | 清理 | 清理 |
FlushMode.COMMIT | 不清理 | 清理 | 清理 |
FlushMode.NEVER | 不清理 | 不清理 | 不清理 |
FlushMode.AUTO是默认值,这也是优先考虑的清理模式,它会保证在整个事务中,数据保持一致。如果事务公包含查询数据库的操作,而不会修改数据库的数据,也可以选用FlushMode.COMMIT模式,这可以避免在执行Session查询方法时先清理缓存,以稍微提高应用程序的性能。
在大多数情况下,应用程序不需要显示调用Session的flush()方法,flush()方法适用于以下场合:
(1)插入、删除或更新某个持久化对象会引发数据库中的触发器。假定向CUSTOMERS表新增一条记录时会引发一个数据库触发器,在应用程序中,通过Session的save()方法保存了一个Customer对象,应用随后调用Session的flush()方法:
session.save(customer);
session.flush();
Session的flush()方法会立即执行insert语句,该语句接着引发相关的触发器工作。
(2)在应用程序中混合使用Hibernate API和JDBC API。
(3)JDBC驱动程序不健壮,导致Hibernate在自动清理缓存的模式下无法正常工作。
我们知道,如果一味的让新的数据放到缓存中去,那我们计算机肯定会内存崩溃。所以进行必要的缓存清除还是很有必要的。
下面我们分析一下几种方法:
1、
for(int i=0;i<1000;i++){ Order order = new Order(); order.setId(); session.save(order); if(i%100==0){ session.flush(); session.clear(); } }
2 、
for(int i=0;i<1000;i++){ Order order = new Order(); order.setId(); session.save(order); session.evict();//清除session缓存 SessionFactory.evict();//清除二级缓存 }
注意:一级缓存只在同一个session中有效,二级缓存是全局性质的
一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据。
二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库。(
Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集合属性包含的数据,后者是指Session的一些集合属性包含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的插件。在默认情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是内存或者硬盘。SessionFactory的外置缓存也被称为Hibernate的第二级缓存。
)
evict()方法
该方法于上一个方法不同,它只能用于处理单个对象的清除工作。
clear()方法
我们可以在session-factory标签下创建property标签,name属性为hibernate.jdbc.batch_size,值为我们想要设定的数字,假如为100,下一步当我们执行操作flush()发送SQL语句时候调用session.clear()方法,就可以实现清除缓存的效果了。