2011-11-30 78 views
5

我意识到这是一个比Grails更多的休眠问题。在一个负载均衡(2个节点)的环境中,我看到我的对象的ID正在跳转很多。即使没有重新启动应用程序服务器,我也会看到数字跳过10,有时候是20个数字。我怀疑休眠会话正在缓存一个序列值块。 有没有办法用grails 1.3.7来控制这种行为?本质上,我确定服务器每次需要时都从数据库中拉取nextval。针对Oracle 11g的Grails序列生成

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

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

回答

3

因为我已经去到数据库并修改与以下DDL序列:

ALTER SEQUENCE MY_SEQ NOCACHE; 

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

谢谢大家!

+1

这应该很好。我认为Hibernate总是会调用MY_SEQ.NEXTVAL,而不管序列是否被缓存;缓存在Oracle内部发生,而不是在Hibernate或JDBC级别。 请注意,如果您的应用程序依赖于没有数字序列中的空白,那么使用Oracle序列可能不是正确的方法。不能保证你在序列中没有任何差距 - 你可能有不同的线程获取序列值,或者你可能有回滚。 –

3

缓存问题是因为Hibernate默认为一个Oracle方言做两件事情。它为所有主键生成的表格创建一个序列共享序列,并且序列缓存每次20个数字,如果它们没有在特定的时间范围内使用,则Oracle将放弃其余部分。

下面的Oracle解决方案来自Burt Beckwith的一篇文章,其中有一段tweek用于防止Oracle序列缓存数字。因此,这种方言会为你做两件事:

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

既然你是在你很可能带出每个表的逻辑顺序,并在NOCACHE序列定义以达到您想要的结果离开映射定义你的表的顺序。

此外,您需要从现有表空间中删除序列,因为Grails不会重新创建它,除非在createcreate-drop方案中。当你这样做的时候,你也可能想要提高新序列的起始值,以防止数据库已经使用的密钥与主密钥相冲突。

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

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; 
     } 
    } 
} 

此链接对这个问题的一些历史,并链接到伯特的原代码,并且对于PostgreSQL的响应:Hibernate & postgreSQL with Grails

+0

谢谢!你是否说没有办法在params中为序列指定NOCACHE或allocationSize? – dbrin

+0

据我所知,不会,因为这将是数据库特定的SQL,它会将您的应用程序绑定到特定品牌的数据库。 – schmolly159

+0

对不起。这不是我正在寻找的解决方案。我会将其标记为对其他人有用。查看我的解决方案以获得其他简单修复...至少我认为是这样:) – dbrin