我在尝试使用Spring Data JPA和Hibernate作为JPA提供程序进行批量插入操作时遇到了问题。
我的服务中有以下方法。这是抛出异常的地方。
@Transactional
private void saveAppointments() {
// create StageFile reference object
StageFile file = new StageFile();
file.setFileName("3312_APPOINTMENT.API");
file.setDeleteFlag('N');
file.setInstitution(institution);
for (StageAppointment appointment : appointments) {
appointment.setStageFile(file);
stageAppointmentRepository.save(appointment);
}
}
@Transactional
private void saveDepartments() {
// create StageFile reference object
StageFile file = new StageFile();
file.setFileName("3312_DEPARTMENT.API");
file.setDeleteFlag('N');
file.setInstitution(institution);
for (StageDepartment department : departments) {
department.setStageFile(file);
stageDepartmentRepository.save(department);
}
}
该机构是一个实例变量,提前获取。
Institution institution = institutionRepository.findByActCode(3312);
我还将实体设置为级联PERSIST和MERGE。
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "stgAppointmentSeq")
@SequenceGenerator(name = "stgAppointmentSeq", sequenceName = "T_STG_APPOINTMENT_SEQ", allocationSize = 50)
@Column(name = "ID")
private Long id;
@ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "FILE_ID", referencedColumnName = "ID")
private StageFile stageFile;
@ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE}, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "STATUS_ID", referencedColumnName = "ID")
private StageStatus stageStatus;
我做错了什么?
此外,我确定这个问题的答案是肯定的,但是当我坚持一个具有所需外键引用的实体时,我是否必须保存完整的关联对象或仅保存ID?似乎与JPA的目的背道而驰。
更新
根据评论,我更新了代码以执行单个事务中的所有操作,但没有任何区别。
@Transactional
private void saveAppointments() {
Institution institution = institutionRepository.findByActCode(3312);
StageStatus stageStatus = stageStatusRepository.findOne(1L);
// create StageFile reference object
StageFile file = new StageFile();
file.setFileName("3312_APPOINTMENT.API");
file.setDeleteFlag('N');
file.setInstitution(institution);
for (StageAppointment appointment : appointments) {
appointment.setStageFile(file);
appointment.setStageStatus(stageStatus);
stageAppointmentRepository.save(appointment);
}
}
更新2:
为什么此代码有效
@Transactional
private void saveUsingTransaction() {
Institution institution = institutionRepository.findByActCode(3312);
StageStatus status = stageStatusRepository.findOne(1L);
StageFile file = new StageFile();
file.setDeleteFlag('N');
file.setFileName("3312_DIRECTORY.API");
file.setInstitution(institution);
StageDirectory directory = new StageDirectory();
directory.setLocalId("11111111111111111");
directory.setFirstName("Joe");
directory.setLastName("Joe");
directory.setPrimaryEmail("joe@gmail.com");
directory.setStageFile(file);
directory.setStageStatus(status);
stageDirectoryRepository.save(directory);
}
这段代码不是
@Transactional
private void savePassingDirectory(StageDirectory directory) {
Institution institution = institutionRepository.findByActCode(3312);
StageStatus stageStatus = stageStatusRepository.findOne(1L);
// create StageFile reference object
StageFile file = new StageFile();
file.setFileName("3312_DIRECTORY.API");
file.setInstitution(institution);
file.setDeleteFlag('N');
directory.setStageFile(file);
directory.setStageStatus(stageStatus);
stageDirectoryRepository.save(directory);
}
答案 0 :(得分:1)
@Transactional
仅适用于源自外部对象的方法调用到托管bean中。
容器不拦截类内方法调用(并且通过定义私有方法只能以这种方式调用),因此对于这些注释@Transactional
没有任何影响。所以我认为你需要检查你的交易边界实际上在哪里。
我的猜测是saveAppointments
目前在任何交易之外运行。您的institutionRepository.findByActCode()
可能是对托管bean的正确调用,因此使用了一个事务。当该方法再次返回时,您可以在任何事务之外执行操作,以便在调用stageAppointmentRepository.save()
时最终输入新事务。
首先,你必须确保方法saveAppointments
本身实际上在一个事务中运行(例如,使它成为public
并直接从你@Inject
之前调用它),然后你必须确保后续方法调用重用同一个事务,而不是启动一个新事务(即没有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
)
答案 1 :(得分:0)
有趣的是,每当有人使用实体经理一段时间,这个问题就会弹出......
所以你把你的数据库连接到你的持久层,现在你想知道,为什么你的生命一旦离开他们的生态系统就会中断。
原因很简单,因为一旦实体离开其边界,即事务上下文,它就会从实体管理器中分离出来。 如果要存储其更改,则必须再次附加它。
如何完成此操作,您可以阅读What is the proper way to re-attach detached objects in Hibernate?
在您的情况下,appointments
已分离。因此,如果传递的对象位于当前实体管理器的上下文中,则必须检入.save(...)
方法,如果不是,则重新附加它。