Как 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 и транзакциях базы данных, проверить эту статью , а также.