J'ai une entité JPA avec Lazy loaded collection sur elle. Je n'ai pas besoin de la collection à chaque fois.OpenSessionInView vs. Transactional? (Spring/Hibernate/JPA)
@Entity(name = "Foo")
@Access(AccessType.FIELD)
@Table(name = "TEST", schema = "TEST")
public class Foo implements Serializable {
private static final long serialVersionUID = 1L;
@OneToMany(mappedBy="foo", targetEntity=Bar.class, fetch=FetchType.LAZY, cascade=CascadeType.ALL)
private List<Bar> bars;
}
@Entity(name = "Bar")
@Access(AccessType.FIELD)
@Table(name = "TEST", schema = "TEST")
public class Bar implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne(targetEntity = Foo.class)
@JoinColumn(name = "FOO_ID", referencedColumnName = "ID")
private Foo foo;
}
J'ai quelques méthodes sur une classe de service qui effectuent beaucoup d'interactions de base de données et à la fin sauver une entité Foo à la base de données. J'ai besoin que cela arrive pour environ 100 articles dans une collection.
@Service
public class FooService {
@Autowired
private FooRepository fooRepository;
public void processAllFoos() {
fooRepository.findAll().forEach(foo -> {
processFoo(foo);
});
}
private void processFoo(Foo foo) {
foo.getBars().forEach(bar -> {
// Do a lot of time consuming stuff here that involves
// entities of other types and modify each bar object
});
fooRepository.save(foo);
}
}
processAllFoos
est appelé à partir d'un @RESTController
chaque fois qu'il reçoit une demande.
Cependant, je ne veux pas processAllFoos
pour être enveloppé dans une seule transaction de base de données, car cela verrouille toute la table Foo jusqu'à ce que la logique métier soit exécutée pour tous les Foos.
Si je fais la méthode processFoo
@Transactional
je reçois le LazyInitializationException
qui se plaint que la session Hibernate est inexistante. Pour que cela fonctionne, j'ai besoin de faire toutes les méthodes dans la pile d'appel @Transactional
afin que les méthodes imbriquées puissent se joindre à la transaction de la méthode appelante. Mais cela verrouille toute la table Foo comme mentionné ci-dessus.
Ajout d'un OpenSessionInViewFilter
pour le dispatcher servlet
résout mon problème mais j'ai lu qu'il ya des problèmes avec la performance et l'entité détachant/refixer (ce que je fais dans d'autres parties de l'application) avec cette approche.
Y at-il un moyen de faire ce que je veux sans utiliser l'approche OpenSessionInView
? Quelles sont les autres vulnérabilités que j'ajoute en utilisant cette approche?
Spring/Hibernate 4.x
Sur la base de la réponse ci-dessous, j'ai pu faire ce qui suit:
@Service
public class FooService {
@Autowired
private FooRepository fooRepository;
@Autowired
private TransactionTemplate transactionTemplate;
public void processAllFoos() {
fooRepository.findAll().forEach(foo -> {
transactionTemplate.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) {
try {
processFoo(foo);
status.flush();
} catch(Exception e) {
status.setRollbackOnly();
}
return null;
}
});
});
}
private void processBar(Foo foo) {
foo.getBars().foreEach(bar -> {
// Do a lot of time consuming stuff here that involves
// entities of other types and modify each bar object
});
fooRepository.save(foo);
}
}
Merci @SergeyBespalov Cela a rendu l'ensemble du processus incroyablement rapide. Y a-t-il des réserves à cette approche? Choses que vous ne devriez pas faire lors de la gestion des transactions explicitement? – battle2048
Je peux vous suggérer deux règles simples basées sur cet exemple: 1. N'utilisez pas la gestion des transactions déclarative si cela ne vous convient pas; 2. N'utilisez pas complètement les objets si vous n'avez besoin que d'une seule propriété; –