Hibernate 的一些注意点
1,org.hibernate.id.IdentifierGenerationException: this id generator generates long, integer, short or string,当用sequence作为主键的generator时,主键只能是long, integer, short or string. 当定义表的主键长于number(18)(18的时候是long)是,hibernate自动生成的代码会用BigDecimal对应,这时候不能用sequence generator.
当数据库字段类型定义为NUMBER(1)的时候,hibernate的mapping文件中会对应为boolean类型.
2 Hibernate中有两个极为相似的方法get()与load(),他们都可以通过指定的实体类与ID从数据库中读取数据,并返回对应的实例,但 Hibernate不会搞两个完全一样的方法的,它们间的不同在于:
a,hibernate中get方法和load方法的根本区别在于:如果找不到符合条件的纪录,get()方法将返回null.而load()将会报出ObjectNotFoundEcception.如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库 中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记 录,那没办法,只能抛异常,所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。由于 session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理,实际使用数据时才查询二级缓存和数据库.所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。
b,load()方法可以返回实体的代理类实例,而get返回的可能是实体类,也可能是代理类.get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载 过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据 库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。
类LockMode定义了6种锁定模式.
(1)LockMode.NONE:默认模式,表示无需锁机制.如果缓存中存在持久化实例,直接使用缓存中的实例.当用session.lock方法的时候,不会执行SQL语句,只有当对被lock的对象作改变,会有update语句.
(2)LockMode.READ:共享锁.载入数据直接从数据库中读取.当用session.lock方法的时候,总会执行SQL Select语句.
(3)LockMode.UPGRADE:使用select……from……for update格式载入数据,并为数据加锁.若数据不支持select……from……for update格式,则使用LockMode.READ模式.
(4)LockMode.UPGRADE_NOWAIT:使用Oracle风格的SQL语句:select……from……for update nowait载入数据.获得锁后,该模式和LockMode.UPGRATE模式的语义相同.
(5)LockMode.WRITE:当Hibernate更新或插入数据时,自动设置LockMode.WRITE模式,该械只能由Hibernate内部使用.
(6)LockMode.FORCE:使用版本控制时,强制实现版本增强,语义和LockMode.UPGRADE相同.
3,如果多表关联在多方的配置文件中指定了property-ref="主表的字段",在做关联查询的时候会执行不必要的SQL语句(在每次取子表数据时,会反向的用SQL再去取主表的数据).
4,多表关联查询用session.createCriteria(XXX.class);可能会包含主表的重复记录,需要用过滤,如下:
criteria.add(Restrictions.eq("mval","mastinfo1")).
setFetchMode("ukdettbs", FetchMode.EAGER); //execute 5 SQL select
List list = criteria.list();
Set set = new HashSet(list);
5,Hibernate 对数据库的操作是更具当前Object处于的状态,从而将Object的值插入或者更新到数据库中.如果在混合使用SQL和HQL,对象值的改变操作时,SQL和HQL对数据的改变不会自动反映到对象上面,如果session在某个SQL或者HQL的update之后,又因为Object的值的改变而执行了另外一个update,SQL或者HQL的update会丢失.如下:
在同一个session中(HQL)的例子,对val的更新会丢失:
tx = sessionFactory.getCurrentSession().beginTransaction();
Hbtest tbo = new Hbtest();
tbo.setId(id);
sessionFactory.getCurrentSession().save(tbo);
sessionFactory.getCurrentSession().createQuery("UPDATE com.test.hb.Hbtest ht SET ht.val=? WHERE ht.id=?")
.setString(0, "val")
.setInteger(1, id).executeUpdate();
tbo.setVal2("val2");//由于val的值并没有反映到tbo对象上,val的值会丢失
tx.commit();
在不同session中(SQL)的例子,对val的更新会丢失:
tx = sessionFactory.getCurrentSession().beginTransaction();
Hbtest tbo = new Hbtest();
tbo.setId(id);
sessionFactory.getCurrentSession().save(tbo);
tx.commit();
tx = sessionFactory.getCurrentSession().beginTransaction();
sessionFactory.getCurrentSession().createSQLQuery("UPDATE HBTEST SET VAL=? WHERE ID=?")
.setString(0, "val")
.setInteger(1, id).executeUpdate();
tx.commit();
tx = sessionFactory.getCurrentSession().beginTransaction();
tbo.setVal2("val2");//由于val的值并没有反映到tbo对象上,val的值会丢失
sessionFactory.getCurrentSession().update(tbo);
tx.commit();
6,Hibernate在做级联删除的时候,如果主表方的inverse为false,会先将子表的外键设为null,然后再删除,如果外键设了not null限制,会有异常发生.如果inverse为true,则不会有将子表的外键设为null的步骤.
在inverse=false的时候,在删除主表对应的Object的时候,如果没有将子表对应的object加到主表对应的object的set里,只会将子表的外键设为null.
如果inverse=true,在删除主表对应的Object的时候,如果没有将子表对应的object加到主表对应的object的set里,只会删除主表,引发数据库异常.
7,cascade的delete 和delete-orphan的区别,在把一个主表对应的记录load或者get到session后,如果将子表的记录从主表对应对象的set里remove掉,delete-orphan会将子表记录delete掉.而cascade设为delete时,如果inverse为false,会将子表的外键设为null,如果inverse设为true,不会对子表进行操作.
8,对native SQL query进行cache的时候遇到的java.lang.ClassCastException: XXXX at org.hibernate.cache.StandardQueryCache.put(StandardQueryCache.java:83) 问题:SQLQuery.addScalar来解决.
Query q1 = session.createSQLQuery("select val from Hbtest h where h.id<3");
q1.setCacheable(true);
((SQLQuery)q1).addScalar("val", Hibernate.STRING);
9,对于一个非active的session进行commit和rollback会有异常,所以在rollback的时候最好进行如下判断:
if (null != tx && tx.isActive()) {
tx.rollback();
}
10,一个表可以对应多个class:
<class name="com.test.hb.Hbtest" table="HBTEST">
<class name="com.test.hb.Hbtest1" table="HBTEST">
11,如果一条数据库的数据被两个线程并发load然后做修改,会出现'丢失更新'.[需要利用LockMode来控制并发访问]