簡體   English   中英

JPA PersistentObjectException:分離的實體傳遞給持久化

[英]JPA PersistentObjectException: detached entity passed to persist

我在嘗試使用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);
}

@Transactional僅適用於從外部對象到托管Bean的方法調用。

容器不會攔截類內方法調用(按照定義,私有方法只能以這種方式調用),因此對於這些方法,@ @Transactional注釋無效。 因此,我認為您需要檢查交易邊界的實際位置。

我的猜測是saveAppointments當前在任何事務之外運行。 institutionRepository.findByActCode() 可能托管bean正確調用,這樣使用事務。 當該方法再次返回時,在調用stageAppointmentRepository.save()時,您將在任何事務之外進行操作以最終進入新事務。

首先,您必須確保方法saveAppointments本身實際上在事務中運行(例如,將其public並從@Inject直接調用它),然后必須確保后續方法調用重復使用同一事務,而不是啟動新的(即沒有@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)

有趣的是,每當有人使用實體管理器一段時間后,此問題就會彈出...

因此,您已將數據庫連接到持久層,現在您想知道,如果您的實體離開生態系統,為什么會崩潰。

這樣做的原因僅是因為一旦實體離開其邊界(即事務上下文),它就會與實體管理器分離。 如果要存儲其更改,則必須再次附加。

您可以在以下位置閱讀如何完成此操作: 在Hibernate中重新附加分離對象的正確方法是什么?

在您的情況下, appointments分離了。 因此,您必須檢入.save(...)方法,如果傳遞的對象是否在當前實體管理器的上下文中,否則,請重新附加它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM