Wie das synchronized
Java-Schlüsselwort funktioniert
Wenn Sie das synchronized
Schlüsselwort zu einer statischen Methode hinzufügen , kann die Methode jeweils nur von einem einzelnen Thread aufgerufen werden.
In Ihrem Fall wird jeder Methodenaufruf:
- erstelle eine neue
SessionFactory
- erstelle eine neue
Session
- Holen Sie sich die Entität
- Geben Sie die Entität an den Anrufer zurück
Dies waren jedoch Ihre Anforderungen:
- Ich möchte, dass dies den Zugriff auf Informationen zu derselben DB-Instanz verhindert.
- Verhindern,
getObjectById
dass für alle Klassen aufgerufen wird, wenn es von einer bestimmten Klasse aufgerufen wird
Selbst wenn die getObjectById
Methode threadsicher ist, ist die Implementierung falsch.
SessionFactory
empfohlene Vorgehensweise
Das SessionFactory
ist threadsicher und es ist ein sehr teures Objekt, das erstellt werden muss, da die Entitätsklassen analysiert und die interne Entitätsmetamodelldarstellung erstellt werden müssen.
Sie sollten also nicht SessionFactory
bei jedem getObjectById
Methodenaufruf das erstellen .
Stattdessen sollten Sie eine Singleton-Instanz dafür erstellen.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Das Session
sollte immer geschlossen sein
Sie haben das Session
in einem finally
Block nicht geschlossen, und dies kann zu Datenbankressourcen führen, wenn beim Laden der Entität eine Ausnahme ausgelöst wird.
Gemäß der Session.load
Methode kann JavaDoc ein auslösen , HibernateException
wenn die Entität nicht in der Datenbank gefunden werden kann.
Sie sollten diese Methode nicht verwenden, um festzustellen, ob eine Instanz vorhanden ist ( get()
stattdessen verwenden). Verwenden Sie diese Option nur, um eine Instanz abzurufen, von der Sie annehmen, dass sie existiert, wobei Nichtexistenz ein tatsächlicher Fehler wäre.
Aus diesem Grund müssen Sie einen finally
Block verwenden, um das zu schließen Session
:
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();
}
}
}
Multithread-Zugriff verhindern
In Ihrem Fall wollten Sie sicherstellen, dass nur ein Thread Zugriff auf diese bestimmte Entität erhält.
Das synchronized
Schlüsselwort verhindert jedoch nur, dass zwei Threads getObjectById
gleichzeitig aufrufen . Wenn die beiden Threads diese Methode nacheinander aufrufen, haben Sie immer noch zwei Threads, die diese Entität verwenden.
Wenn Sie also ein bestimmtes Datenbankobjekt sperren möchten, damit kein anderer Thread es ändern kann, müssen Sie Datenbanksperren verwenden.
Das synchronized
Schlüsselwort funktioniert nur in einer einzelnen JVM. Wenn Sie mehrere Webknoten haben, wird dadurch der Multithread-Zugriff über mehrere JVMs nicht verhindert.
Sie müssen die Änderungen wie folgt verwenden LockModeType.PESSIMISTIC_READ
oderLockModeType.PESSIMISTIC_WRITE
anwenden:
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();
}
}
Also, das habe ich getan:
- Ich habe eine neue erstellt
EntityTransaction
und eine neue Datenbanktransaktion gestartet
- Ich habe die
Post
Entität geladen , während ich den zugehörigen Datenbankeintrag gesperrt habe
- Ich habe die
Post
Entität geändert und die Transaktion festgeschrieben
- Im Falle eines
Exception
Werfens habe ich die Transaktion zurückgesetzt
Weitere Informationen zu ACID- und Datenbanktransaktionen finden Sie auch in diesem Artikel .