Looking Forward to JPA 2.0
Join the DZone community and get the full member experience.
Join For FreeThe Java Persistence API (JPA), version 1.0, released in May 2006 as part of the Java EE 5 platform, represented a significant leap forward for architects and developers wanting to persist Java state to relational databases using standard metadata and a portable API. While a standard ORM specification was somewhat overdue, when it did finally arrive it brought with it not only the database portability that was inherent in existing ORM products, but also the ability to plug in different vendor implementations.
Although some were satisfied with the proprietary APIs offered by products like JBoss Hibernate and Oracle TopLink, the more experienced and forward-thinking application developers moved over to using JPA. The only problem was that JPA 1.0 did not have all of the features that were found in the proprietary APIs.
The solution, however, was far from rocket science, and involved simply using JPA for the features that were needed and available there, and reverting to the proprietary API for the few features that were missing from the standard. The result was that changing the underlying implementation only required porting a small amount of code coupled to the existing implementation. But it was not ideal. The JPA 1.0 expert group had only a limited period of time in which to start from nothing and end up with an entire persistence standard. As a useful and achievable goal, it endeavored to include 80-90% of the features supported by the existing ORM frameworks. The rest would have to be added in subsquent releases.
The JSR 217 expert group is currently engaged in the next phase of the JPA progression. It will deliver, to be released as part of Java EE 6, version 2.0 of JPA. It will also include most of the features that people missed and asked for in JPA 1.0, such as increased modeling flexibility, additional object-relational mapping capability, more locking options and a runtime Java API-based query language. In this multi-part series we will discuss some of these new features, how they are being added to the specification and how users will be able to make use of them.
Disclaimer
Although many of these features have been discussed, and are close to being concluded, some have not yet been, and are yet to be discussed within the group. Even those topics for which discussion has subsided are not set in stone, and may yet change. So while this series will give you a taste of what’s coming, it offers no guarantees that the features it describes are in their final released form, or even that they will necessarily be included at all!
On the other side, neither should the features described in these articles be assumed to be a complete list of the new feature set. Group members regularly make suggestions for new features that were not on the agenda but are nonetheless of enough value to expend the effort to specify.
Additional Modeling Abilities
In this first article we will illustrate some of the ways that JPA 2.0 will increase the flexibility that you have as a Java developer to model your objects. This includes lifting some of the constraints that were placed upon the model in version 1.0, as well as providing access to additional features and creating a more natural and self-documenting domain model.
Flexible Access Modes
Sometimes the noblest of causes will still produce casualities (no political commentary intended!), but this was borne out by the attempts to make JPA very default-driven. The cost of trying to default the way that JPA implementers (commonly referred to as persistence providers, or simply providers) access the state of an entity is that it became very difficult to actually have an exception from the default without stumbling into complexity. The result was that there was no way to mix accessing the state through the instance variables or through property methods within the same entity hierarchy, or even across an owning entity and the objects it may have embedded.
A solution has been found, however, to allow an entity in the midst of a hierarchy to declare that its state be accessed differently from the other classes in the hierarchy. The access mode for a class can be set by placing an @Access annotation on it, causing the default access mode in effect for the hierarchy to be locally overridden. The value attribute of the @Access annotation specifies either FIELD or PROPERTY to determine whether field-based or property-based access is to be undertaken by the provider.
This annotation will be useful for more than just the case of using a different access mode for an entity in a hierarchy. It will also be useful to solve the problem mentioned above, where an entity has an embedded object and the embeddable happens to have been mapped using a different access type than the owning entity. This is particularly helpful if the embeddable type is embedded by multiple entity types with different access types. The current specification all but rules this out.
Another case when this feature will be useful is when you need to perform some kind of simple transformation to the data when reading from or writing to the database. In general you will want the provider to access the data using FIELD access, but in this case you will need to use a get/set method pair to perform the transformation.
@Entity
@Access(FIELD)
public class Customer {
public static final LOCAL_AREA_CODE = “613”;
@Id private int id;
@Transient private String pNum;
…
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getPhoneNumber() { return pNum; }
public void setPhoneNumber(String num) { this.pNum = num; }
@Access(PROPERTY) @Column(name=”PHONE”)
protected String getPhoneNumberForDb() {
if (pNum.length() == 10)
return pNum;
else
return LOCAL_AREA_CODE + pNum;
}
protected void setPhoneNumberForDb(String num) {
if (num.startsWith(LOCAL_AREA_CODE))
pNum = num.subString(3);
else
pNum = num;
}
…
}
From this example we can see that there are essentially three steps to making a single attribute be accessed via its property instead of through the field:
- We put the @Access(FIELD) annotation on the class to state what the default is for the class and to enable the recognition of the property-level annotation.
- We place the @Access(PROPERTY) annotation on the property that we want the provider to access the state through.
- Finally, we mark the corresponding field as @Transient so that the default access mode does not try to map it in addition to the property, and thereby map the same state twice.
As I explained above, there are also other usages for this annotation. For example, marking an embeddable to be accessed a specific way so that it will be properly mapped regardless of the entity type that owns it. This would be accomplished by annotating the embeddable with the @Access mode appropriate to the mappings within the object.
Derived Identifiers
In some cases, specifically in legacy databases, the data model dictates that the primary key of one table includes a foreign key to another. The figure below shows what this data model would look like diagramatically if a project had as its primary key both the name of the project as well as a foreign key to the department.
[img_assist|nid=2463|title=|desc=|link=none|align=undefined|width=329|height=127]
In object terms, this means that the identifier of an entity includes one (or possibly more than one) of its ManyToOne or OneToOne relationship attributes.
The current way to deal with this modeling scenario is to add, for each of the relationships in the identifier, an additional integer @Id attribute to the entity to represent the foreign key of the related entity. The new object attribute is completely extraneous and is redundantly mapped to the same join column as the relationship. Additionally, one of the two mappings must be read-only to avoid both of them trying to insert into the same column at create-time.
It would be better if the relationship, itself, could be designated to be part of the identifier, avoiding the redundancy of a superfluous foreign key attribute. In JPA 2.0 this is the way such an entity would be modeled, by putting an @Id annotation (or the equivalent element in the XML mapping descriptor) on the relationship attribute.
The primary key class would still be required in the usual cases where the id was composed of both entity state plus a relationship foreign key, or if it was composed of multiple foreign keys. The primary key class would include the foreign key attribute(s), of course, because the value is needed to retrieve an entity. The naming of the primary key class attribute is also consistent with existing rules, whereby the attribute in the PK class must match the corresponding one in the entity, but the type of the attribute in the PK class would be integral instead of the target relationship entity type. A straightforward example actually illustrates this much better than a world of words.
@Entity
@IdClass(ProjectId.class)
public class Project{
@Id String name;
@Id @ManyToOne @JoinColumn(name=”D_ID”)
Department dept;
…
}
public class ProjectId {
String name;
int dept;
…
}
Orphan Removal
Aggregation of entities, or parent-child relationships, is not all that uncommon in models. Although it was possible to manually model this scenario, unfortunately there was no specific support for it in JPA 1.0. New orphanRemoval attributes are now being added to @OneToOne and @OneToMany relationship annotations to mark the relationship as a parent-child relationship to make it easier to implement and more explicit to model.
@Entity
public class Department {
@Id int id;
@OneToMany(mappedBy=”project”, orphanRemoval=true)
Collection<Project> projects;
…
}
If, like in the example above, the orphanRemoval attribute is set to true then if a department is shut down then its projects will also get removed. In general, two behaviors will apply when orphan removal is enabled:
First, the cascade REMOVE option will automatically apply to the relationship, regardless of whether or not it was specified in the set of cascaded operations. If the parent is removed then the child will also be removed.
Second, if the relationship from the parent to the child is severed, meaning that the pointer from the parent to the child is nulled out, the child will be automatically removed. For example, setting a parent-child collection to null will cause all of the entities that were in the collection to be removed from the database when the transaction commits.
A final note about orphan removal is that the children must be treated as privately owned by the parent. In other words, a child must never be owned by more than one parent (precisely why it is only supported by OneToX relationships) and if a child is dis-owned by its parent it cannot be reassigned to a new parent. A new instance must be created in the context of its new parent.
Persistently Ordered Lists
There currently exists two common ways in JPA to manage collection-valued relationships that are ordered. The first is to maintain the order by ensuring that the collection of entities is loaded in the expected order using @OrderBy, and possibly performing ordering in memory to maintain the order if and when necessary.
The second is to add a “position” attribute to the target entity being stored in the collection and map it to a column in the target table. This attribute represents the position of the entity in the List and must be kept current and in sync with the actual index position of the entity in the collection. Albeit a little onerous, requiring that the column be explicitly mapped and visible in the domain model was kind of in harmony with the data model. However, there are those who do not want the position to percolate into the domain model, so JPA 2.0 will offer a more convenient solution.
Version 2.0 will introduce a new annotation called @OrderColumn, which defines the column to use as the collating column. The provider will take care of writing out the correct position column for each of the entities in the collection, as well as sorting the query result collection when reading them in.
A note of warning is in order here, though. This kind of modeling is not typically going to be the best approach, and it can be quite expensive in terms of updates since simply removing the first element of a large collection will cause an update to every other entity in the collection, or every relationship entry in the case of a ManyToMany relationship. Sometimes people insist on maintaining the order in a non-attribute column, however, so this feature will be a boon to them.
An example of using @OrderColumn is shown below:
@Entity
public class Department {
@Id int id;
@ManyToMany
@JoinTable(name=”DEPT_SUPP”)
@OrderColumn(name=”PREF_ORD”)
List<Supplier> preferredSuppliers;
…
}
The relationship is a ManyToMany, so the order column that is listed will apply to the join table, DEPT_SUPP, that stores the relationship. If it were a OneToMany relationship then the order column would be in the target entity table (the supplier table in this case).
Summary
In this installment you got a taste of some of the JPA features that will improve your ability to model your domain objects, including being able to mix entity access modes, elegantly create identifiers derived from relationships, define privately owned parent-child relationships and use lists that persistently store their order. The good news is that this was just a warm-up, and there is much more to come!
In the next installment we will forge ahead to look at the newly added ORM mappings and functionality. See you again at the same bat-time, same bat-place.
Opinions expressed by DZone contributors are their own.
Comments