Search code examples
javajakarta-eeejbtoplink

How to force TopLink to set up OneToMany relation immediately?


I have two entites, Person and Teacher, with a OneToMany relationship:

public class Person implements Serializable {
    @Id
    @Column(nullable = false)
    private String login;
    @OneToMany(mappedBy = "person")
    private List<Teacher> teacherList;
}

public class Teacher implements Serializable {
    @ManyToOne
    @JoinColumn(name = "LOGIN")
    private Person person;
}

I also have a DTO class for Person:

public class PersonDTO extends Person {
    private boolean isTeacher;

    public PersonDTO(Person p) {
        setLogin(p.getLogin());
        isTeacher = getTeacherList().size() > 0;
    }

    public boolean isTeacher() {
        return isTeacher;
    }
}

My EJB has code like this for inserting new Teachers and reading Persons:

public void addTeacher(String login, String password, String name,
                       String dept, String title) {
    p = new Person(true, false, login, name, password);
    persistEntity(p);
    Teacher t = new Teacher(dept, p, title);
    persistEntity(t);
    em.flush();    // 'em' is the Entity Manager
}

public PersonDTO readPerson(String login) {
    Person p = em.find(Person.class, login);
    PersonDTO pdto = new PersonDTO(p);
    return pdto;
}

On my webpage, there's a table showing all teachers, textboxes for the various fields, and a button to add a new teacher and update the table, the listener for which looks like this:

public void addTeacherListener(ActionEvent actionEvent) {
    adminManager.addTeacher(/* parameters */);

    // update table (which is bound to 'teacherList')
    teacherList = new LinkedList<Teacher>();
    List<String> logins = adminManager.findAllPerson();
    for (String login : logins) {
        PersonDTO pdto = adminManager.readPerson(login);
        if (pdto.isTeacher()) {
          teacherList.add(pdto.getTeacherList().get(0));
        }
    }
}

The problem is that after inserting a new Teacher, it doesn't show up in the table. The precise reason for this is that the PersonDTO object returned by readPerson has isTeacher set to false: which means that at the time the DTO object is created (that is, after the insertion operation finished, including the two persists and the flush at the end), the Person object for the given teacher has an empty teacherList.

However, if I close the page and relaunch it from the IDE, the newly inserted elements show up in the table. So apparently TopLink does set up the relationship, just not immediately. What exactly is going on, and how can I force it to do it right after the insertion?


Solution

  • JPA doesn't maintain consistent bidirectional relationships between object in memory automatically, it's your own task.

    That is, after executing addTeacher() you have a Teacher with person property pointing to the Person. This relationship is reflected in the database, but it's not reflected in teacherList of the Person that is already in memory. em.find() return the existing instance of Person from the persistence context, so its teacherList is iempty.

    Therefore you need to add a Teacher into Person.teacherList in addTeacher().