Fonctionnement du synchronizedmot-clé Java
Lorsque vous ajoutez le synchronizedmot - clé à une méthode statique, la méthode ne peut être appelée que par un seul thread à la fois.
Dans votre cas, chaque appel de méthode:
- créer un nouveau
SessionFactory
- créer un nouveau
Session
- chercher l'entité
- renvoyer l'entité à l'appelant
Cependant, voici vos exigences:
- Je veux que cela empêche l'accès aux informations de la même instance de base de données.
- éviter d'
getObjectByIdêtre appelé pour toutes les classes lorsqu'il est appelé par une classe particulière
Ainsi, même si la getObjectByIdméthode est thread-safe, l'implémentation est incorrecte.
SessionFactory les meilleures pratiques
Le SessionFactoryest thread-safe, et c'est un objet très coûteux à créer car il doit analyser les classes d'entité et construire la représentation du métamodèle d'entité interne.
Donc, vous ne devriez pas créer le SessionFactoryà chaque getObjectByIdappel de méthode.
Au lieu de cela, vous devez créer une instance singleton pour elle.
private static final SessionFactory sessionFactory = new Configuration()
.configure()
.buildSessionFactory();
Le Sessiondevrait toujours être fermé
Vous n'avez pas fermé le Sessiondans un finallybloc, ce qui peut entraîner une fuite des ressources de la base de données si une exception est levée lors du chargement de l'entité.
Selon la Session.loadméthode, JavaDoc peut lancer un HibernateExceptionsi l'entité est introuvable dans la base de données.
Vous ne devez pas utiliser cette méthode pour déterminer si une instance existe (utilisez à la get()place). Utilisez ceci uniquement pour récupérer une instance dont vous supposez qu'elle existe, où la non-existence serait une erreur réelle.
C'est pourquoi vous devez utiliser un finallybloc pour fermer le Session, comme ceci:
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();
}
}
}
Empêcher l'accès multi-thread
Dans votre cas, vous vouliez vous assurer qu'un seul thread ait accès à cette entité particulière.
Mais le synchronizedmot - clé empêche seulement deux threads d'appeler getObjectByIdsimultanément. Si les deux threads appellent cette méthode l'un après l'autre, vous aurez toujours deux threads utilisant cette entité.
Ainsi, si vous souhaitez verrouiller un objet de base de données donné afin qu'aucun autre thread ne puisse le modifier, vous devez utiliser des verrous de base de données.
Le synchronizedmot-clé ne fonctionne que dans une seule JVM. Si vous disposez de plusieurs nœuds Web, cela n'empêchera pas l'accès multi-thread sur plusieurs JVM.
Ce que vous devez faire est d'utiliser LockModeType.PESSIMISTIC_READouLockModeType.PESSIMISTIC_WRITE lors de l'application des modifications à la base de données, comme ceci:
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();
}
}
Alors, voici ce que j'ai fait:
- J'ai créé une nouvelle
EntityTransactionet commencé une nouvelle transaction de base de données
- J'ai chargé l'
Postentité tout en maintenant un verrou sur l'enregistrement de base de données associé
- J'ai changé d'
Postentité et commis la transaction
- Dans le cas d'un
Exceptionrejet, j'ai annulé la transaction
Pour plus de détails sur les transactions ACID et de base de données, consultez également cet article .