2

I'm creating a system where I have some entities that have some properties that are common, like address (with street, number, zip etc.) and phone (number, type, etc.) and I don't want to repeat these columns on each entity.

Here' a example:

  • Student has address and phone
  • Teacher has multiple addresses (home and office) and multiple phones (home, mobile, office)
  • StaffMember has address and multiple phones (home, mobile and office)

I've used something like that while developing Ruby On Rails using polymorphic associations. I've searched for something like on Java/JPA/Hibernate and couldn't find something much like it. I've found many things about JPA inheritance but I don't quite understand it.

Can you give me a example on how to model it and how to use it?

EDIT

After reading my question I think it's not clear enough, so let me add here the database schema I have:

Student
-------
id bigint
name varchar
birth_date date
...

Teacher
-------
id bigint
name varchar
birth_date date
department varchar
...

StaffMember
-------
id bigint
name varchar
birth_date date
department varchar
function varchar
...

Address
-------
id bigint
street varchar
number int
...
entity_id bigint
entity_type varchar

Phone
-----
id bigint
type varchar
number varchar
...
entity_id bigint
entity_type varchar

And for both Address and Phone the columns entity_id and entity_type are references to Student, Teacher and StaffMember.

But how to map it using Hibernate/JPA?

3
  • Just to be sure: Your database schema is fixed an JPA/hibernate has to adapt it? Commented Nov 19, 2016 at 18:23
  • Hi @larsgrefer, no the schema is flexible enough, above is just an example of what it thought it would be the best, but I'm open to suggestions. Commented Nov 19, 2016 at 18:40
  • When you are using spring-boot, just set spring.jpa.generate-ddl=true and let hibernate generate the schema for you Commented Nov 19, 2016 at 19:30

3 Answers 3

1

If you don't mind using Hibernate-specific annotations, there are the @Any and @AnyDef annotations that do what you want. You will need to specify a @MetaValue entry for each entity type related to Address so Hibernate knows how to store the proper value for item_type

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;

import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.MetaValue;

@Entity
public class Address {

    @Any(metaColumn = @Column(name = "ITEM_TYPE"))
    @AnyMetaDef(idType = "long", metaType = "string",
            metaValues = {
                    @MetaValue(targetEntity = Student.class, value = "STUDENT"),
                    @MetaValue(targetEntity = Teacher.class, value = "TEACHER")
            })
    @JoinColumn(name="ITEM_ID")
    private Object item;

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String type;

    @Column
    private String street;

    @Column
    private int number;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public Long getId() {
        return id;
    }

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

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }


    public Object getItem() {
        return item;
    }

    public void setItem(Object item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "Address{" +
                "person=" + item +
                ", id=" + id +
                ", type='" + type + '\'' +
                ", street='" + street + '\'' +
                ", number=" + number +
                '}';
    }
}

Now, you can just use Address in any bean that has the proper @MetaValue entry, both as @ManyToOne association:

@ManyToOne
protected Address address;

Or a @OneToMany association:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Address> addresses;

I created a simple test project using Spring Boot and it seemed to work nicely!

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @João Marcus, that's exactly what I was looking for!
Please explain "..that has the proper @MetaValue entry". This way I am getting unidirectional @OneToMany relation from Student to Addresses which creates an additional students_addresses, teachers_addresses table due to lack of mappedBy attribute which cannot be defined. How can I avoid that?
0

I think what you need is to use a generic repository class.

This post http://blog.netgloo.com/2014/12/18/handling-entities-inheritance-with-spring-data-jpa explains pretty much it. Let me know if works for you.

1 Comment

Hi @Nicolas Fontenele, that's not exactly what I want (or I interpreted it wrong). I've added a little bit more of detail to my question, so it might help you understand better. Thanks anyway.
0

Something along these lines:

   @Embeddable
   public class Address {
        // Fields & accessors
        // Do the Phone class in similar fashion
   }

   @Entity
   @DiscriminatorColumn(name = "entity_type")
   public abstract class Person {
        @Id
        private Integer id;

        private String name;

        @Embedded 
        private Address homeAddress;

        @Embedded 
        private Phone homePhone;

       // Other shared fields & accessors

   }

   @Entity
   public abstract class Employee extends Person {

        @Embedded 
        private Phone mobilePhone;

        @Embedded 
        private Phone officePhone;

       //...

    }

    @Entity
    public class Students extends Person {

    }

    @Entity
    public class StaffMember extends Employeee {

    }

    @Entity
    public class Teacher extends Employeee {

      @Embedded
      private Address officeAddress;

      // Accessors & other ...
   }

2 Comments

Hi @dev-null by using Embeddable can I have Address being stored in a separate table? I don't want to replicate this structure on each table on my database.
The point of @Embeddable is that it does not have special table it is just the data structure embedded in the other tables. Something like compound data type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.