7

Il mio progetto utilizza la modalità di sospensione con il gestore delle transazioni primavera e il mio database è postgres (potrebbe non essere rilevante).vincolo database di gestione ibernazione

Sto cercando di leggere grandi file xml e di costruire oggetti da questi (gli oggetti non sono grandi ma la quantità è) e li inseriamo nel database.

Se per caso uno dei miei oggetti viola il vincolo del database, l'intero processo si interrompe. Come posso saltare quelli che violano il vincolo del database? in alternativa registra il loro id o qualunque cosa su un file di log?

aggiornamento Domanda:

Sto passando in rassegna attraverso SO e ha scoperto che per gli inserti in batch è meglio consiglia di utilizzare sessione Stateless, ma ho ancora lo stesso problema e inserto ferma:

May 26, 2012 4:45:47 PM org.hibernate.util.JDBCExceptionReporter logExceptions 
SEVERE: ERROR: duplicate key value violates unique constraint "UN_FK" 
    Detail: Key (fid)=(H1) already exists. 

Ecco le parti rilevanti del mio codice per l'analisi di xml e l'inserimento in db, per semplicità assumiamo che sto inserendo filmati:

//class field 
@Autowired 
private SessionFactory sessionFactory; 

@Override 
public void startDocument() throws SAXException { 
    session = sessionFactory.getCurrentSession(); 
} 

@Override 
public void endElement(String uri, String localName, String qName) throws SAXException { 
if (qName.equalsIgnoreCase("FILM")) { 
     movie.setCategory(category); 
     movie.setAdded(new Date()); 
     session.insert(movie); 
    } 
} 

I e ho impostato questa proprietà nell'app-ctx hibernate.jdbc.batch_size su 100. È davvero necessario selezionare prima di inserire per evitare questo?

Aggiornamento 2:

Se uso StatelessSession invece della sessione, ottengo arround 20 inserti e che le fermate di trasformazione a tempo indeterminato senza alcuna eccezione o nulla.

Suppongo che il numero 20 sia perché sto collegando le connessioni con tomcat e ho maxActive="20".

Bounty Aggiornamento:

Mi piacerebbe davvero vedere qualcuno soluzione offerta (senza selezionare difensiva se possibile). Utilizzo di statelessSession o solo sessione.

risposta

4

La maggior parte dei tipi di vincoli, ad esempio se una colonna è annullabile o ha una larghezza massima, è possibile verificare utilizzando Hibernate Validator. Basta eseguire manualmente la validazione sull'oggetto prima di tentare di mantenerlo.

Per alcune cose, in particolare per i vincoli univoci, è necessario eseguire una selezione "difensiva" per verificare se esiste una collisione o mantenere un set di valori di memoria già inserito.

2

Per inserire una grande quantità di oggetti da un file xml, è necessario considerare l'utilizzo del batch di primavera. Il parametro skip-limit consente di indicare quante linee errate è possibile avere nel proprio xml prima di interrompere il processo batch. Controlla anche skip-policy ed eccezione skippable; vedi Configuring a Step nella documentazione di primavera.

Se non si desidera utilizzare il batch di primavera, utilizzare solo un semplice try catch che consentirà al processo di continuare fino alla fine.

+0

Una semplice istruzione try cattura non lo farà. Una volta che un'eccezione viene generata da Hibernate, lo stato della sessione è incoerente, la transazione deve essere ripristinata e la sessione chiusa. Inoltre, l'eccezione verrà lanciata solo al momento del flush, molto tempo dopo che il record difettoso è stato mantenuto. –

+0

Ecco perché è consigliabile utilizzare una sessione di sospensione stateless per tali casi. Eviterà lo stato incoerente e ridurrà anche il consumo di memoria (o non dovrai rimuovere le entità già trattate) –

1

Penso che Affe abbia l'approccio giusto con una selezione difensiva prima di inserire.

Si potrebbe considerare l'utilizzo di savepoint prima di ogni inserimento e il rollback sul punto di salvataggio se viene generata un'eccezione. Ci sarà un sovraccarico di prestazioni nella creazione di un punto di salvataggio. Il punto di salvataggio dovrà essere rilasciato quando hai finito.

Vedi AbstractTransactionStatus.setSavepoint() o se si ha accesso al pool di connessioni JDBC setSavepointrollback(Savepoint) e releaseSavepoint(Savepoint)

2

perché pensi che tutto questo deve essere uno grande transazione? Tutto ciò che descrivi in ​​realtà implica per me che in effetti hai molte transazioni qui. Per gli oggetti che "eliminano", rimuovi semplicemente quell'entità e ripristina la transazione. Diventa un po 'più complesso se l'elemento "FILM" definisce un grafico di oggetti, ma l'idea è la stessa.

4

Penso che sia impossibile convalidare qualcosa in modo così completo da poter garantire un inserimento di successo. In alcuni casi, qualunque cosa tu faccia, qualcun altro può inserire qualcosa nel DB tra la validazione e l'inserimento causando una violazione del vincolo.

Nella maggior parte dei casi, consiglierei di gestire l'eccezione come qualsiasi altra.

0

Anche se è una vecchia domanda, ho affrontato una situazione simile di recente ed ecco cosa ho fatto.

Ho usato anche sessioni senza stato ma ecco cosa ho fatto in modo diverso. Ho emesso un session.insert che ha esito positivo o negativo a causa della violazione di Constraint, fino alla violazione di Constraint che viene lanciata una ConstraintViolationException, la cattura che ti darebbe l'oggetto che ha fallito l'inserimento, fare qualsiasi cosa tu volessi fare con l'oggetto fallito. Insieme alle transazioni vengono utilizzati anche i punti di salvataggio per ogni inserto (i punti di salvataggio sono più economici e non causano un impatto notevole sulle prestazioni), quindi quando una transazione fallisce a causa di qualche problema il commit viene eseguito fino all'ultimo punto di salvataggio (nella clausola catch).

mio codice sembrava qualcosa di simile:

try { 
    session.connection().setSavePoint("lastSaved"); 
    session.insert(obj); 
} 
catch(ConstraintViolationException) { 
    log.error(obj.getUserId()); 
    .... 
} 
tx.commit(); 
.... 
catch(TransactionException e) { 
    tx.rollback(); 
}