Como a synchronizedpalavra-chave Java funciona
Quando você adiciona a synchronizedpalavra-chave a um método estático, o método pode ser chamado apenas por um único thread por vez.
No seu caso, toda chamada de método irá:
- crie um novo
SessionFactory
- crie um novo
Session
- buscar a entidade
- retornar a entidade de volta para o chamador
No entanto, esses eram seus requisitos:
- Quero que isso impeça o acesso a informações na mesma instância do banco de dados.
- impedindo de
getObjectByIdser chamado para todas as classes quando é chamado por uma classe específica
Portanto, mesmo que o getObjectByIdmétodo seja seguro para threads, a implementação está errada.
SessionFactory Melhores Práticas
O SessionFactorythread é seguro e é um objeto muito caro para criar, pois precisa analisar as classes de entidade e criar a representação interna do metamodelo da entidade.
Portanto, você não deve criar a chamada SessionFactorya cada getObjectByIdmétodo.
Em vez disso, você deve criar uma instância singleton para ela.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
O Sessiondeve sempre estar fechado
Você não fechar o Sessionem umfinally bloco, e isso pode vazar recursos de banco de dados se uma exceção é lançada quando o carregamento da entidade.
De acordo com o Session.loadmétodo, o JavaDoc pode lançar a HibernateExceptionse a entidade não puder ser encontrada no banco de dados.
Você não deve usar esse método para determinar se existe uma instância (use em get()vez disso). Use isso apenas para recuperar uma instância que você supõe existir, onde a inexistência seria um erro real.
É por isso que você precisa usar um finallybloco para fechar o Sessionseguinte:
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();
}
}
}
Impedindo o acesso multithread
No seu caso, você queria garantir que apenas um encadeamento tenha acesso a essa entidade específica.
Mas a synchronizedpalavra - chave impede apenas que dois threads chamem ogetObjectById concorrente. Se os dois threads chamarem esse método um após o outro, você ainda terá dois threads usando essa entidade.
Portanto, se você deseja bloquear um determinado objeto de banco de dados para que nenhum outro encadeamento possa modificá-lo, será necessário usar bloqueios de banco de dados.
A synchronizedpalavra-chave funciona apenas em uma única JVM. Se você tiver vários nós da web, isso não impedirá o acesso multiencadeado em várias JVMs.
O que você precisa fazer é usar LockModeType.PESSIMISTIC_READouLockModeType.PESSIMISTIC_WRITE ao aplicar as alterações no banco de dados, assim:
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();
}
}
Então, foi o que eu fiz:
- Criei um novo
EntityTransactione iniciei uma nova transação de banco de dados
- Carreguei a
Postentidade enquanto mantinha um bloqueio no registro do banco de dados associado
- Mudei a
Postentidade e comprometi a transação
- No caso de um
Exceptionser jogado, reverti a transação
Para obter mais detalhes sobre transações ACID e banco de dados, consulte este artigo também.