Demiurg
Demiurg

Reputation: 347

JPA mapping Views which extends Tables

This is similar to JPA mapping views and tables with inheritance but since accepted answer does not satisfy me I decided to ask my own question.

I have one based class that holds common fields for all entities

@MappedSuperclass
@Access(AccessType.FIELD)
public abstract class CommonFields{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;
   (...)
}

Lets say I have one entity called Table

@Entity
@Table(name = "my_table")
public class Table extends CommonFields{
(..)
}

This works perfectly for me. I created view, which adds one column to my_table. I mapped it this way:

@Immutable
@Entity
@Table(name = "my_table_plus_view")
public class TableView extends Table{
(..)
}

TableView is read_only. This is simple Table with additional informations for presentation purposes.

This application runs on Tomcat and Postgres DB. Server starts whithout errors but when I try to get all records from view I got an error that my Table does not contain DTYPE column. I know what it is and how it works but since I don't need it I don't want it.

I want read/write my Table just like now but I don't know how to map my TableView so JPA query can use it. I did some efforts whith InheritanceType but with no success. If I copy all fields from Table to TableView then my application will run just as expected but this this doesn't suit me. How can I map my view so I can use inheritance?

Upvotes: 2

Views: 5219

Answers (1)

Alan Hay
Alan Hay

Reputation: 23226

You could have one Entity mapped to 2 tables (or a table and a view in your case). You can use @SecondaryTable. The columns of the view then become properties of the entity Table and can be queried like any other.

To ensure immutability you can mark the column you can use the insertable and updatable properties of @Column.

@Entity
@Table(name = "my_table")
@SecondaryTable(name = "my_table_plus_view")
public class Table extends CommonFields{

     @Column(name="col_from_view", table="my_table_plus_view", 
           insertable=false, updatable = false)
     private String someField;
}

https://en.wikibooks.org/wiki/Java_Persistence/Tables#Multiple_tables

In this scenario your application is then dealing only with one entity.

An alternative would be to create a new Entity pointing to the view and map it from Table using a @OneToOne. As long as this relationship always exists (i.e. is marked as optional=false) then this approach has the benefit of only loading the view data on demand whereas with the @SecondaryTable option the join will be performed on each load. Clients of your model could still deal only with one entity as you do not need to expose the second entity to the outside world:

@Entity
@Table(name = "my_table")
public class Table extends CommonFields{

   //can be lazily loaded only when optional = false
   //see: http://stackoverflow.com/questions/17987638/hibernate-one-to-one-lazy-loading-optional-false
   @OneToOne(optional = false)
   @JoinColumn(name = "x")
   private TableView tableView;

   public int getCalculatedValue(){
        return tableView.getCalculatedValue();
   }
}


@Entity
@Table(name = "my_table_plus_view")
public class TableView{

    private int calculatedValue;
}

In an alternative scenario you can use inheritance as you have tried above. Your application would then expose 2 entities Table and TableView. As you have 2 separate tables you would need to indicate that you wanted to use a Joined inheritance strategy. This is the bit that is missing in your code:

https://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Joined.2C_Multiple_Table_Inheritance

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name = "my_table")
public class Table extends CommonFields{

}


@Entity
@Table(name = "my_table_plus_view")
public class TableView extends Table{

}

With the Joined strategy Hibernate does not require a discriminator column.

Upvotes: 6

Related Questions