Search code examples
javajpainheritancemappedsuperclass

@MappedSuperclass and implementation table


I inherited some pretty awful code that I am looking to refactor to make more reusable. There is a set of reporting tables which are primarily composed of 3 columns: id, report_type_fk, and report_description. I would like to merge all the reporting tables into one for ease of use.

I am refactoring the code and think that it would be better to break our current entities up so that Report is an abstract class with type implementations. For example a DmvReport extends Report, CreditScoreReport extends Report, etc.

The problem I am running into is that there would only be 1 report table that all entities would need to save to. Is there a way to make all concrete implementations of the abstract Report object save into the same table?

Here's an example of the bad code I inherited

Report class

@Entity
@Table(name = "report")
public class Report<E extends Exception> {
    private long id;
    private ReportType type;
    private String description;
   ...
   ...
}

CreditReport class

@Entity
@Table(name = "credit_report")
public class CreditScore Report<E extends Exception> extends Report<E> {
    private long id;
    private ReportType type;
    private String description;
   ...
   ...
}

I am looking to turn it into:

@MappedSuperclass
@Table(name = "report")
public abstract class Report<E extends Exception> {
    @Id @Column(name="id")
    private long id;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "report_type_id")
    private ReportType type;

    @column(name="description")
    private String description;
   ...
   ...
}

@Entity
@Table(name = "report")
public class CreditScoreReport<E extends Exception> extends Report<E> {

   public void doCreditScoreStuff(){
      ...
   }
}

@Entity
@Table(name = "report")
public class DmvReport<E extends Exception> extends Report<E> {
   public void doDmvStuff(){
      ...
   }
}

Solution

  • I think you should use @Inheritance instead of @MappedSuperClass. Your code would look like this:

    @Entity
    @Table(name = "report")
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(name = "report_type_id", discriminatorType = DiscriminatorType.INTEGER)
    public abstract class Report<E extends Exception> {
        @Id @Column(name="id")
        private long id;
    
        @column(name="description")
        private String description;
       ...
       ...
    }
    
    @Entity(name = "CreditScoreReport")
    @DiscriminatorValue("1") // the id corresponding to the credit score report
    public class CreditScoreReport<E extends Exception> extends Report<E> {
    
       @Column(name = "specific_credit_score_report_1)
       private Integer specificCreditScoreReport1;
    
       public void doCreditScoreStuff(){
          ...
       }
    }
    
    @Entity(name = "DmvReport")
    @DiscriminatorValue("2") // the id corresponding to the DMV report
    public class DmvReport<E extends Exception> extends Report<E> {
    
       @Column(name = "specific_dmv_score_report_1)
       private Integer specificDmvScoreReport1;
    
       public void doDmvStuff(){
          ...
       }
    }
    

    This strategy allows you to store credit score report and DMV report data in one table (report), but instanciate the proper entity according to the report_value_id field. You don't have to define the report_value_id in your parameters because it was already used to create the required entity.

    Is this what you're looking for?