简体   繁体   中英

Hibernate and Java generics - Unable to create session factory using MappedSuperClass

I have a few classes that we are attempting to extend to allow reuse of code, but hibernate is having none of it. Here are the new classes and their extensions:

Super statement class

@MappedSuperclass
public abstract class CoreStatement<S extends Approval>
    implements java.io.Serializable
{

    public abstract Long getId();

    public abstract void setId(Long id);

    public abstract Set<S> getApprovals();

    public abstract void setApprovals(Set<S> approvals);

}

Base statement class - This does get extended later on, but via a single table inheritance

@Entity
@Table(name="EXPNS_STTMNT")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
        name="CLASS_ID",
        discriminatorType = DiscriminatorType.INTEGER
)
public abstract class ExpenseStatement extends CoreStatement<ExpenseApproval>
{
    private Set<ExpenseApproval> approvals;

    @Override
    @Id
    @Column(name="ID", unique=true, nullable=false, precision=10, scale=0)
    public Long getId() {
        return this.id;
    }

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

    @Override
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="statement",
        targetEntity = ExpenseApproval.class)
    public Set<ExpenseApproval> getApprovals() {
        return approvals;
    }

    public void setApprovals(Set<ExpenseApproval> approvals) {
        this.approvals = approvals;
    }
}

Approval super class

@MappedSuperclass
public abstract class Approval<T extends CoreStatement> implements java.io.Serializable  {
    public abstract Long getId();

    public abstract void setId(Long id);
    public abstract T getStatement();

    public abstract void setStatement(T statement);
}

Approval base class

@Entity
@Table(name="APPRVL")
public class ExpenseApproval extends Approval<ExpenseStatement>{
    private Long id;
    private ExpenseStatement statement;
    @Id
    @Column(name="ID", unique=true, nullable=false, precision=10, scale=0)
    public Long getId() {
        return this.id;
    }

    @Override
    public void setId(Long id) {
        this.id = id;
    }
    @Override
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="EXPENSE_STATEMENT_ID", nullable=true)
    public ExpenseStatement getStatement() {
        return statement;
    }

    @Override
    public void setStatement(ExpenseStatement statement) {
        this.statement= statement;
    }
}

When running through the UnitTests, we get the error:

java.lang.ExceptionInInitializerError Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: EXPNS_STTMNT, for columns: [org.hibernate.mapping.Column(approvals)] at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:314) at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:292) ....

It appears to be a mapping error of some kind, but I can't narrow it down. Many people who have posted the issue before had the problem where their Annotations were located above the private property and also getters ie They mixed and matched their annotation placement, but this doesn't appear to be the case here. Does anyone else have any suggestions on what could be causing the issue?

Hibernate and it's error messages are generally terrible, but I managed to solve this. It was nothing to do with the set, but more the location of the annotations. The annotations should generally be on the superclass and not the base class.

So the example would be:

@MappedSuperclass
public abstract class Approval<T extends CoreStatement> implements java.io.Serializable  {

    private Long id;
    private T statement


    @Id
    @Column(name="ID", unique=true, nullable=false)
    public Long getId() {
        return id;
    }

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

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="STATEMENT_ID", nullable=true)
    public T getStatement() {
        return statement;
    }

    public void setStatement(T statement) {
        this.statement = statement;
    }
}

And then to set the appropriate column or override the attributes of the annotation, you can use @AssociationOverrides on top of the class naming convention.

@Entity
@Table(name="APPRVL")
@AssociationOverrides({
    @AssociationOverride(name="statement", joinColumns = @JoinColumn(name = "EXPENSE_STATEMENT_ID"))
})    
public class ExpenseApproval extends Approval<ExpenseStatement>{
    private ExpenseStatement statement;

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM