2

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?

Community
  • 1
  • 1
Demiurg
  • 327
  • 1
  • 4
  • 14
  • The accepted answer and associated comments offer 2 ways of achieving this. The difference with your solution is that you are essentially defining TableView as an independent entity which will have all the fields of Table plus the additional field which seems to be not what you want. Implementing one of the proposed solutions via `@OneToOne` or via `@SecondaryTable` would give you what you want. – Alan Hay Feb 14 '17 at 16:39
  • In my design I need to use same getters for both entities. If object `Table` has field `private Object field;` then I want to use `new TableView().getField();` – Demiurg Feb 15 '17 at 07:17

1 Answers1

6

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.

Alan Hay
  • 20,941
  • 2
  • 47
  • 97
  • 1
    Thanks for your answer. Although both solution would work for me they have one disadvantage. I'm using SQL View becouse I have some calculations which I decided to perform on DB side. I'm using my regular `Table` everywhere but on one specific case I need my `Table` with additional informations. Thats why I created `TableView`. `@SecondaryTable` would be nice but it forces me to perform calculations from SQL view everytime I need `Table` object. `InheritanceType.JOINED` creates unnecessary left outer join which (I think so) stress the DB with calculations I don't need as well. – Demiurg Feb 15 '17 at 13:45
  • See update answer using `@OneToOne` to avoid join on every fetch. – Alan Hay Feb 15 '17 at 13:56
  • This is one interesting desing I didn't come up with earlier. I'll give it a try and accept your answer since it would probably be the best solution for me. However I wish there would be something like `@DiscriminatorColumn(null)` :/ – Demiurg Feb 15 '17 at 14:11