How to map an abstract collection with jpa?

2020-08-22 12:58发布

问题:

I am trying to work out if it is possible get JPA to persist an abstract collection with concrete implementations.

So far my code looks like this:

@Entity
public class Report extends Model {

    @OneToMany(mappedBy = "report",fetch=FetchType.EAGER)
    public Set<Item> items;
}

@MappedSuperclass
public abstract class OpsItem extends Model {

    @ManyToOne
    public RetailOpsBranch report;
}


@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class AItem extends OpsItem {
...
}

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class BItem extends OpsItem {
...
}

But I keep stumbling on the mapping error below and I don't really know if this is feasible?

JPA error
A JPA error occurred (Unable to build EntityManagerFactory): Use of @OneToMany or
@ManyToMany targeting an unmapped class: models.Report.items[models.OpsItem]

UPDATE

I don't think the problem is with the abstract class but the the @MappedSuperClass annotation. It looks like jpa does not like to map the one to many relationship with a @MappedSuperClass. If I change the abstract class into a concrete class I have the same error.

If I then change to @Entity annotation this seem to work with both the abstract and concrete class.

This seems a bit strange to map an abstract class with @Entity. I'm I missing anything?

SOLUTION

Managed to figure it out with the help from rhinds. Two points to note:

1) the abstract class needs to be annotated with @Entity and inheritance strategy of table per class for the subclasses to have their own table.

2) Identity Id generation will not work in this scenario and I had to use Table generation type.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class OpsItem extends GenericModel {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    public Long id;

    public String          branchCode;

    @ManyToOne
    public Report report;
}

@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class AItem extends OpsItem {
...
}

回答1:

Yep, it is possible. You should only have the MappedSuperClass on the top abstract class (that isnt going to be persisted in itself) and add Entity annotation on the implementation classes.

Try changing it to something like this:

@MappedSuperclass
public abstract class OpsItem extends Model {

    @ManyToOne
    public RetailOpsBranch report;
}

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class AItem extends OpsItem {
...
}

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class BItem extends OpsItem {
...
}

Check this section of the hibernate docs for details on its use: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e1168


UPDATE

Sorry, completely missed the table per class bit. Hibernate does not support mapping of abstract objects for table per class (can only map List if all implementations are within a single SQL table, and the TABLE_PER_CLASS uses the "table per concrete class" strategy.

Details of limitations and the strategies here: http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#inheritance-limitations