Home > Enterprise >  Spring JPA abstract/inheritance relationships
Spring JPA abstract/inheritance relationships

Time:09-24

I have a User, a Shop, and an Address entity. Both a user and a shop can have an address.

What would be the best way to set up such a relationship using Spring JPA/Hibernate?

I had thought of creating an interface and let user and shop implement it, then use the interface in the address entity class to access the their common code. I couldn't get it to work, and I kinda feel like I'm over-complicating things. It might have looked something like this:

  Addressable          Address
       |
    --- --- 
   |       |
 Shop     User
@Entity
@Table(name = "shops")
public class Shop implements Addressable {

    @OneToOne
    @JoinColumn(name = "address_id")
    private Address address;

    @Column(name = "name", nullable = false)
    private String name;
@Entity
@Table(name = "users")
public class User implements Addressable {

    @OneToOne
    @JoinColumn(name = "address_id")
    private Address address;

    @Column(name = "name", nullable = false)
    private String name;
public interface Addressable {

    String getName();
    void setName(String name);
@Entity
@Table(name = "addresses")
public class Address {

    @OneToOne
    private Addressable addressable;

    @Override
    public String toString() {
        return addressable.getName()   "\n"  
               streetName   " "   streetNumber   "\n"  
               postalCode   " "   cityName;
    }

I might be waay off course, it doesn't quite feel right. I'm sure this is problem other people have encountered as well, though I as of yet to have found a nice if any solution.

CodePudding user response:

Do you need your addresses to be separate entities? In other words, is there a specific need to store addresses in a dedicated table, or do you just want to group address related properties in a common object included in shop and user just for the purpose of code reuse? If the latter applies, you could make the address embeddable and mark it as embedded in your user and shop entities. They become additional properties in the user and shop table represented by the corresponding entities.

@Entity
@Table(name = "shops")
public class Shop {

    @Embedded
    private Address address;



@Entity
@Table(name = "users")
public class User {

    @Embedded
    private Address address;



@Embeddable
public class Address {

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

    public String getName() {...}

If you need to store them as separate entries in a dedicated table, using a "real" relationship is the correct approach (one-to-one, one-to-many annotations etc...)

Using an interface as a member of a relationship in JPA instead of a real object is not possible. A member of a relationship needs to be a POJO (a real object) because the JPA engine (like Hibernate) needs to be able to instantiate such classes in the background. Since you cannot instantiate an interface (or abstract class), you cannot use interfaces in relationships directly. One rather dirty trick could be working when defining custom converters that translate between the interface and a real POJO. This I have never tried before so I am not sure this will work. I also discourage this approach since it would imply some mechanism to choose the concrete class implementation to instantiate in your converter which seems very bad code design to me ;-)

What you also could try is create an abstract class storing the name property together with getter and setters and proper column annotations for all common properties. Mark this abstract class as MappedSuperclass and let your User and Shop class subclass this abstract class. Both subclasses have the superclass' common code inherited. The mappedsuperclass and your subclass code will be merged into one entity.

@Entity
@Table(name = "shops")
public class Shop extends Addressable{

    ...



@Entity
@Table(name = "users")
public class User extends Addressable{

    ...



@MappedSuperclass
public abstract class Addressable {

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

    public String getName() {...}

CodePudding user response:

It looks like you are just using Java EE JPA, not Spring JPA or Hibernate.

There are data model patterns such as "the party model" where Person and Shop are subclasses of the same parent. (Parties have one or more Addresses and are People or Organizations, Shops are Organizations)

while you can use @MappedSuperClass to have a non-entity superclass, for your specific question I would look the Java EE JPA's @AttributeOverride. This helps to let JPA know that what in the Interface will be part of the Entity.

  • Related