2011-11-30 16 views
5

Mi rendo conto che si tratta di una questione di ibernazione più grave di Grails. In un ambiente con carico bilanciato (2 nodi) vedo che gli ID dei miei oggetti stanno saltando un po '. Anche senza riavviare il server delle app vedo che i numeri saltano 10 a volte 20 numeri. Sospetto che la sessione di ibernazione stia memorizzando nella cache un blocco di valori di sequenza. C'è un modo per controllare questo comportamento con Grails 1.3.7? Essenzialmente sono OK con il server che tira nextval dal DB ogni volta che ne ha bisogno.Generazione sequenza Grails per Oracle 11g

mia dichiarazione dominio sequenza oggetto (lo stesso per 2 oggetti):

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

risposta

3

allora ho andato al database e modificato la sequenza con il seguente DDL :

ALTER SEQUENCE MY_SEQ NOCACHE; 

Penso che questa sia la soluzione migliore per questo problema. Qualcuno vede potenziali problemi con questo approccio?

Grazie a tutti!

+1

Questo dovrebbe funzionare bene. Penso che Hibernate chiamerà sempre MY_SEQ.NEXTVAL indipendentemente dal fatto che la sequenza sia memorizzata nella cache o no; il caching avviene internamente in Oracle, non nel livello Hibernate o JDBC. Si noti che se l'applicazione dipende dal non avere spazi vuoti in una sequenza di numeri, quindi l'utilizzo di una sequenza Oracle potrebbe non essere l'approccio giusto. Non vi è alcuna garanzia che non vi siano lacune nelle sequenze: si potrebbero avere thread diversi che ottengono valori di sequenza o si potrebbero avere dei rollback. –

3

Il problema è dovuto al fatto di cache di default Hibernate per un dialetto di Oracle, che fa due cose. Crea una sequenza condivisa su tutte le tabelle per la generazione di chiavi primarie e la sequenza memorizza nella cache 20 numeri alla volta, che se non vengono utilizzati entro un periodo di tempo specifico, Oracle eliminerà il resto.

La seguente soluzione Oracle deriva da un post di Burt Beckwith, con un tweek per impedire che la sequenza Oracle memorizzi i numeri nella cache. Così, questo dialetto farà due cose per voi:

  • Si creerà una singola sequenza per ogni tabella, in modo che la sequenza non è condivisa e numeri chiave primaria non sono suddivisi tra i tavoli.
  • Disabilita la memorizzazione nella cache dei numeri in Oracle, quindi non perderai alcun numero di sequenza in una cache scaduta. Questo è impostato nella proprietà PARAMETERS con il comando NOCACHE.

Dal momento che si sta definendo la sequenza della vostra tabella nella mappatura probabilmente si potrebbe togliere la sequenza logica per ogni tavolo e lasciare nella definizione NOCACHE sequenza per ottenere i risultati desiderati.

Inoltre, è necessario rilasciare la sequenza dal tablespace esistente, poiché Grails non la ricrearà, tranne negli scenari create e create-drop. Quando si esegue questa operazione, si potrebbe anche voler aumentare il valore iniziale della nuova sequenza per evitare problemi di chiave primaria in conflitto con le chiavi già in uso dal database.

Per utilizzare il dialetto, aggiungere dialect = SequencePerTableOracleDialect al file DataSource.groovy nella chiusura della definizione 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; 
     } 
    } 
} 

Questo collegamento ha una certa storia su questa questione, con un link al codice originale di Burt, e una risposta per PostgreSQL: Hibernate & postgreSQL with Grails

+0

Grazie per quello! Stai dicendo che non c'è modo di specificare NOCACHE o allocationSize in params per una sequenza? – dbrin

+0

Non per quanto ne so, dato che sarebbe SQL specifico per database, che legherebbe la tua applicazione a una specifica marca di database. – schmolly159

+0

Scusa schmolly. Questa non è la soluzione che stavo cercando. Lo contrassegnerò come forse utile per gli altri. Vedi la mia soluzione per una soluzione alternativa facile. ...almeno penso di sì :) – dbrin

Problemi correlati