Come funziona la synchronized
parola chiave Java
Quando si aggiunge la synchronized
parola 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
getObjectById
essere chiamato per tutte le classi quando viene chiamato da una determinata classe
Pertanto, anche se il getObjectById
metodo è 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 SessionFactory
on 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 Session
in afinally
blocco e questo può perdere le risorse del database se viene generata un'eccezione durante il caricamento dell'entità.
Secondo il Session.load
metodo 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 finally
blocco 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 synchronized
parola chiave impedisce solo a due thread di chiamare getObjectById
contemporaneamente. 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 synchronized
parola 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_READ
oLockModeType.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
EntityTransaction
e avviato una nuova transazione di database
- Ho caricato l'
Post
entità tenendo un blocco sul record del database associato
- Ho cambiato l'
Post
entità e ho eseguito la transazione
- Nel caso di un
Exception
lancio, ho eseguito il rollback della transazione
Per maggiori dettagli sulle transazioni ACID e database, consulta anche questo articolo .