我有一个包含多对一关系的jpa持久化对象模型:一个Account有多个transaction。一个事务有一个帐户。

下面是一段代码:

@Entity
public class Transaction {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = {CascadeType.ALL},fetch= FetchType.EAGER)
    private Account fromAccount;
....

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @OneToMany(cascade = {CascadeType.ALL},fetch= FetchType.EAGER, mappedBy = "fromAccount")
    private Set<Transaction> transactions;

我能够创建Account对象,向其添加事务,并正确地持久化Account对象。但是,当我创建一个事务,使用现有的已经持久化的帐户,并持久化的事务,我得到一个异常:

导致:org.hibernate.PersistentObjectException:传递给persist: com.paulsanwald.Account的分离实体 org.hibernate.event.internal.DefaultPersistEventListener.onPersist (DefaultPersistEventListener.java: 141)

因此,我能够持久化一个包含事务的Account,但不能持久化一个具有Account的Transaction。我认为这是因为帐户可能没有附加,但这段代码仍然给了我相同的异常:

if (account.getId()!=null) {
    account = entityManager.merge(account);
}
Transaction transaction = new Transaction(account,"other stuff");
 // the below fails with a "detached entity" message. why?
entityManager.persist(transaction);

如何正确地保存与已经持久化的帐户对象相关联的事务?


当前回答

这里的问题是缺乏控制。

当我们使用CrudRepository/JPARepository保存方法时,我们失去了事务控制。

为了解决这个问题,我们有了事务管理

我更喜欢@Transactional机制

进口

import javax.transaction.Transactional;

完整源代码:

package com.oracle.dto;

import lombok.*;

import javax.persistence.*;
import java.util.Date;
import java.util.List;

@Entity
@Data
@ToString(exclude = {"employee"})
@EqualsAndHashCode(exclude = {"employee"})
public class Project {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO,generator = "ps")
    @SequenceGenerator(name = "ps",sequenceName = "project_seq",initialValue = 1000,allocationSize = 1)
    @Setter(AccessLevel.NONE)
    @Column(name = "project_id",updatable = false,nullable = false)
    private Integer pId;
    @Column(name="project_name",nullable = false,updatable = true)
    private String projectName;
    @Column(name="team_size",nullable = true,updatable = true)
    private Integer teamSize;
    @Column(name="start_date")
    private Date startDate;
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name="projectemp_join_table",
        joinColumns = {@JoinColumn(name = "project_id")},
        inverseJoinColumns = {@JoinColumn(name="emp_id")}
    )
    private List<Employee> employees;
}
package com.oracle.dto;

import lombok.*;

import javax.persistence.*;
import java.util.List;

@Entity
@Data
@EqualsAndHashCode(exclude = {"projects"})
@ToString(exclude = {"projects"})
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO,generator = "es")
    @SequenceGenerator(name = "es",sequenceName = "emp_seq",allocationSize = 1,initialValue = 2000)
    @Setter(AccessLevel.NONE)
    @Column(name = "emp_id",nullable = false,updatable = false)
    private Integer eId;
    @Column(name="fist_name")
    private String firstName;
    @Column(name="last_name")
    private String lastName;
    @ManyToMany(mappedBy = "employees")
    private List<Project> projects;
}


package com.oracle.repo;

import com.oracle.dto.Employee;
import org.springframework.data.jpa.repository.JpaRepository;

public interface EmployeeRepo extends JpaRepository<Employee,Integer> {
}

package com.oracle.repo;

import com.oracle.dto.Project;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProjectRepo extends JpaRepository<Project,Integer> {
}

package com.oracle.services;

import com.oracle.dto.Employee;
import com.oracle.dto.Project;
import com.oracle.repo.ProjectRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.transaction.Transactional;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

@Component
public class DBServices {
    @Autowired
    private ProjectRepo repo;
    @Transactional
    public void performActivity(){

        Project p1 = new Project();
        p1.setProjectName("Bank 2");
        p1.setTeamSize(20);
        p1.setStartDate(new Date(2020, 12, 22));

        Project p2 = new Project();
        p2.setProjectName("Bank 1");
        p2.setTeamSize(21);
        p2.setStartDate(new Date(2020, 12, 22));

        Project p3 = new Project();
        p3.setProjectName("Customs");
        p3.setTeamSize(11);
        p3.setStartDate(new Date(2010, 11, 20));

        Employee e1 = new Employee();
        e1.setFirstName("Pratik");
        e1.setLastName("Gaurav");

        Employee e2 = new Employee();
        e2.setFirstName("Ankita");
        e2.setLastName("Noopur");

        Employee e3 = new Employee();
        e3.setFirstName("Rudra");
        e3.setLastName("Narayan");

        List<Employee> empList1 = new LinkedList<Employee>();
        empList1.add(e2);
        empList1.add(e3);

        List<Employee> empList2 = new LinkedList<Employee>();
        empList2.add(e1);
        empList2.add(e2);

        List<Project> pl1=new LinkedList<Project>();
        pl1.add(p1);
        pl1.add(p2);

        List<Project> pl2=new LinkedList<Project>();
        pl2.add(p2);pl2.add(p3);

        p1.setEmployees(empList1);
        p2.setEmployees(empList2);

        e1.setProjects(pl1);
        e2.setProjects(pl2);

        repo.save(p1);
        repo.save(p2);
        repo.save(p3);

    }
}

其他回答

我基于Spring Data jpa的回答是:我只是在外部方法中添加了一个@Transactional注释。

为什么它有效

由于没有活动的Hibernate Session上下文,子实体立即被分离。提供一个Spring (Data JPA)事务可以确保存在Hibernate会话。

参考:

https://vladmihalcea.com/a-beginners-guide-to-jpa-hibernate-entity-state-transitions/

通过在下一个对象之前保存依赖对象来解决。

这发生在我身上,因为我没有设置Id(这不是自动生成的)。并试图拯救@ManytoOne的关系

这是我的药。

Below is my Entity. Mark that the id is annotated with @GeneratedValue(strategy = GenerationType.AUTO), which means that the id would be generated by the Hibernate. Don't set it when entity object is created. As that will be auto generated by the Hibernate. Mind you if the entity id field is not marked with @GeneratedValue then not assigning the id a value manually is also a crime, which will be greeted with IdentifierGenerationException: ids for this class must be manually assigned before calling save()

@Entity
@Data
@NamedQuery(name = "SimpleObject.findAll", query="Select s FROM SimpleObject s")
public class SimpleObject {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column
    private String key;

    @Column
    private String value;

}

这是我的主类。

public class SimpleObjectMain {

    public static void main(String[] args) {

        System.out.println("Hello Hello From SimpleObjectMain");

        SimpleObject simpleObject = new SimpleObject();
        simpleObject.setId(420L); // Not right, when id is a generated value then no need to set this.
        simpleObject.setKey("Friend");
        simpleObject.setValue("Bani");

        EntityManager entityManager = EntityManagerUtil.getEntityManager();
        entityManager.getTransaction().begin();
        entityManager.persist(simpleObject);
        entityManager.getTransaction().commit();

        List<SimpleObject> simpleObjectList = entityManager.createNamedQuery("SimpleObject.findAll").getResultList();
        for(SimpleObject simple : simpleObjectList){
            System.out.println(simple);
        }

        entityManager.close();
        
    }
}

我想救它的时候,它把它扔出去了

PersistentObjectException: detached entity passed to persist.

我所需要修复的是删除主方法中simpleObject的id设置行。

在我的情况下,我正在提交事务时,持久化方法被使用。 在更改persist to save方法时,该问题得到了解决。

可能在这种情况下,您使用merge逻辑获得了帐户对象,而persist用于持久化新对象,如果层次结构有一个已经持久化的对象,它将报错。在这种情况下,应该使用saveOrUpdate,而不是持久化。