OpenSessionInView 与交易? (Spring/Hibernate/JPA)
OpenSessionInView vs. Transactional? (Spring/Hibernate/JPA)
我有一个 JPA 实体,上面有延迟加载 collection。我不需要每次 collection。
@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;
}
我在服务 class 上有几个方法执行大量数据库交互,最后将 Foo 实体保存到数据库中。我需要在 collection.
中对大约 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 table,直到对所有 Foos 执行业务逻辑。
如果我使用 processFoo
方法 @Transactional
,我会得到 LazyInitializationException
,它抱怨 Hibernate session 是 non-existent。为了完成这项工作,我需要在调用堆栈中创建所有方法 @Transactional
,以便嵌套方法可以加入调用方法的事务。但这会锁定整个 Foo table 如上所述。
为 dispatcher servlet
添加 OpenSessionInViewFilter
解决了我的问题,但我读到性能和实体 detaching/reattaching 存在问题(我在应用程序的其他部分这样做) 用这种方法。
有没有一种方法可以在不使用 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);
}
}
OpenSessionInViewFilter
常用于解决View层(UI组件或页面模板)的LazyInitialization问题,因为View层不能也不能直接管理事务。
在您的情况下,可以应用另一种获取所有 Bar
对象的方法。
首先 您获取所有 Foo
对象 ID 而不是获取完整对象。
第二 使用 Foo
ids 集合迭代相关的 Bar
对象。
第三 如果您不想进行一笔大交易,那么您可以使用Spring 交易模板来显式管理交易。
您的代码示例可能如下所示:
@Service
public class FooService {
@Autowired
private FooRepository fooRepository;
@Autowired
private BarRepository barRepository;
@Autowired
private TransactionTemplate transactionTemplate;
public void processAllFoos() {
final List < Long > fooIdList = transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
return fooRepository.findIdList();
}
});
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
barRepository.findByFooIdList(fooIdList).forEach(bar - > {
processBar(bar);
});
return null;
}
});
}
private void processBar(Bar bar) {
// Do a lot of time consuming stuff here that involves
// entities of other types and modify each bar object
barRepository.save(bar);
}
}
下面的示例显示了如何在不增加性能开销的情况下解决您的任务。但是你应该明白,如果Foo
和Bar
table链接有外键约束,那么Foo
table中的相关记录可能会被[=19阻塞=] 每次更新 Bar
table.
中的行
我有一个 JPA 实体,上面有延迟加载 collection。我不需要每次 collection。
@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;
}
我在服务 class 上有几个方法执行大量数据库交互,最后将 Foo 实体保存到数据库中。我需要在 collection.
中对大约 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 table,直到对所有 Foos 执行业务逻辑。
如果我使用 processFoo
方法 @Transactional
,我会得到 LazyInitializationException
,它抱怨 Hibernate session 是 non-existent。为了完成这项工作,我需要在调用堆栈中创建所有方法 @Transactional
,以便嵌套方法可以加入调用方法的事务。但这会锁定整个 Foo table 如上所述。
为 dispatcher servlet
添加 OpenSessionInViewFilter
解决了我的问题,但我读到性能和实体 detaching/reattaching 存在问题(我在应用程序的其他部分这样做) 用这种方法。
有没有一种方法可以在不使用 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);
}
}
OpenSessionInViewFilter
常用于解决View层(UI组件或页面模板)的LazyInitialization问题,因为View层不能也不能直接管理事务。
在您的情况下,可以应用另一种获取所有 Bar
对象的方法。
首先 您获取所有 Foo
对象 ID 而不是获取完整对象。
第二 使用 Foo
ids 集合迭代相关的 Bar
对象。
第三 如果您不想进行一笔大交易,那么您可以使用Spring 交易模板来显式管理交易。
您的代码示例可能如下所示:
@Service
public class FooService {
@Autowired
private FooRepository fooRepository;
@Autowired
private BarRepository barRepository;
@Autowired
private TransactionTemplate transactionTemplate;
public void processAllFoos() {
final List < Long > fooIdList = transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
return fooRepository.findIdList();
}
});
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
barRepository.findByFooIdList(fooIdList).forEach(bar - > {
processBar(bar);
});
return null;
}
});
}
private void processBar(Bar bar) {
// Do a lot of time consuming stuff here that involves
// entities of other types and modify each bar object
barRepository.save(bar);
}
}
下面的示例显示了如何在不增加性能开销的情况下解决您的任务。但是你应该明白,如果Foo
和Bar
table链接有外键约束,那么Foo
table中的相关记录可能会被[=19阻塞=] 每次更新 Bar
table.