Как synchronized
работает ключевое слово Java
Когда вы добавляете synchronized
ключевое слово в статический метод, метод может вызываться только одним потоком за раз.
В вашем случае каждый вызов метода будет:
- создать новый
SessionFactory
- создать новый
Session
- получить объект
- вернуть объект обратно вызывающей стороне
Тем не менее, это были ваши требования:
- Я хочу, чтобы это предотвратить доступ к информации для того же экземпляра БД.
- предотвращение
getObjectById
вызова для всех классов, когда он вызывается определенным классом
Так что, даже если getObjectById
метод является поточно-ориентированным, реализация ошибочна.
SessionFactory
лучшие практики
SessionFactory
Потокобезопасно, и это очень дорогой объект для создания , как это необходимо для анализа классов сущностей и построить внутреннее представление сущности метамодели.
Таким образом, вы не должны создавать SessionFactory
на каждомgetObjectById
вызов метода.
Вместо этого вы должны создать для него одноэлементный экземпляр.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Session
Всегда должен быть закрыт
Вы не закрыли Session
вfinally
блоке, и это может привести к утечке ресурсов базы данных , если исключение при загрузке сущности.
Согласно Session.load
методу JavaDoc может выдать , HibernateException
если объект не может быть найден в базе данных.
Вы не должны использовать этот метод, чтобы определить, существует ли экземпляр (используйте get()
вместо этого). Используйте это только для получения экземпляра, который, как вы полагаете, существует, где несуществование будет реальной ошибкой.
Вот почему вам нужно использовать finally
блок, чтобы закрыть 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();
}
}
}
Предотвращение многопоточного доступа
В вашем случае вы хотели убедиться, что только один поток получает доступ к этой конкретной сущности.
Но synchronized
ключевое слово не позволяет двум потокам вызыватьgetObjectById
одновременный . Если два потока вызывают этот метод один за другим, у вас все равно будет два потока, использующих этот объект.
Итак, если вы хотите заблокировать данный объект базы данных, чтобы никакой другой поток не мог изменить его, вам нужно использовать блокировки базы данных.
synchronized
Ключевое слово работает только в одной виртуальной машине Java. Если у вас есть несколько веб-узлов, это не помешает многопоточному доступу через несколько JVM.
Что вам нужно сделать, это использовать LockModeType.PESSIMISTIC_READ
илиLockModeType.PESSIMISTIC_WRITE
при внесении изменений в БД, например так:
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();
}
}
Итак, вот что я сделал:
- Я создал новую
EntityTransaction
и начал новую транзакцию базы данных
- Я загрузил
Post
объект, удерживая блокировку связанной записи базы данных
- Я изменил
Post
сущность и совершил транзакцию
- В случае
Exception
выбрасывания я откатил транзакцию
Для получения более подробной информации о ACID и транзакциях базы данных, проверить эту статью , а также.