Come funziona la synchronizedparola chiave Java
Quando si aggiunge la synchronizedparola chiave a un metodo statico, il metodo può essere chiamato solo da un singolo thread alla volta.
Nel tuo caso, ogni chiamata di metodo sarà:
- creane uno nuovo
SessionFactory
- creane uno nuovo
Session
- recuperare l'entità
- restituisce l'entità al chiamante
Tuttavia, questi erano i tuoi requisiti:
- Voglio che ciò impedisca l'accesso alle informazioni alla stessa istanza DB.
- impedendo di
getObjectByIdessere chiamato per tutte le classi quando viene chiamato da una determinata classe
Pertanto, anche se il getObjectByIdmetodo è thread-safe, l'implementazione è errata.
SessionFactory migliori pratiche
Il SessionFactory è thread-safe, ed è un oggetto molto costoso creare poiché è necessario analizzare le classi di entità e costruire la rappresentazione interna entità metamodel.
Quindi, non dovresti creare il SessionFactoryon ognigetObjectById chiamata metodo.
Invece, è necessario creare un'istanza singleton per esso.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Il Session devono essere chiusi
Non hai chiuso Sessionin afinally blocco e questo può perdere le risorse del database se viene generata un'eccezione durante il caricamento dell'entità.
Secondo il Session.loadmetodo JavaDoc potrebbe generare un erroreHibernateException se l'entità non è stata trovata nel database.
Non utilizzare questo metodo per determinare se esiste un'istanza (utilizzare get()invece). Utilizzalo solo per recuperare un'istanza che ritieni esista, in cui la non esistenza sarebbe un errore reale.
Ecco perché è necessario utilizzare un finallyblocco per chiudere Session, in questo modo:
public static synchronized Object getObjectById (Class objclass, Long id) {
Session session = null;
try {
session = sessionFactory.openSession();
return session.load(objclass, id);
} finally {
if(session != null) {
session.close();
}
}
}
Prevenire l'accesso multi-thread
Nel tuo caso, volevi assicurarti che solo un thread potesse accedere a quella particolare entità.
Ma la synchronizedparola chiave impedisce solo a due thread di chiamare getObjectByIdcontemporaneamente. Se i due thread chiamano questo metodo uno dopo l'altro, avrai comunque due thread che utilizzano questa entità.
Pertanto, se si desidera bloccare un determinato oggetto di database in modo che nessun altro thread possa modificarlo, è necessario utilizzare i blocchi di database.
La synchronizedparola chiave funziona solo in una singola JVM. Se si dispone di più nodi Web, ciò non impedirà l'accesso multi-thread su più JVM.
Quello che devi fare è usare LockModeType.PESSIMISTIC_READoLockModeType.PESSIMISTIC_WRITE mentre applichi le modifiche al DB, in questo modo:
Session session = null;
EntityTransaction tx = null;
try {
session = sessionFactory.openSession();
tx = session.getTransaction();
tx.begin();
Post post = session.find(
Post.class,
id,
LockModeType.LockModeType.PESSIMISTIC_READ
);
post.setTitle("High-Performance Java Perisstence");
tx.commit();
} catch(Exception e) {
LOGGER.error("Post entity could not be changed", e);
if(tx != null) {
tx.rollback();
}
} finally {
if(session != null) {
session.close();
}
}
Quindi, questo è quello che ho fatto:
- Ho creato un nuovo
EntityTransactione avviato una nuova transazione di database
- Ho caricato l'
Postentità tenendo un blocco sul record del database associato
- Ho cambiato l'
Postentità e ho eseguito la transazione
- Nel caso di un
Exceptionlancio, ho eseguito il rollback della transazione
Per maggiori dettagli sulle transazioni ACID e database, consulta anche questo articolo .