Mike has posted 2 posts at DZone. View Full User Profile

Looking Forward To JPA 2.0 - Part 2

06.03.2008
| 51059 views |
  • submit to reddit

Embedded Relationships


Sometimes an entity has a relationship to another entity, but the relationship is actually more conveniently stored as part of an embedded object within the source entity. Relationships were not previously allowed to be contained within embeddable objects, but in JPA 2.0 this restriction was lifted.

Going back to our Vehicle example we can add an embedded PurchaseInfo object which contains information like the purchase price, and the date of sale, but also a reference to the dealer that sold the vehicle. Our PurchaseInfo object might look like:

@Embeddable
public class PurchaseInfo {
    int price;

    @Temporal(DATE) 
    @Column(name=”PUR_DATE”)
    Date purchaseDate;

    @ManyToOne
    Dealer dealer;
}

The @ManyToOne relationship that exists in the PurchaseInfo is really just a relationship from Vehicle to Dealer, but it just so happens that the relationship is defined in the PurchaseInfo object that is embedded in Vehicle.

This relationship could be unidirectional, but we could just as easily make it bidirectional by mapping it on the Dealer side. If it were bidirectional then the Dealer would refer to the target entity as Vehicle, and point to the dealer attribute through the purchaseInfo attribute of Vehicle. The relevant Vehicle and Dealer code is below.

@Entity
public class Vehicle {

    @Id int vin;

    @Embedded
    PurchaseInfo purchaseInfo;
    …
}

@Entity
public class Dealer {

    @Id int dealerId;

    @OneToMany(mappedBy=”purchaseInfo.dealer”)
    Collection<Vehicle> vehiclesSold;
    …
}

Maps and more Maps

Maps can currently be used to contain entities in one-to-many and many-to-many relationships, but the keys must be primary key attributes, or otherwise unique attributes of the target entities. It would be nice to be able to store the entities keyed by a basic type, or simply be able to use a Map to store basic typed values.

Maps may now contain any combination of basic types, embeddable objects and entities as keys or values. This offers significant flexibility and opens the door to using almost completely arbitrarily typed maps in the entity model. Because of space limitations I couldn’t possibly show examples of all possible combinations, but I’ll take a couple of configurations and demonstrate how they can be used, and hope you get the basic idea.

In our previous code example our PurchaseInfo object, embedded in Vehicle, contained a many-to-one relationship to Dealer, and Dealer had a one-to-many relationship back to the Vehicle. If we wanted to actually see the purchase info from the Dealer side it would actually make sense to have a Map of sold vehicles keyed by the PurchaseInfo. To achieve this our Dealer entity would look like:

@Entity
public class Dealer {

    @Id int dealerId;

    @OneToMany(mappedBy=”purchaseInfo.dealer”)
    Map<PurchaseInfo,Vehicle> vehiclesSold;

    @ElementCollection
    @CollectionTable(name=”VEH_INV”,
                     joinColumns=@JoinColumn(name=”DLR_ID”))
    @MapKeyJoinColumn(name=”V_ID”)
    @Column(name=”COUNT”)
    Map<Vehicle,Integer> inventory;
    …
}

In a bidirectional one-to-many target foreign key relationship the key of the relationship is stored in the target table. In our case the key is just an embedded object (in the target table) so the mapped values of the embeddable class are applied to obtain the PurchaseInfo object from the VEHICLE table. As a cautionary note, the PurchaseInfo object that is embedded in a Vehicle is not necessarily identical to the key instance in the vehiclesSold Map. These are not first class entities, and thus do not qualify to have their identity uniquely managed.

The second collection is a Map that models the vehicle inventory, keeping track of the number of each type of vehicle in stock. The key is a Vehicle entity and the value is a simple integer, so it is an element collection that is stored in a collection table.

The @MapKeyJoinColumn and @Column annotations designate the columns used to store the key and value for each map entry and apply to the collection table. The @MapKeyJoinColumn annotation is only used when the Map key is an entity and indicates the foreign key column referencing the primary table of that entity. The @Column annotation indicates the column containing the Map value, and in this case we are storing the inventory value in the “COUNT” column. Assuming the above annotations we are specifying a collection table that looks like the following:

Published at DZone with permission of its author, Mike Keith.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Bernhard Messerer replied on Wed, 2008/06/04 - 7:06am

So this is the JPA of the future? Anybody else diturbed by having five times as many annotations as code - it's becoming sort of an embedded language.

Couldn't find it, but when annotations came up, there was a joke about 'annotation programming', a servlet with 20 or so annotations like '@flow("if user is root then show_admin_link else show_user_link end")' ... (un)funny - whenever we discover something is 'bad' (XML-DDs) we swing completely to the opposite direction and find it equally bad (wait a few years, everybody will scream about the lots and lots of 'heavyweight'annotations unchangeable at runtime) instead of finding the middle ground.

 Messi

Geoffrey De Smet replied on Fri, 2008/06/06 - 1:09am

It feels strange to me that the @Column defines the name of a column on another table. It violates the principle of least surprise (at least to me).

I feel like a seperate annotation or property on @CollectionTable would be a lot more consistent:

@ElementCollection
@CollectionTable(name=”VEH_OPTNS”, columnName="FEAT")
Set<FeatureType> optionalFeatures;

 

Great article. Much better than those (java != dead) articles that litter the frontpage these days. How's the work on standarizing the Hibernate Criteria API going? I am really intersted to see how JPA will define that API.

Donny A. Wijaya replied on Sun, 2008/07/06 - 2:36am

Hi

I wonder if the next JPA 2.0 would include @TypeDef like in Hibernate 3? IMHO, it is a very useful feature. It will allow developers to specify custom type to allow JPA to persist the attribute transparently using the custom type (like in Jasypt and Joda-Time)

Donny

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.