JPA entity with a interface attribute, is it possible?

It is really a good idea but unfortunately directly mapping interfaces as an entity attribute is not supported by JPA.

You can only map top level classes directly annotated with @Entity. This top level class may implement an interface though.

This feature has been requested and discussed for a long time.

Also take a look at this and this.

Depending on what you're trying to accomplish, @Inheritance annotation with table-per-class strategy could be an option.

I hope it helps.


No, not possible with JPA or Hibernate.

It does seem strange, when coding in the Java language which allows for attributes to be interfaces, that a persistence standard like JPA, intended for Java, does not support persisting attributes that are interfaces.

I always found it really very frustrating when my ORM would force me to refactor my 'pure' OO model just so that it could persist it.

It's not that it's technically impossible to implement persisting of interface attributes - in fact JDO has supported persistence of interfaces since forever which is why I started using it years ago for all of my own projects.

I've been tempted to switch to JPA, not because it is technically superior (in fact, quite the opposite) but just because of "herd mentality".

In recent contract work I have been forced to gain experience with JPA/Hibernate and in doing so, have lived out the many limitations and inefficiencies of that combination compared with JDO/DataNucleus. This was a great experience because it helped me quell my desire to join "the herd" :)


It is possible with one caveat - you have to point JPA to a target entity which should be a concrete class. However, it can be an abstract class which implements your interface. So one principle of good design you'll have to break and in particular - "favour composition over inheritance".

Here's how to do it:

  1. In your user class (which references your interface Task):
    @OneToOne(targetEntity = BaseTask.class)
    private Task task;

so here Task is an interface, but you have to declare an abstract class BaseTask.

  1. In your BaseTask:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="task_type")
@Table(name="Task")
public abstract class BaseTask implements Task{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    }

Here the important thing is @DiscriminatorColumn - since all fields of the inheritance tree will be stored in 1 table (you specified that with the @Inheritance(strategy = InheritanceType.SINGLE_TABLE) annotation above. So this discriminator column will contain a label which will allow JPA to differentiate what kind of task you're talking about

  1. Your concrete classes:
@Entity
@DiscriminatorValue("api")
public class ApiTask extends BaseTask {

or

@Entity
@DiscriminatorValue("ssh")
public class SshTask extends BaseTask{

As you can see the discriminator value tells JPA what task it is going to load (what class to instantiate).