0

我有一个带有延迟加载集合的 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每当收到请求时都会从 a 调用。

但是,我不想processAllFoos被包装在单个数据库事务中,因为这会锁定整个 Foo 表,直到为所有 Foo 执行业务逻辑。

如果我使用该processFoo方法@Transactional,我会得到LazyInitializationException抱怨 Hibernate 会话不存在的方法。为了完成这项工作,我需要在调用堆栈中创建所有方法,@Transactional以便嵌套方法可以加入调用方法的事务。但这会锁定整个 Foo 表,如上所述。

添加一个OpenSessionInViewFilterfor thedispatcher 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);
    }
}
4

1 回答 1

2

OpenSessionInViewFilter通常用于解决 View 层(UI 组件或页面模板)中的 LazyInitialization 问题,因为 View 层不能也不能直接管理事务。Bar在您的情况下,可以应用另一种获取所有对象的方法。

首先,您获取所有Foo对象 ID,而不是获取完整对象。

其次使用Fooids 集合遍历相关Bar对象。

第三,如果您不想要一个 BIG 事务,那么您可以使用 Spring Transaction 模板来显式地管理事务。

您的代码示例可能如下所示:

@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相关记录Foo可能会被阻塞。RDBMSBar

于 2016-09-27T03:32:10.370 回答