现在的位置: 首页 > 综合 > 正文

用oracle的trigger生成主键的时候和hibernate冲突的讨论

2013年05月25日 ⁄ 综合 ⁄ 共 2717字 ⁄ 字号 评论关闭

oracle的trigger和hibernate冲突

capitain write:

我们的数据库定义mapping使用了

<id name="id" column="ID" type="long">
    <generator class="sequence">
        <param name="sequence">seq_somename</param>
    </generator>
</id>

但是这样做 我发现 hibernate会取得一次sequence, 同时数据库还有一个triger会取得一次sequence, 这样实际的sequence会加2了!! 而且create返回的对象的主键id也是错误的, 因为还被triger加了1, 数据库里面的记录全面是双数, 2,4,6,8 create返回主键却是1,3,5,7 请问 这样问题怎么解决, 因为triger不能删除, 能让hibernate利用triger生成的取值吗?

我查看了hibernate的文档和源代码, 发现generator 为 sequence时候

它最后会调用这个方法

dialect下面的Oracle9Dialect中的

    public String getSequenceNextValString(String sequenceName) {
        return "select " + sequenceName + ".nextval from dual";
    }

triger再这么走一遍 sequence必然是加入2次

我使用native,发现工作结果和sequence一样, 难道没有办法解决了吗?

robbin write:

我有一个最简单的解决办法:

Hibernate2.0.3源代码的net.sf.hibernate.dialect.Oracle9Dialect第78行:

public String getSequenceNextValString(String sequenceName) {
  return "select " + sequenceName + ".nextval from dual";
}

只要我们把它改成下面这个样子,就行了:

public String getSequenceNextValString(String sequenceName) {
  return "select " + sequenceName + ".currval + 1 from dual";
}

也就是说Hibernate去取序列值的时候,不让序列加1,只取当前值然后加1返回,插入的时候让trigger来给序列加1

因此你可以这样来做,写一个新的Dialect:

package net.sf.hibernate.dialect;

public class OracleMyDialect extends OracleDialect {
  public String getSequenceNextValString(String sequenceName) {
    return "select " + sequenceName + ".currval + 1 from dual";
  }
}

编译好以后放在CLASSPATH里面,配置Hibernate

hibernate.dialect net.sf.hibernate.dialect.OracleMyDialect

这样就行了,解决掉你那个问题了。

capitain write:

谢谢, 我对oracle很不熟悉, 不知道,这样只加一 如果在多个连接向数据库内插入记录的时候, 会不会有问题,

换句话说,不知道hibernate里面这个getSequenceNextValString()和triger发生之间时间内如果有插入会有问题吗? 不知道oracle的驱动能不能保证这是一个原子操作?

robbin write:

不能够保证原子性,可能会有并发访问的冲突。不过目前来说你也只能如此了,说实话这个办法只能算是权宜之计。

由于session.save() 需要返回主键,所以当你使用trigger来创建id的时候,Hibernate是无法知道trigger当前创建的主键是什么,不论你用

select sequ.currval from dual;

还是

select max(id) from table_name;

在插入数据之后来获得主键,都有并发冲突的可能性。唯一的避免并发冲突的办法是:

insert into table_name ...   returning :id;

用Oracle特有的这个sql来插入数据,返回主键,让Hibernate知道主键的值,不过Hibernate目前并不能够支持这个sql语句,如果要让Hibernate支持这个sql,需要对源代码做相当大的改动。看看以后会不会加吧。

这个问题我不是第一次和别人讨论了,确实没有好的解决办法,除非对Hibernate进行大面积修改。但是话说回来,我用Oracle那么多年了,从来都不使用trigger实现Auto increment字段,我个人认为这种方式不好。本来Oracle已经把sequence从字段里面解放出来了,你还要在把它放回去。不用trigger一样可以生成主键,看两个例子:

插入单条数据:

insert table_name(id, name,...) values(sequ.nextval, ....);

批量插入数据:

insert table_name(id, name,...)  select sequ.nextval, name,...  from table2_name;

richart write:

这么做的话,最好是用一下nvl()函数(oracle自带),而且只能改动oracle相关的一个类。如果使用的是别的数据库,不知道有没有相似的问题存在?

robbin write:

oldma 写道:呵呵,谁能给解释一下?

看下面的PL/SQL:

create trigger y before insert on bob
  for each row
  when (new.a is null)
  begin
    select x.nextval into :new.a from dual;
  end;

用trigger来读sequence,插入表的主键字段,模拟SQL Server中的Auto Increment 字段

capitain write:

那oracle还有什么标准的办法实现Auto Increment 字段? 我们目前的程序插入的时候是按照auto increment字段来的, 就是不写id那个字段, 让数据库操作, 请问其他办法可以适用吗?

mikeho write:

是的,oracle是不必再写sp了,我们以前是在SQL server里面写了个sp来生成seq,没有用自增长,因为自增长在方法调用后返回id比较麻烦。现在都使用模拟UUID了(只有50位,理论上有重复建的危险,呵呵)。

抱歉!评论已关闭.