Wie das synchronizedJava-Schlüsselwort funktioniert
Wenn Sie das synchronizedSchlü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,
getObjectByIddass für alle Klassen aufgerufen wird, wenn es von einer bestimmten Klasse aufgerufen wird
Selbst wenn die getObjectByIdMethode threadsicher ist, ist die Implementierung falsch.
SessionFactory empfohlene Vorgehensweise
Das SessionFactoryist 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 SessionFactorybei jedem getObjectByIdMethodenaufruf das erstellen .
Stattdessen sollten Sie eine Singleton-Instanz dafür erstellen.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Das Sessionsollte immer geschlossen sein
Sie haben das Sessionin einem finallyBlock nicht geschlossen, und dies kann zu Datenbankressourcen führen, wenn beim Laden der Entität eine Ausnahme ausgelöst wird.
Gemäß der Session.loadMethode kann JavaDoc ein auslösen , HibernateExceptionwenn 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 finallyBlock 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 synchronizedSchlüsselwort verhindert jedoch nur, dass zwei Threads getObjectByIdgleichzeitig 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 synchronizedSchlü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_READoderLockModeType.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
EntityTransactionund eine neue Datenbanktransaktion gestartet
- Ich habe die
PostEntität geladen , während ich den zugehörigen Datenbankeintrag gesperrt habe
- Ich habe die
PostEntität geändert und die Transaktion festgeschrieben
- Im Falle eines
ExceptionWerfens habe ich die Transaktion zurückgesetzt
Weitere Informationen zu ACID- und Datenbanktransaktionen finden Sie auch in diesem Artikel .