继续测试时发现,使用 mysql 5.1 版本的时候,junit 无法通过测试。

测试方法都是把数据库中的表全部删除,通过运行测试案例时,让系统自动建立数据库。

在 jboss 中运行包 msweb 时,这时在 junit 测试中插入的数据可以正常列出来,可以编辑和删除。可是进行添加的时候,数据添加不成功。服务器中输出的错误是:
ERROR com.jdon.persistence.hibernate.HibernateTemplate - exception while execute
org.hibernate.PropertyValueException: not-null property references a null or transient value: sample.model.User.address
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)

...

at org.hibernate.impl.SessionImpl.save(SessionImpl.java:519)
at com.jdon.persistence.hibernate.HibernateTemplate$12.execute(HibernateTemplate.java:259)
at com.jdon.persistence.hibernate.HibernateTemplate.doHibernate(HibernateTemplate.java:74)
at com.jdon.persistence.hibernate.HibernateTemplate.save(HibernateTemplate.java:257)
at com.jdon.persistence.hibernate.HibernateCRUDTemplate.insert(HibernateCRUDTemplate.java:68)
at com.jdon.persistence.DaoCRUDTemplate.insert(DaoCRUDTemplate.java:54)
at sample.service.UserServiceImp.create(UserServiceImp.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

...

> not-null property references a null
这些都是Hibernate的错误:你数据表字段大概设置不为空,但是对应对象字段为空。

你现在已经进入深入调试,建议使用Eclipse断点调试跟踪程序,很难根据错误结果准确推断到错误原因的。

http://www.jdon.com/jivejdon/thread/33515.html

终于调试通过了。其实问题出在数据库的约束违例上。

当前的数据库设置是, user 表中的 addressId 不允许为空。而问题恰恰出现在这里。

为什么在 junit 上的插入操作可以完成而在 msweb 上操作却不能完成呢?我们看一下这两者保存数据时的区别。

在 junit 上,数据保存时,是按一定的顺序保存的。
其顺序是,先保存 address 记录,然后把 address 记录设置给 user ,这时保存 user 的时候, address 的主键已经不是空了,所以保存可以正常进行。

而在 msweb 上操作时,数据的保存是让 hibernate 自己的内部机制来保存的。通过日志输出可以看到,hibernate 先保存 user 数据,其语句输出如下:
Hibernate: insert into nec_userb (name, deptid, addressid) values (?, ?, ?)
接着再保存 address、property 等数据:
Hibernate: insert into nec_address (name, id) values (?, ?)
Hibernate: insert into nec_property (name, value, id) values (?, ?, ?)
...

而我们看一下 User.hbm.xml 里相应 address 的配置:
...
<many-to-one name="address" class="sample.model.Address" column="addressid" unique="true" not-null="true" cascade="all"></many-to-one>
...

当 hibernate 进行保存 user 数据的时候,由于 address 数据还没有保存,也就不存在 addressId ,而 user 表中的 addressId 又不允许为空,所以保存的时候,自然出错了。

因此更正的办法是,修改 User.hbm.xml 里相应 address 的配置为:
...
<many-to-one name="address" class="sample.model.Address" column="addressid" cascade="all"></many-to-one>
...

这时数据保存正确,msweb 运行时不再有错误。
当然更好的办法是在设置“双向一对多关联”,这个可以参考《深入浅出Hibernate》中的第4.4章节里的“双向一对多关联”(P157)。

Banq,有一个问题没想清楚,请教一下。

在 UserService 接口中,有两个方法:
public abstract User getUser(Integer id);
public abstract User findUser(Integer id);

我看了一下,这两个方法在 UserServiceImp 实现时,就是 findUser 方法调用 getUser 方法来实现的。我搞不明白为什么要实现两个方法。能解释一下吗?

这是由于设计原因考虑,具体已经不是很清楚。

getXXX 与findXXX区别在于,get会被缓存拦截,而find不会。

在jivejdon3中,进行这两个区分则是为权限问题,get会检查权限,而find不会

谢谢 banq ,Jdon 框架确实有意思。熟悉后做快速开发应该是蛮有效的。我现在谈不上熟悉,但已经能感受到一点快速开发的味道了。

当我把 user 表中的数据清空后再运行这个测试案例的时候,运行到这个语句
User user = (User)session.get(User.class, 1);
又运行不下去了。后来我定义了一个类变量:
private Integer newUserId;
在语句
private HibernateTemplate ht;
后面,然后在方法 testCreate() 的语句
ht.save(user);
后添加了保存这个新用户的 id 语句:
newUserId = user.getId();
最后把方法 testGetUser() 中的读取用户信息的语句改为:
User user = (User)session.get(User.class, newUserId);
但测试时仍然出错,我把 newUserId 打印输出后发现它的值竟然是 null
于是再把类变量定义语句定义为静态变量:
private static Integer newUserId;
这时,测试终于不再出错。

测试程序中的原语句中的
session.get(User.class, "402881e614ed5dbd0114ed5dbf660004");
字符串,应该是在 banq 测试时,在他的机器上产生的新用户 id
我现在通过静态变量从 testCreate() 方法传入 testGetUser() 后,这个程序应该可以跑在任意的机器上了。
[该贴被lqixv于2008-03-11 12:41修改过]

测试时发现,方法:
public void testUpdate()
中有语句
User user = (User)ht.load(User.class, 0);
但这里不管后面的 id 是什么,junit测试总是绿条通过。但id对应的用户不存在时,控制台会抛出异常。
所以我觉得这个测试方法没有完成其测试的作用。应该还需要修改。

测试案例通过后,查看控制台输出,发现还是有一部分错误输出,如:
ERROR com.jdon.persistence.hibernate.HibernateTemplate - exception while execute
org.hibernate.SessionException: Session is closed!
...
似乎是 hibernate 的 session 开关不正常。但数据没有问题。我用的是 sql server 2000 数据库测试的,不知是不是这个的问题引起的错误。

banq,不好意思,我因为想用这个框架开发项目,所以可能有点太过于较真了 :P

小弟重新做这个示例时,新建时报
23:45:28,280 INFO [STDOUT] 23:45:28,280 ERROR [PARSER] line 1:1: unexpected token: form
23:45:28,280 INFO [STDOUT] 23:45:28,280 ERROR [HibernateTemplate] exception while execute
java.lang.IllegalArgumentException: node to traverse cannot be null!

。。。。。。。。

ERROR [STDERR] java.lang.Exception: java.lang.IllegalArgumentException: node to traverse cannot be null!
23:45:28,280 ERROR [STDERR] at com.jdon.persistence.hibernate.HibernateTemplate.doHibernate(HibernateTemplate.java:86)
23:45:28,280 ERROR [STDERR] at com.jdon.persistence.hibernate.HibernateTemplate.find(HibernateTemplate.java:450)
23:45:28,280 ERROR [STDERR] at com.jdon.persistence.hibernate.HibernateTemplate.find(HibernateTemplate.java:442)
23:45:28,280 ERROR [STDERR] at org.mark.sample.dao.JdbcDaoImp.getDepts(JdbcDaoImp.java:40)
23:45:28,280 ERROR [STDERR] at org.mark.sample.service.UserServiceImp.getDepts(UserServiceImp.java:67)
23:45:28,280 ERROR [STDERR] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
23:45:28,280 ERROR [STDERR] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
23:45:28,280 ERROR [STDERR] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
23:45:28,280 ERROR [STDERR] at java.lang.reflect.Method.invoke(Method.java:585)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.reflection.MethodInvokerUtil.execute(MethodInvokerUtil.java:54)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.reflection.ProxyMethodInvocation.methodInvoke(ProxyMethodInvocation.java:108)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:76)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.interceptor.SessionContextInterceptor.invoke(SessionContextInterceptor.java:76)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
23:45:28,280 ERROR [STDERR] at com.jdon.aop.interceptor.StatefulInterceptor.invoke(StatefulInterceptor.java:82)
23:45:28,296 ERROR [STDERR] at com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
23:45:28,296 ERROR [STDERR] at com.jdon.aop.interceptor.PoolInterceptor.invoke(PoolInterceptor.java:91)
23:45:28,296 ERROR [STDERR] at com.jdon.aop.reflection.ProxyMethodInvocation.proceed(ProxyMethodInvocation.java:84)
23:45:28,296 ERROR [STDERR] at com.jdon.aop.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:58)

你使用这个案例的环境是什么?
jdk?Hibernate?数据库?

今天重新把这个压缩包下载下来后发现,里面的错误基本上改了(我原来提的所谓错误其实有些不是错误),但测试的时候还是有一些需要改正的地方:

1、方法:testCreate()中,在第93行语句
ht.save(user);
后必须加上关闭 session 的语句,否则如果单独测试这个方法,数据是不会更新保存到数据库中。

2、测试“lazy櫴加载”的方法:testGetUser()中,必须把第108行语句:
session.close();
改为:
ht.getSessionProvider().closeSession();
否则后面所有的方法测试都会抛出 session 已关闭的异常。
同时,可以把已被注释掉的第104、105、106行语句
// User user = (User)session.get(User.class, 402881e614ed5dbd0114ed5dbf660004");
// System.out.print(" ++++++++deptname=" + user.getDept().getName());
//System.out.print(" ++++++++address=" + user.getAddress().getName());
的注释去掉,并把第104行改为:
User user = (User)session.get(User.class, 1);
这样,这个方法就可以正常运行。

3、方法testUpdate()在删除属性语句:
properties.remove(deleteprop);//删除操作
的后面,应该加上删除属性的操作语句:
ht.delete(deleteprop);
这样才能真正从数据库删除这条属性记录。否则的话,hibernate 只把这个属性对应于数据库中的记录的 userid 清为 null,而不会删除这条记录。原因是,虽然在 User.hbm.xml 中,定义了 <bag name="properties" cascade="all"> ,cascade="all"使得主控方的所有操作都级联到被控方,但由于此时主控方并没有做删除操作,只是做了修改操作,使得被控方也只是做了修改操作,而本应删除的属性记录没有得到删除。当加上 ht.delete(deleteprop); 语句后,这条记录才真正被删除。

呵呵,帮 banq 改点小 bug ,也算是对 jdon 的支持吧。

第一次运行这个案例前要注意,最好先运行一下这个案例中的测试类,让其在数据库中建好表,并添加数据,这样再部署在 web 中的时候就不会出错。否则的话,那就必须在数据库中建好表,并在表 nec_dept 最少添加一条数据。如果这个表是空的话,运行 web 程序时就无法添加数据成功,并且会显示错误。

部署在 jboss 后,如果不手工删除其中冲突的一些日志包,如 log4j.jar ,那程序可以正常运行,但日志输出会有错误。
[该贴被lqixv于2008-03-18 18:10修改过]

非常感谢 已经纠正。