У меня есть объект JPA с Lazy загруженной коллекцией на нем. Мне не нужна коллекция каждый раз.OpenSessionInView против транзакции? (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;
}
У меня есть несколько методов в классе обслуживания, которые выполняют много взаимодействия баз данных и в конце сохранить объект Foo в базу данных. Мне нужно, чтобы это произошло примерно за 100 предметов в коллекции.
@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
вызывается из @RESTController
всякий раз, когда он получает запрос.
Однако я не хочу, чтобы processAllFoos
был обернут в транзакцию одной базы данных, поскольку это блокирует всю таблицу Foo до тех пор, пока бизнес-логика не будет выполнена для всех Foos.
Если у меня есть метод processFoo
@Transactional
Я получаю LazyInitializationException
, который жалуется, что сеанс Hibernate не существует. Для выполнения этой работы мне необходимо сделать все методы в стеке вызовов @Transactional
, чтобы вложенные методы могли присоединиться к транзакции вызывающего метода. Но это блокирует всю таблицу Foo, как указано выше.
Добавление OpenSessionInViewFilter
для dispatcher servlet
решает мою проблему, но я прочитал, что с этим подходом возникают проблемы с производительностью и отсоединением/повторной привязкой объектов (что я делаю в других частях приложения).
Есть ли способ, которым я могу сделать то, что хочу, не используя подход OpenSessionInView
? Какие еще уязвимости я могу добавить с помощью этого подхода?
Spring/Hibernate 4.x
На основе ниже ответа, я был в состоянии сделать следующее:
@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);
}
}
Спасибо @SergeyBespalov Это сделало весь процесс невероятно быстрым. Есть ли какие-либо оговорки к этому подходу? Вещи, которые вы не должны делать при явном управлении транзакциями? – battle2048
Я могу предложить вам два простых правила, основанные на этом примере: 1. Не используйте декларативное управление транзакциями, если оно вам не подходит; 2. Не используйте полностью объекты, если вам нужно только одно свойство; –