Jak synchronized
działa słowo kluczowe Java
Po dodaniu synchronized
słowa kluczowego do metody statycznej metoda może być wywoływana jednocześnie tylko przez jeden wątek.
W twoim przypadku każde wywołanie metody:
- Stwórz nowy
SessionFactory
- Stwórz nowy
Session
- pobierz jednostkę
- zwraca jednostkę z powrotem do dzwoniącego
Jednak takie były Twoje wymagania:
- Chcę, aby to uniemożliwiło dostęp do informacji do tej samej instancji bazy danych.
- zapobieganie
getObjectById
wywoływaniu wszystkich klas, gdy jest wywoływane przez określoną klasę
Tak więc, nawet jeśli getObjectById
metoda jest bezpieczna dla wątków, implementacja jest nieprawidłowa.
SessionFactory
najlepsze praktyki
SessionFactory
Jest thread-safe, i jest to bardzo kosztowny obiekt do tworzenia, ile potrzebuje do analizowania klas encji i budować wewnętrzną reprezentację podmiotu metamodel.
Dlatego nie powinieneś tworzyć wywołania metody SessionFactory
przy każdym getObjectById
wywołaniu.
Zamiast tego należy utworzyć dla niego pojedynczą instancję.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Session
Powinny być zawsze zamknięte
Nie zamknąłeś bloku Session
w finally
bloku, co może spowodować wyciek zasobów bazy danych, jeśli podczas ładowania jednostki zostanie zgłoszony wyjątek.
Zgodnie z Session.load
metodą JavaDoc może HibernateException
zgłosić, jeśli jednostka nie może zostać znaleziona w bazie danych.
Nie należy używać tej metody do określania, czy instancja istnieje ( get()
zamiast tego użyj ). Użyj tego tylko do pobrania instancji, która, jak zakładasz, istnieje, a nieistnienie byłoby rzeczywistym błędem.
Dlatego musisz użyć finally
bloku, aby zamknąć Session
, w ten sposób:
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();
}
}
}
Zapobieganie dostępowi do wielu wątków
W twoim przypadku chciałeś mieć pewność, że tylko jeden wątek uzyska dostęp do tej konkretnej jednostki.
Ale synchronized
słowo kluczowe zapobiega tylko getObjectById
równoczesnemu wywoływaniu dwóch wątków . Jeśli dwa wątki wywołują tę metodę jeden po drugim, nadal będziesz mieć dwa wątki korzystające z tej jednostki.
Tak więc, jeśli chcesz zablokować dany obiekt bazy danych, aby żaden inny wątek nie mógł go zmodyfikować, musisz użyć blokad bazy danych.
Słowo synchronized
kluczowe działa tylko w pojedynczej maszynie JVM. Jeśli masz wiele węzłów WWW, nie zapobiegnie to wielowątkowemu dostępowi do wielu maszyn JVM.
To, co musisz zrobić, to użyć LockModeType.PESSIMISTIC_READ
lubLockModeType.PESSIMISTIC_WRITE
podczas stosowania zmian w bazie danych, na przykład:
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();
}
}
Tak więc zrobiłem:
- Utworzyłem nową
EntityTransaction
i rozpocząłem nową transakcję w bazie danych
- Załadowałem
Post
jednostkę, trzymając blokadę skojarzonego rekordu bazy danych
- Zmieniłem
Post
podmiot i dokonałem transakcji
- W przypadku
Exception
wyrzucenia wycofałem transakcję
Więcej informacji na temat ACID i transakcji bazodanowych można znaleźć w tym artykule .