1

I'm getting the detached entity passed to persist: model. Company when I use @OneToMany mapping. I tried different method but the same result. I added @OneToMany with CascadeTypes Persist and all. Remove those. Set the manager and tried to persist, commented the set of manager and tried to persist. None of them worked.

Company:

@Entity
public class Company {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private int id;

    private int age;

    @ElementCollection
    private Set<Manager> managers;

    private String name;


    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setManagers(Set<Manager> managers) {
        this.managers = managers;
    }


    public Set<Manager> getManager() {
        return managers;
    }

}

Manager

@Entity
public class Manager {

    @Id
    @GeneratedValue
    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Test

public class Test {
    public static void main(String args[]) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpatestproject");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

    //  Manager m1 = new Manager();
    //  m1.setName("abc");
    //  Manager m2 = new Manager();
    //  m2.setName("def");
    //  Set<Manager> managers = new HashSet();
    //  managers.add(m1);
    //  managers.add(m2);
    //  

        Company company = new Company();
        company.setId(1);
        company.setName("LTE");
    //  company.setManagers(managers);


        tx.begin();

        em.persist(company);
        tx.commit();
        System.out.println("Done");
    }
}
MWiesner
  • 8,868
  • 11
  • 36
  • 70
user3310115
  • 1,372
  • 2
  • 18
  • 48

2 Answers2

2

You are setting the Company id manually with company.setId(1);.

A @GeneratedValue is best left alone.

The persistence context will assume the record already exists but is detached since the id is set but the entity is not in the current persistence context (represented by the current EntityManager).

CannedMoose
  • 509
  • 1
  • 10
  • 18
0

First, one can use an abstract base class annotated with @MappedSuperClass for generic aspects, such as primary key management:

@MappedSuperClass
public abstract class AbstractEntity {

    @Id
    @Column(name = "id")
    //@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pk-sequence")
    //@SequenceGenerator(name = "pk-sequence", sequenceName = "ID_GEN", allocationSize = 1)
    protected Long objectID = -1;

    @Version
    private int version;

    public int getVersion() {
        return version;
    }

    public long getObjectID() {
        return objectID;
    }   
}

Note: there is no setter for either the objectID nor for the version attribute. Thus, at runtime, both values will remain under control by the ORM only. This way, instances of EntityManager can safely manage the lifecycle of an Entity. See another answer for details on @Version. Moreover, this generic approach can be enhanced to include other generic aspects, e.g. creation/modification date/timestamps.

This will greatly simplify your existing @Entity classes.

@Entity
public class Company extends AbstractEntity  {
    private static final long serialVersionUID = 1L;

    private int age;

    // @ElementCollection <-- Use a 1-n annotation here instead.
    @OneToMany(cascade=CascadeType.ALL) 
    // to ensure non persisted manager objects will be persisted via a cascade chain here...
    private Set<Manager> managers;

    private String name;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setManagers(Set<Manager> managers) {
        this.managers = managers;
    }


    public Set<Manager> getManager() {
        return managers;
    }

}

Please also note that I commented out @ElementCollection but instead changed the mapping to make use of the @OneToMany annotation. The rationale behind this is that an ElementCollection can only be used to specify a one-to-many relationship to an Embeddable object, or basic types (e.g., referenced via a Set< Integer >, etc.) which is not the case for the entity class Manager.

Finally,

@Entity
public class Manager extends AbstractEntity {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

With the above changes you should be able to run the code presented in Test#main(..) method. Please note, that you should still check the correct association order before persisting. I have not tested the provided code snippet as presented in your question; nevertheless you should be able to make it work.

Hope it helps.

MWiesner
  • 8,868
  • 11
  • 36
  • 70