Como a synchronized
palavra-chave Java funciona
Quando você adiciona a synchronized
palavra-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
getObjectById
ser chamado para todas as classes quando é chamado por uma classe específica
Portanto, mesmo que o getObjectById
método seja seguro para threads, a implementação está errada.
SessionFactory
Melhores Práticas
O SessionFactory
thread é 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 SessionFactory
a cada getObjectById
método.
Em vez disso, você deve criar uma instância singleton para ela.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
O Session
deve sempre estar fechado
Você não fechar o Session
em 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.load
método, o JavaDoc pode lançar a HibernateException
se 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 finally
bloco para fechar o Session
seguinte:
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 synchronized
palavra - 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 synchronized
palavra-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_READ
ouLockModeType.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
EntityTransaction
e iniciei uma nova transação de banco de dados
- Carreguei a
Post
entidade enquanto mantinha um bloqueio no registro do banco de dados associado
- Mudei a
Post
entidade e comprometi a transação
- No caso de um
Exception
ser jogado, reverti a transação
Para obter mais detalhes sobre transações ACID e banco de dados, consulte este artigo também.