Search code examples
annotationsjaxbjpa-2.0one-to-manysingle-table-inheritance

complex JPA inheritance with JAXB specific contract


Hi guys I've searched everywhere for this, so this is literally a last resort, I am trying to get a nice database structure for the following code:

@Entity
@Table(name = "tasks")
@XmlRootElement(name = "task")
@XmlType(propOrder = { "id", "taskTitle", "notifications" })
public class Task {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "identifier")
    private String taskTitle;

    @OneToMany(mappedBy = "???super.task???", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<User> users;

    @OneToMany(mappedBy = "task", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Group> groups;

    @XmlElement(name = "notify_list")
    public Notify getNotifications() {
        return new Notify(this);
    }
... (the users and groups getters are XmlTransient)
}

@XmlAccessorType
@XmlType(propOrder = { "users", "groups" })
public class Notify {
    private Task task;
    public Notify() {
        super();
    }

    public Notify(Task task) {
        this.task = task;
    }

    @XmlElementWrapper(name = "users")
    @XmlElements({ @XmlElement(name = "user") })
    public Set<User> getUsers() {
        return task.getUsers();
    }

    @XmlElementWrapper(name = "groups")
    @XmlElements({ @XmlElement(name = "group") })
    public Set<Group> getGroups() {
        return task.getGroups();
    }
}

@MappedSuperclass
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @ManyToOne
    private Task task;

    @Column(name = "person_id")
    private Integer personId;

    @Transient
    private String firstName, lastName, email;

...(public getters/setters here)
}

@Entity
@Table(name = "persons")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "group_or_user")
@DiscriminatorValue(value = "group")
@XmlAccessorType
@XmlType(propOrder = { "personId", "firstName", "lastName", "email" })
public class Group extends Person {
    ...(inherits everything from Person and defines XmlElements)
}

@Entity
@DiscriminatorValue(value = "user")
@XmlAccessorType
@XmlType(propOrder = { "username", "personId", "firstName", "lastName",
    "email", "phone" })
public class User extends Group {

    @Transient
    private String username, phone;

    @XmlElement(name = "username")
    public String getUsername() {
        return username;
    }

    @XmlElement(name = "phone")
    public String getPhone() {
    return phone;
    }

...(the other getters/setters are inherited)
}

also i want my xml like this:

<task>
    <id>10001</id>
    <identifier>Title of the task</identifier>
    <notify_list>
      <users>
        <user>
          <username>firstname.lastname</username>
          <person_id>12</person_id>
          <firstname>FirstName</firstname>
          <lastname>LastName</lastname>
          <email>me@myname.com</email>
          <phone>023545848796</phone>
        </user>
      </users>
      <groups>
        <group>
          <person_id>21247415</person_id>
          <firstname>PizzaGroup</firstname>
          <lastname>Pizza</lastname>
          <email>blabla@something.com</email>
        </group>
      </groups>
    </notify_list>
</task>

and by nice db structure i mean: 1 table for the task and 1 table for persons(with a discriminator column of course). As you can see i ran into a problem with the OneToMany relationship on the Set of users because it sais that "it can't find any reference to the unknown target entity i mapBy for the users i.e. the task, which is defined in the MappedSuperclass Person so from what i know it should inherit it because Group inherits it. So the question actually is: what am i doing wrong?

EDIT:

i forgot to mention that i can persist only the person_id's for the users and groups and that the rest i get from another db, but that's another story :)


Solution

  • The mappedBy attribute value should be "task". The problem is that the task field in Person is of type MaintenanceTask instead of Task.

    Also, make sure all the entities are listed in the hibernate configuration file.

    But I think this mapping won't work, because you try to define two different associations on one side, mapped by a single one (and a single column) on the other size. Since the collection of groups will also hold the users (as User extends Group), you should probably remove the users set in the task. Just provide two getters that return a filtered collection, based on the actual type of the group.