近日,在将一个基于jsf+seam+hibernate+db2的应用部署到jboss上时,出现这么一个报错
org.hibernate.HibernateException: The database returned no natively generated identity value
症状:可以select,update和delete,不能insert,insert就报上面这个错,id的生成有问题。
到网上搜索解决方法,都是说这是数据库中表的id没有设置自增引起的。可是我的数据库中表的id都设置了自增,为何也报这个错误呢?
跟踪代码发现Hibernate从insert语句的PreparedStatement.getGeneratedKeys()方法返回的ResultSet中获取生成的id,但没有获取到,报了这么个错误。
分析一下,报这个错,可能的原因有三:数据库的原因,JDBC driver的原因,hibernate的原因。
数据库的原因应该就是网上大部分人遇到的id自增长的问题,不过我这里不存在这个问题。
JDBC driver的原因,有可能,尝试换一个驱动试试看,可是没找到。。。。
hibernate呢?考虑到使用的jboss自带的hibernate,于是使用最新的稳定版本替换了一下,还是不行。。。
被该问题困扰整日,次日再搜索相关资料时,偶然发现一个hibernate jbdc的属性hibernate.jdbc.use_get_generated_keys,其介绍如下:
Enables use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve natively generated keys after insert.
Requires JDBC3+ driver and JRE1.4+, set to false if your driver has problems with the Hibernate identifier generators. By
default, it tries to determine the driver capabilities using connection metadata.
e.g. true|false
顿时灵光一闪,感觉找到了打开通向光明之门的金钥匙。
于是在persistence.xml中添加该属性的设置
<property name="hibernate.jdbc.use_get_generated_keys" value="false"/>
再一deploy,万事OK。
再次跟踪代码,发现原来通过该参数的设置,可以将id的获取委派到不同的类:
public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
PostInsertIdentityPersister persister,
Dialect dialect,
boolean isGetGeneratedKeysEnabled) throws HibernateException {
if ( isGetGeneratedKeysEnabled ) {
return new GetGeneratedKeysDelegate( persister, dialect );
}
else if ( dialect.supportsInsertSelectIdentity() ) {
return new InsertSelectDelegate( persister, dialect );
}
else {
return new BasicDelegate( persister, dialect );
}
}
hibernate.jdbc.use_get_generated_keys=true时,委派到GetGeneratedKeysDelegate,但该类的相关方法没搞定;
hibernate.jdbc.use_get_generated_keys=false时,这里委派到了 BasicDelegate,该类通过DB2Dialect的getIdentitySelectString()即使用db2的方言“values identity_val_local()”成功获取id。