前言
Hibernate作为一个成熟的持久层解决方案,通过简单的配置,可以在不修改源码的情况下,无缝的在不同的数据库上运行。尽管如此,使用不同的数据库仍然会出现一些问题,比如本文要讨论的Oracle中的问题。
1. Hibernate无法创建hibernate_sequence的问题
错误提示:
问题分析:
从错误提示上看,查询语句没有找到hibernate_sequence这个序列。到Oracle数据库中查看,果然没有。按道理说,hibernate应该在Oracle中自动生成这个序列,那为什么没有生成呢?原因主要有两个:
1.hibernate的核心配置中没有设置自动建对象的属性,导致无法自动建立序列:
<property name="hibernate.hbm2ddl.auto">update</property> |
2.其他用户存在hibernate_sequence这个序列,导致当前用户不再创建该序列。
原因:
hibernate在启动时即需要去检测所要创建的序列是否存在,如果存在,则不再创建此序列。在某些情况下,hibernate能够探测到其它用户所创建的同名序列,而被认为不再需要创建序列,这时候如果进行保存数据的操作,就会因为在当前用户下找不到该序列从而导致出错。
要解决这个问题的首要方法是需要找到hibernate判断指定序列是否存在的方法,在hibernate的dialet中,取决于getQuerySequencesString这个方法,此方法返回了查询所有序列的方法。
Hibernate中的与Oracle相关dialet一般配置为Oracle10gDialect、Oracle9iDialect、Oracle8iDialect。从源码我们可以看到他们之间是继承关系,如:Oracle10gDialect extend Oracle9iDialect extend Oracle8iDialect extend Dialet。通过源代码我们可以看出,该方法只是在Oracle8iDialect中进行了覆写,其子类都沿用了该实现。实现代码如下:
public String getQuerySequencesString() { return " select sequence_name from all_sequences" + " union" + " select synonym_name" + " from all_synonyms us, all_sequences asq" + " where asq.sequence_name = us.table_name" + " and asq.sequence_owner = us.table_owner"; } |
从代码分析看,它是从all_sequence中寻找所能找到的sequence,而当前用户能找到的sequence全在于当前用户能有多大的权限,以及具体的oracle jdbc实现。在这种情况下,如果当前用户拥有dba权限(开发时候,经常给一个用户授dba权限)或者由于其它某种原因,就能够找到其他用户的sequence了。而如果其他用户也创建了一个叫hibernate_sequence的sequence,那hibernate在当前用户就不能再创建sequence了。
知道了这个原因后,如何解决呢?
我们可以创建一个新的OracleDialect,直接继承于Oracle10gDialect,并在新的类中覆盖getQuerySequencesString方法,将其中的sql语句改为从user_sequences(即当前用户的sequence空间)中寻找相应的序列,这样就不会寻找到其它用户的序列了。参考代码如下:
public class Oracle10gDialectEx extends Oracle10gDialect { public String getQuerySequencesString() { return "select sequence_name from user_sequences" ; } } |
还有个解决办法,就是去掉该Oracle用户的跨用户访问的权限,如select any table的权限或dba角色。
2. 使用hibernate生成表不能正确创建表的问题
问题:
hbm2ddl.auto配置成update时,发现hibernate并没有按照默认的生成规则生成相应的数据表信息
分析发现,这里需要引用的表p_menu在另一个用户空间里已经存在了,而hibernate在创建表时,在另一个用户空间中找到了这个表,故不再在当前的用户空间中创建这个表了。而在创建关联表时,由于关联的是本用户空间的表,故有此错误。
hibernate使用了jdbc默认的databasemeta来寻找相应表数据信息,当使用默认的配置时,由于某种原因(并不是每次都能发生,取决于数据库本身以及相应的驱动)。当使用当前用户连接到数据库时,使用databasemeta寻找数据库表信息时,会查询出其它用户的数据表信息(即使当前用户没有相应的权限)。
两种解决方案:
1.在hibernate核心配置中配置一个属性:
< property name = " hibernate.default_schema" > 当前连接用户</ property > |
2.在hbm文件配置中的class元素中指定schema=""的属性
<class name="class" table="table" schema="当前连接用户 "> |
3.去掉该Oracle用户的跨用户访问的权限,如select any table的权限
3. SSH启动中的输出日志中有createClob的info信息的问题
在使用SSH+Oracle启动的时候,控制台会打印:
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
如果是使用的Oracle10G,实体类使用了Clob字段的时候,会抛出这个信息。
如果使用的是Oracle11G,实体类使用了复杂的数据类型的时候,也会抛出这个信息。
分析:
这个东西只是个 INFO 级别的,也就是说仅仅是提醒,无关紧要的。但总觉的不爽。如何解决呢?
从驱动上寻找解决方案:
使用Oracle10G,将默认的JDBC驱动ojdbc14升级为更高版本,如Oracle11G的ojdbc6.jar的版本。
从hibernate上寻找解决方案:
修改hibernate配置项hibernate.temp.use_jdbc_metadata_defaults=false。
另外:Hibernate4的官方资料里说11g和10g都是Oracle10gDialect,方言通用,这个没啥影响。