我有一个由 Hibernate 备份的简单存储库
@Repository
@Transactional(rollbackFor = Exception.class)
public class WidgetDAOImpl implements WidgetDAO {
@Autowired
private SessionFactory sf;
@Override
public Widget getWidget(long id) {
return (Widget) sf.getCurrentSession().load(Widget.class, id);
}
@Override
public void saveWidget(Widget w){
sf.getCurrentSession().saveOrUpdate(w);
}
}
这真的很简单,而且还可以。不幸的是,需求发生了变化,现在我需要在一次业务通话期间阅读大量内容并保存大量小部件。此外,业务电话的数量也会增加。
例如,这可能是单独类中的业务逻辑:
@Scheduled(fixedDelay = 100L) // lets say this is "often"
public void updateWidgets(List<Long> ids){ // lets say the list is ~10 ids
for(long id: ids){
Widget w = widgetDAO.getWidget(id);
doSomeStuff(w);
widgetDAO.saveWidget(w);
}
}
我担心这会影响性能。我该怎么做才能节省性能?
- 我应该批量加载/保存,即一次性加载/保存吗?我有 ID 列表,所以也许有可能?
- 我是否应该以 session 以外的方式缓存实例(如您所见, session 很短)?
一些想法/片段非常受欢迎,因为我是一个 Hibernate 新手。
最佳答案
您当前的解决方案正在创建大量事务。每个项目 2 个事务,1 个用于读取,1 个用于写入。
创建事务(通常)是一项繁重且耗时的操作,因为它将打开与数据库的连接,启动 hibernate session 并进行一些同步。
最好您的整个方法是一个事务,这可以通过使用 @Transactional
注释您的方法来完成。
@Scheduled(fixedDelay = 100L)
@Transactional
public void updateWidgets(List<Long> ids){ ... }
执行此操作时,如果您有大量数据,您可能需要偶尔刷新
并清除
当前 session 。
如果您不希望这样,您至少应该在单个事务中进行读取和写入。为此,您可以使用 TransactionTemplate
并在 for 循环中使用它。
@Autowired
private TransactionTemplate transactionTemplate;
@Scheduled(fixedDelay = 100L)
public void updateWidgets(List<Long> ids) {
for(long id: ids){
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
Widget w = widgetDAO.getWidget(id);
doSomeStuff(w);
widgetDAO.saveWidget(w);
}
}
}
或者将 for 循环中的代码移至用 @Transactional
注释的服务方法。
关于java - 优化一些后续 Hibernate 加载/保存调用的想法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24817643/