资源描述
Hibernate中常见问题
No row with the given identifier exists问题的原因及解决
产生此问题的原因:
有两张表,table1和table2. 产生此问题的原因就是table1里做了关联<one-to-one>或者<many-to-one unique="true">(特殊的多对一映射,实际就是一对一)来关联table2.当hibernate查找的时候,table2里的数据没 有与table1相匹配的,这样就会报No row with the given identifier exists这个错.(一句话,就是数据的问题!)
假如说,table1里有自身的主键id1,还有table2的主键id2,这两个字段.
如果hibenrate设置的单项关联,即使table1中的id2为null值,table2中id2中有值,查询都不会出错.但是如果table1中的id2字段有值,但是这个值在table2中主键值里并没有,就会报上面的错!
如果hibernate是双向关联,那么table1中的id2为null值,但是table2中如果有值,就会报这个错.这种情况目前的解决办法就是改成单项关联,或者把不对应的数据改对!
这就是报这个错的原因了,知道原因了就相应的改就行了.或许还有些人迷惑hibernate关联都配好了,怎么会出现这样的错?其实这是编程的时候出现的 问题,假如说我在添加信息的时候,页面传过来的struts的formbean到dao方法中需要封装成hibernate的po(就是 hibenrate的bean),要是一个个po.get(form.set())实在太麻烦了,这样一般都会写个专门的方法来封装,遇到 po.get(form.set())这种情况直接把struts的formbean对象传到此方法中封装就行了,假如我有个字段是创建人id,那么这个 字段是永远不会改的,我在添加的时候还调用这个方法,这个专门封装的方法是有一些判断的,假如说我判断一下,如果遇到创建人id传过来为空值,我判断如果 是空值,我把创建人id设为0,但是用户表中userid是主键从1开始自增的,那么这样数据就对应不上了,一查就会出这个错了.这个错在开发刚开始的时 候经常发生,因为每个人的模块都是由相应的人独立开发完成以后再整合在一起的,每个人写单独那一块的时候往往会忽略这些,所以整合的时候这些问题往往就都 一下子全冒出来了....整合很辛苦,tnnd!
hibernate的查询的比较
hibernate的查询有很多,Query,find,Criteria,get,load
query使用hsql语句,可以设置参数是常用的一种方式
criteria的方式,尽量避免了写hql语句,看起来更面向对象了。
find方式,这种方式已经被新的hibernate丢弃
get和load方式是根据id取得一个记录
下边详细说一下get和load的不同,因为有些时候为了对比也会把find加进来。
1,从返回结果上对比:
load方式检索不到的话会抛出org.hibernate.ObjectNotFoundException异常
get方法检索不到的话会返回null
2,从检索执行机制上对比:
get方法和find方法都是直接从数据库中检索
而load方法的执行则比较复杂
1,首先查找session的persistent Context中是否有缓存,如果有则直接返回
2,如果没有则判断是否是lazy,如果不是直接访问数据库检索,查到记录返回,查不到抛出异常
3,如果是lazy则需要建立代理对象,对象的initialized属性为false,target属性为null
4, 在访问获得的代理对象的属性时,检索数据库,如果找到记录则把该记录的对象复制到代理对象的target
上,并将initialized=true,如果找不到就抛出异常 。
java.lang.IllegalArgumentException: node to traverse cannot be null!
错误原因:
通常此类错误都是由于HQL语句写的不正确,例如from写成了form,或者set A = 1 and B = 2,其中set不同字段用逗号","分离而不是用and.总之仔细检查HQL语句,看看有没有语法错误即可.
ids for this class must be manually assigned before calling save()..
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save():
引起问题的原因:
由Hibernate根据数据库表自动生成的"类名.hbm.xml"映射文件引起的。
首先我的表(Info)由两个字段组成,即:
int id;//主建
String name;
(自己做测试,所以就简单的建了个表)
由Hibernate生成的Info.hbm.xml中是这样写的:
-----------------------------------------------------
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="assigned"/>
</id>
-----------------------------------------------------
<id>这个是必须有的。它是用来定义实体的标识属性(对应数据库表的主键)
而我这里由于id本身就是主键,所以column的属性便是id
下面是很关键的一点<generator>,由于一时兴趣,于是找了很多资料,关于它的解释是:用于指定主键的生成策略。它的值有多,下面是转来的:
--------------------------------------------------------------------------------
“assigned”
主键由外部程序负责生成,在 save() 之前指定一个。
“hilo”
通过hi/lo 算法实现的主键生成机制,需要额外的数据库表或字段提供高位值来源。
“seqhilo”
与hilo 类似,通过hi/lo 算法实现的主键生成机制,需要数据库中的 Sequence,适用于支持 Sequence 的数据库,如Oracle。
“increment”
主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:不能在集群下使用。
“identity”
采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL 中的主键生成机制。
“sequence”
采用数据库提供的 sequence 机制生成主键。如 Oralce 中的Sequence。
“native”
由 Hibernate 根据使用的数据库自行判断采用 identity、hilo、sequence 其中一种作为主键生成方式。
“uuid.hex”
由 Hibernate 基于128 位 UUID 算法 生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
“uuid.string”
与uuid.hex 类似,只是生成的主键未进行编码(长度16),不能应用在 PostgreSQL 数据库中。
“foreign”
使用另外一个相关联的对象的标识符作为主键。
--------------------------------------------------------------------------------
看了上面的介绍,再看看代码,原来是<Generator>属性设置有问题。
然后改为"identity"、"native"问题便解决。
使用Hibernate设计Dao层的几种方式
直接奔主题,说一说数据库访问层的问题,该层操作数据库的实现方式有很多种,当然各有各的好处,选择哪一种,根据你的心情而定,dao层所做的事情就是单纯的数据CRUD,不做别的事情,如果你发现你的dao层存在业务逻辑,那么赶紧在项目没有做大之前改掉吧,因为我就曾今看到过我以为同学,把DAO层和Service层搞混了,dao存在于大多数软件工程中,它已成为程序架构必不可少的组成部分,当然,dao是可拔插的,如果你的dao写死在了某个程序里,那说明你对于dao的理解并不透彻,试着把你的dao移植到别的程序里吧,如果不能做到“一次编写,到处运行”,那就考虑重构你的dao层吧!
第一种:直接写JDBC来实现数据库的操作,这种方式是最原始的,当然,如果你对于写JDBC非常的熟悉,那没问题,但是这并不是一个追求上进的程序员做的事,久而久之,这将是一件痛苦的事,而不是一件值得炫耀的事……
那么以下的几种方法就是直接通过或间接通过hibernate框架来实现dao层的设计了,因为hibernate够强大,够流行,够灵活,曾经试过使用JBoss写DAO,的确不怎么爽!
第二种:使用hibernate的session实现数据库操作,这种方式是使用hibernate的最基础的方式,也是最灵活的一种方式,因为session实现了hibernate的所有数据库操作方法,剩下的就看你如何组装这些语句完成你的程序逻辑了
第三种:继承Spring的HibernateDaoSupport,Spring为Hibernate的Dao提供的工具类,其底层是通过HibernateTemplate来实现数据库的操作,但是使用这个工具类,有些地方,对于数据库的操作并不够灵活,曾经我为了这些问题纠结了很久,也许是我个人对这个类的熟悉度有限,如果有哪位童鞋能灵活的运用此类,还望指点一二
第四种:使用HibernateTemplate,它提供了非常多的常用方法来完成数据库的基本操作,使得持久层访问摸板化,这种方式其实和第三种差不多,不多做解释,但是要灵活的运用该模板,还得知道第五种方式
第五种:Hibernate的复杂用法HibernateCallback回调函数,通过调用回调函数来实现的数据库操作,这种方式可以完全使用Hibernate的 session操作数据库,这也是我最喜欢用的方法,因为它够强大,够灵活,够高深
第六种:使用EntityManager,EntityManager里也封装了hibernate对数据库的操作,可以通过@PersistenceContext注解为其注入实例,但是本人对于此类的使用方法并不是很熟悉,也只使用过一次而已,还在学习中
以上只是列举了我使用过的DAO层的设计方案,并没有提供具体的实现代码,这里写代码的确不方便,以上的六种实现方式,都有各的好处,也遇到过一些细节上的问题,只有经过更多的人的使用,才可以找到问题,并且去解决问题,对于有意了解使用以上几种方式的详情的童鞋,可以一起讨论讨论,并提供你的实现源码!
如何打破多表关联的关系
在多表关联的关系中,如果要删除其中一个表中的一条子数据,必定与这个表,这条数据有关联的数据也会被删除,但是这样的结果却不是我们要见到的。而我们要的效果是,1.删除子数据,父数据不被删除,与关联的表也不删除。2.删除父数据,而父数据下的子数据,孙子数据也会被删除,而与它关联的表也不删除。而怎样实现这样的结果呢,这就需要我们去打破他们之间的关系了。怎么打破呢,看下面的代码。
----这是一个个人空间中说说表say的删除代码-----------
发表一条说说,要知道发表说说的人,和回复说说的人,而这俩者都是一个user表里面的。
所以说,这个说说表say是跟用户表user是关联的,用户表跟说说表是一对多的关系。
-------删除父说说------
@Transactional
public boolean deleteFatherSay(Integer sayId) {
Say say = sayDao.findById(sayId); //获取"父节点"
Iterator<Say> iter = say.getSays().iterator(); //获取"父节点"下的"子节点",并存在迭代器里面
for (int i = 0; i < say.getSays().size(); i++) {//循环遍历每个"子节点"
Say say1 = iter.next(); //获取单个的子节点
Iterator<Say> iter1 = say1.getSays().iterator(); //获取单个"子节点"下"孙子节点",并存在迭代器里面
for (int j = 0; j < say1.getSays().size(); j++) {//循环遍历每个"孙子节点"
Say say2 = iter1.next(); //获取单个的孙子节点
say2.setUser(null); //打破"孙子节点"与user的关联
say2.setSay(null); //打破"孙子节点"与"子节点"的关系(删除"孙子节点"时,就不会把与"孙子节点"相关联的"子节点"也删除)
sayDao.delete(say2);
}
say1.setSay(null);
say1.setUser(null);
sayDao.delete(say1);
}
say.setUser(null);
sayDao.delete(say);
return true;
}
----删除子说说----
@Transactional
public boolean deleteSonSay(Integer sayId) {
Say say = new Say();
say.setSayId(sayId);
String hql = "from Say as say1 where say1.say=" + sayId;
List<Say> list = sayDao.findByHql(hql);
Iterator<Say> iter = list.iterator();
for (int i = 0; i < list.size(); i++) {
Say say1 = iter.next();
say1.setUser(null);
say1.setSay(null);
sayDao.delete(say1);
}
say.setSay(null);
say.setUser(null);
say.setSays(null);
sayDao.delete(say);
return true;
}
---删除孙子说说---
@Transactional
public boolean deleteGrandsonSay(Integer sayId) {
Say say = sayDao.findById(sayId);
say.setSay(null);
say.setUser(null);
say.setSays(null);
sayDao.delete(say);
return true;
}
----------结论-----------
如何打破他们的关系,就要知道一点,先打破下层关系,再打破上层的关系,这是自下而上来打破它们间的关系。
父---子---孙子,(说说表say和用户表user有关联,所以say.setUser(null),这样就打破了和用户表之间的关联关系).
1.删除孙子的说说,这要把上层的关系打破就行了say.setSay(null),这样删除孙子说说,就不会删除子说说和父说说。
2.删除子说说(子说说是夹在父说说和孙子说说之间),还是按照自下而上的规则,先找出子说说的所有孙子说说,然后用for循环遍历每一个孙子说说,然后再打破孙子say.setSay(null);say.setUser(null);和上层的关系,最后删除孙子说说,然后子说说在打破和上层父说说的关系say.setSay(null);say.setUser(null);,最后删除子说说,那么子说说下的孙子说说也全部删除,而父说说则不会影响到。
3.删除父说说,那么就要循环删除子说说和孙子说说,及要打破他们各自的关系。删除完父说说的子说说和孙子说说,最后再打破和用户表的关系say.setUser(null).这样就能删除父说说的同时也删除完他下面的说说,而不会影响到用户表user.
展开阅读全文