Search code examples
javahibernatejpaone-to-manymany-to-one

One-To-Many - How to Link to Objects correctly?


I'm using JPA and have some difficulties to understand how the One-To-Many Realtionship works.

I have the following two classes:

@Entity
public class myCheck {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    protected int Check_id;

    @Column
    private String name;

    @ManyToOne
    private mySystem system;

    @Override
    public String toString() {
        return this.name;
    }

    public int getId() {
        return Check_id;
    }

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

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

    public mySystem getLinkSystem() {
        return system;
    }

    public void linkSystem(mySystem system) {
        this.system = system;
    }

}

and:

@Entity
public class mySystem {
    @Id
    @GeneratedValue
    @Column(name = "system_id")
    protected int system_id;
    @Column
    public String name;

    @ManyToOne(cascade=CascadeType.ALL)
    private mySystem parent;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "system", fetch = FetchType.EAGER)
    private List<myCheck> checks;

    public mySystem() {
        //subSystems = new ArrayList<mySystem>();
        checks = new ArrayList<myCheck>();
    }   

    public boolean linkCheck(myCheck hc) {
        return checks.add(hc);
    }

    public boolean unlinkCheck(myCheck hc) {
        return checks.remove(hc);
    }

    public List<myCheck> getlinkedChecks() {
        return checks;
    }

    public myCheck getLinkCheck(int hcId) {
        for (myCheck hc : checks) {
            if (hc.getId() == hcId)
                return hc;
        }
        return null;
    }

    public int getId() {
        return system_id;
    }

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

    public String getName() {
        return name;
    }

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


    @Override
    public String toString() {
        return this.getName();
    }

}

Now I have an existing System in my database, which is loaded:

    // load System
    EntityManager entityManager1 = entityManagerFactory.createEntityManager();
    List<mySystem> systems = entityManager1.createQuery("from mySystem").getResultList();
    entityManager1.close();

I want to add two new checks to Systems. What is working is:

    EntityManager entityManager2 = entityManagerFactory.createEntityManager();
    entityManager2.getTransaction().begin();

    myCheck check = new myCheck();
    check.setName("Check 1");
    check.linkSystem(systems.get(0));
    entityManager2.persist(check);

    myCheck check2 = new myCheck();
    check2.setName("Check 2");
    check2.linkSystem(systems.get(0));
    entityManager2.persist(check2);

    entityManager2.merge(systems.get(0));
    entityManager2.getTransaction().commit();
    entityManager2.close();

But I can't do this:

    EntityManager entityManager2 = entityManagerFactory.createEntityManager();
    entityManager2.getTransaction().begin();

    myCheck check = new myCheck();
    check.setName("Check 1");
    systems.get(0).linkCheck(check);
    entityManager2.persist(check);

    myCheck check2 = new myCheck();
    check2.setName("Check 2");
    systems.get(0).linkCheck(check);
    entityManager2.persist(check2);

    entityManager2.merge(systems.get(0));
    entityManager2.getTransaction().commit();
    entityManager2.close();

The second solution will save the checks, but I don't link them with the system.

Has someone a explanation for this? I really want to understand this.


Solution

  • You have bidirectional relation which means each side of the relation should have a reference to the other side.

    so in your persistence logic you will also need to inject the system in your check

    myCheck check = new myCheck();
    check.setName("Check 1");
    check.linkSystem(systems.get(0);
    systems.get(0).linkCheck(check);
    entityManager2.persist(check);
    

    But in this case you will have a problem if your (systems.get(0)) is not attached to the persistence context because you will be having reference to deatached object when persisting the check, you can either put Cascade on the system inside check class or instead persist the system, it already cascades the check so the check will be persisted