开发者

JPA - One to One relationship with shared primary key among multiple tables

开发者 https://www.devze.com 2023-04-10 15:16 出处:网络
Consider the below example: I have 3 tables: Fruit, Orange and Apple id is generated in fruit table and is the primary key here

Consider the below example:

I have 3 tables: Fruit, Orange and Apple

id is generated in fruit table and is the primary key here

id is also primary key for Orange and Apple (shared primary key)

So for e.g. if id in fruit is 1, 2, 3, 4, 5 -- then scenario could be 1, 2 are Orange, 3, 4 are Apple and 5 is again Orange..

So Orange table will have id 1,2,5 while Apple table will have id 3, 4

=================================== 
 Fruit
===================================
 id     |   shape
===================================
  1     |   round
  2     |   round
  3     |   oblong
  4     |   oblong
  5     |   round
===================================


===================================
 Orange
===================================
 id     |   color    | taste
===================================
  1     |   orange   |  sour
  2     |   orange   |  sour
  5     |   orange   |  sour
==开发者_高级运维=================================


===================================
 Apple
===================================
 id     |   density    | weight
===================================
  1     |   hard       |  200
  2     |   hard       |  220
  5     |   hard       |  230
===================================

Issue: How to create entity classes capturing relationshipd also with only JPA annotations (I don't want to use hibernate generatedValue annotation).

If such annotation is possible with pure JPA then please guide me towards it.

Nik


Your case looks like an instance of the design pattern known as “Generalization Specialization” , or Gen-Spec for short. The question of how to model gen-spec using database tables comes up all the time in SO.

If you were modeling gen-spec in an OOPL such as Java, you would use the subclass inheritance facility to take care of the details for you. You would simply define a class to take care of the generalized objects, and then define a collection of subclasses, one for each type of specialized object. Each subclass would extend the generalized class. It’s easy and straightforward.

Unfortunately the relational data model does not have subclass inheritance built in, and the SQL database systems don’t offer any such facility, to my knowledge. But you’re not out of luck. You can design your tables to model gen-spec in a way that parallels the class structure of OOP. You then have to arrange to implement your own inheritance mechanism when new items are added to the generalized class. Details follow.

The class structure is fairly simple, with one table for the gen class and one table for each spec subclass. Here’s a nice illustration, from Martin Fowler’s website. Class Table Inheritance. Note that in this diagram, Cricketer is both a subclass and a superclass. You have to choose which attributes go in which tables. The diagram shows one sample attribute in each table.

The tricky detail is how you define primary keys for these tables. The gen class table gets a primary key in the usual way (unless this table is a specialization of yet another generalization, like Cricketers). Most designers give the primary key a standard name, like “Id”. They use the autonumber feature to populate the Id field. The spec class tables get a primary key, which can be named “Id”, but the autonumber feature is not used. Instead the primary key of each subclass table is constrained to refer to the primary key of the generalized table. This makes each of the specialized primary keys a foreign key as well as a primary key. Note that in the case of Cricketers, the Id field will reference the Id field in Players, but the Id field in Bowlers will reference the Id field in Cricketers.

Now, when you add new items, you have to maintain referential integrity, Here’s how.
You first insert a new row into the gen table, providing data for all of its attributes, except the primary key. The autonumber mechanism generates a unique primary key. Next you insert a new row into the appropriate spec table, including data for all of its attributes, including the primary key. The primary key you use is a copy of the brand new primary key just generated. This propagation of the primary key can be called “poor man’s inheritance”.

Now when you want all the generalized data together with all the specialized data from just one subclass, all you have to do is join the two tables over the common keys. All the data that does not pertain to the subclass in question will drop out of the join. It’s slick, easy, and fast.

The design of SQL tables that implement the gen-spec pattern can be a little tricky. Database design tutorials often gloss over this topic. But it comes up again and again in practice.

If you search the web on “generalization specialization relational modeling” you’ll find several useful articles that teach you how to do this. You’ll also be pointed to several times this topic has come up before in this forum.

The articles generally show you how to design a single table to capture all the generalized data and one specialized table for each subclass that will contain all the data specific to that subclass. The interesting part involves the primary key for the subclass tables. You won’t use the autonumber feature of the DBMS to populate the sub class primary key. Instead, you’ll program the application to propagate the primary key value obtained for the generalized table to the appropriate subclass table.

This creates a two way association between the generalized data and the specialized data. A simple view for each specialized subclass will collect generalized and specialized data together. It’s easy once you get the hang of it, and it performs fairly well.


To be tested by forcing the two Java fields (id and association) to map on the same column.

For instance,

class Orange {

   @Id
   @Column(name="id")
   long id;


   @OneToOne
   @JoinColumn(name="id")
   Fruit fruit;

   public Orange(Fruit fruit) {
      this.fruit = fruit;
      this.id = fruit.id;
   }

   protected Orange() { } // default constructor required by JPA
}

But I don't know how the JPA provider will behave.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号