Oracle 11g的Grails序列生成

时间:2011-11-30 22:37:42

标签: hibernate grails

我意识到这比Grails更像是一个休眠问题。在负载平衡(2节点)环境中,我看到我的对象的id跳了很多。即使没有重新启动应用服务器,我也看到数字有时会跳过10个数字。我怀疑hibernate会话正在缓存一个序列值块。 有没有办法用grails 1.3.7来控制这种行为?基本上我没关系服务器每次需要时从数据库中提取nextval。

我的域对象序列声明(对于2个对象相同):

static mapping = {
        id generator:'sequence', params:[sequence:'MY_SEQ']  
    } 

2 个答案:

答案 0 :(得分:3)

缓存问题是因为Hibernate默认使用Oracle方言,它执行两项操作。它创建了一个在所有表中共享的序列,用于生成主键,序列一次缓存20个数字,如果它们未在特定时间范围内使用,Oracle将丢弃其余的数据。

以下Oracle解决方案来自Burt Beckwith的帖子,其中包含一个防止Oracle序列缓存数字的内容。因此,这种方言会为你做两件事:

  • 它将为每个表创建一个序列,因此不会共享序列,并且不会在表之间拆分主键号。
  • 它将禁用Oracle中数字的缓存,因此您不会丢失过期缓存中的任何序列号。这是使用PARAMETERS命令在NOCACHE属性中设置的。

由于您在映射中定义了表的序列,因此可能会删除每个表逻辑的序列,并保留NOCACHE序列定义以获得所需的结果。

此外,您需要从现有表空间中删除序列,因为Grails不会重新创建它,但createcreate-drop方案除外。执行此操作时,您可能还希望提高新序列的起始值,以防止数据库已使用的密钥发生主键问题冲突。

使用方言将dialect = SequencePerTableOracleDialect添加到dataSource定义闭包中的DataSource.groovy文件中。

import java.util.Properties;

import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.Oracle10gDialect;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.SequenceGenerator;
import org.hibernate.type.Type;

public class SequencePerTableOracleDialect extends Oracle10gDialect {
    public static final String PARAMETERS = "MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 NOCACHE NOCYCLE";

    /**
     * Get the native identifier generator class.
     * 
     * @return TableNameSequenceGenerator.
     */
    @Override
    public Class<?> getNativeIdentifierGeneratorClass() {
        return TableNameSequenceGenerator.class;
    }

    /**
     * Creates a sequence per table instead of the default behavior of one
     * sequence.
     */
    public static class TableNameSequenceGenerator extends SequenceGenerator {

        /**
         * {@inheritDoc} If the parameters do not contain a
         * {@link SequenceGenerator#SEQUENCE} name, we assign one based on the
         * table name.
         */
         @Override
         public void configure(final Type type, final Properties params,
                 final Dialect dialect) {
             if (params.getProperty(SEQUENCE) == null
                    || params.getProperty(SEQUENCE).length() == 0) {
                 /* Sequence per table */
                 String tableName = params
                        .getProperty(PersistentIdentifierGenerator.TABLE);
                 if (tableName != null) {
                     params.setProperty(SEQUENCE, createSequenceName(tableName));
                 }
                 /* Non-Caching Sequence */
                 params.setProperty(PARAMETERS, SequencePerTableOracleDialect.PARAMETERS);
            }
            super.configure(type, params, dialect);
        }

        /**
         * Construct a sequence name from a table name.
         * 
         * @param tableName
         *            the table name
         * @return the sequence name
         */
        String createSequenceName(final String tableName) {
            return "seq_" + tableName;
        }
    }
}

此链接在此问题上有一些历史记录,包含Burt原始代码的链接以及PostGreSql的响应:Hibernate & postgreSQL with Grails

答案 1 :(得分:3)

我已经进入数据库并使用以下DDL修改了序列:

ALTER SEQUENCE MY_SEQ NOCACHE;

我认为这是此问题的最佳解决方案。有没有人看到这种方法的潜在问题?

全部谢谢!