【发布时间】:2016-08-05 14:49:18
【问题描述】:
我想问一下这种行为的原因,因为当遇到 Spring @Transactional 方法/类时,我似乎不完全理解 Hibernate 中 persist() 和 merge() 之间的区别。
我有以下代码应该回滚数据库操作但它没有(整个类被注释为@Transactional):
@Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
bean = myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
以下代码在抛出异常时按预期回滚:
@Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
save()方法来自org.springframework.data.jpa.repository.support.SimpleJpaRepository类,所以它的代码是:
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
该实体是现有的,所以我知道它正在执行merge()。根据JPA specification:
find 方法(前提是它在没有锁的情况下调用或使用 LockModeType.NONE) 和 getReference 方法不需要 在事务上下文中调用。 如果实体经理有 事务范围的持久性上下文正在使用中,结果 实体将被分离;如果实体管理器具有扩展 使用持久化上下文,它们将被管理。
合并操作允许从分离的状态传播 实体到由实体管理器管理的持久实体上。这 应用于实体 X 的合并操作的语义为 如下:
- 如果 X 是分离实体,则 X 的状态将复制到具有相同身份的预先存在的托管实体实例 X' 或新的 已创建 X 的托管副本 X'。
- 如果 X 是一个新的实体实例,则创建一个新的托管实体实例 X',并将 X 的状态复制到新的托管实体中 实例 X'。
- 如果 X 是已移除的实体实例,则合并操作将抛出 IllegalArgumentException(或事务提交将 失败)。
- 如果 X 是一个托管实体,它会被合并操作忽略,但是,合并操作会级联到由 来自 X 的关系,如果这些关系已经用 级联元素值 cascade=MERGE 或 cascade=ALL 注释。
- 对于由来自 X 的具有级联元素值 cascade=MERGE 或 cascade=ALL 的关系引用的所有实体 Y,Y 被合并 递归为 Y'。对于 X 引用的所有此类 Y,X' 设置为 参考 Y'。 (请注意,如果 X 是托管的,则 X 与 X'。)
- 如果 X 是合并到 X' 的实体,并引用另一个实体 Y,其中未指定 cascade=MERGE 或 cascade=ALL,则 从 X' 导航相同的关联会产生对 a 的引用 具有与 Y 相同的持久标识的托管对象 Y'。
如果
merge() 返回的副本应该是托管实体,为什么当我使用分离的实体时更改存储在数据库中? (除非有例外。这是我想要的行为)如果我修改了新的托管实体但抛出异常,为什么仍然提交更改?
编辑应@alan-hay的要求:
package org.customer.somefoos.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.customer.somefoos.entity.MyBean;
import org.customer.somefoos.repository.MyBeanRepository;
import org.customer.somefoos.service.MyBeanManagement;
import org.customer.somefoos.service.FooManagement;
@Service
@Transactional
public class MyBeanManagementImpl implements MyBeanManagement {
@Resource
private MyBeanRepository myBeanRepository;
@Resource
private FooManagement fooManagement;
@Override
public List<MyBean> findAll() {
return myBeanRepository.findAll();
}
@Override
public MyBean findById(Integer id) {
return myBeanRepository.findOne(id);
}
@Override
public void delete(Integer id) {
myBeanRepository.delete(id);
}
@Override
public MyBean save(MyBean myBean) {
return myBeanRepository.save(myBean);
}
@Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
}
【问题讨论】:
-
能把全班的代码贴出来吗?
-
完成。 @AlanHay,您认为其他地方有什么问题吗?
标签: spring hibernate jpa transactional