Domain models and metadata
3.3 Domain model metadata
3.3.1 Annotation-based metadata
The big advantage of annotations is to put metadata next to the information it describes, instead of separating it physically into a different file. Here’s an example:
import javax.persistence.Entity;
@Entity
public class Item { }
You can find the standard JPA mapping annotations in the javax.persistence pack- age. This example declares the Item class as a persistent entity using the @javax .persistence.Entity annotation. All of its attributes are now automatically persis- tent with a default strategy. That means you can load and store instances of Item, and all properties of the class are part of the managed state.
(If you followed the previous chapter, you probably notice the missing required @Id annotation and identifier property. If you want to try the Item example, you’ll
have to add an identifier property. We’ll discuss identifier properties again in the next chapter, in section 4.2.)
Annotations are type-safe, and the JPA metadata is included in the compiled class files. Hibernate then reads the classes and metadata with Java reflection when the application starts. The IDE can also easily validate and highlight annotations—they’re regular Java types, after all. If you refactor your code, you rename, delete, or move classes and properties all the time. Most development tools and editors can’t refactor XML element and attribute values, but annotations are part of the Java language and are included in all refactoring operations.
When the standardized Java Persistence annotations are insufficient, a JPA provider may offer additional annotations.
USINGVENDOREXTENSIONS
Even if you map most of your application’s model with JPA-compatible annotations from the javax.persistence package, you’ll have to use vendor extensions at some point. For example, some performance-tuning options you’d expect to be available in high-quality persistence software are only available as Hibernate-specific annotations. This is how JPA providers compete, so you can’t avoid annotations from other pack- ages—there’s a reason why you picked Hibernate.
This is the Item entity source code again with a Hibernate-only mapping option:
import javax.persistence.Entity;
@Entity
@org.hibernate.annotations.Cache(
usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE )
public class Item {
}
We prefer to prefix Hibernate annotations with the full org.hibernate.annotations package name. Consider this good practice, because you can easily see what metadata for this class is from the JPA specification and which is vendor-specific. You can also easily search your source code for “org.hibernate.annotations” and get a complete overview of all nonstandard annotations in your application in a single search result.
Is my class now dependent on JPA?
Yes, but it’s a compile-time only dependency. You need JPA libraries on your class- path when compiling the source of your domain model class. The Java Persistence API isn’t required on the classpath when you create an instance of the class: for example, in a desktop client application that doesn’t execute any JPA code. Only when you access the annotations through reflection at runtime (as Hibernate does internally when it reads your metadata) will you need the packages on the classpath.
If you switch your Java Persistence provider, you only have to replace the vendor- specific extensions where you can expect a similar feature set to be available with most mature JPA implementations. Of course, we hope you’ll never have to do this, and it doesn’t happen often in practice—just be prepared.
Annotations on classes only cover metadata that is applicable to that particular class. You often need metadata at a higher level, for an entire package or even the whole application.
GLOBALANNOTATIONMETADATA
The @Entity annotation maps a particular class. JPA and Hibernate also have annota- tions for global metadata. For example, a @NamedQuery has global scope; you don’t apply it to a particular class. Where should you place this annotation?
Although it’s possible to place such global annotations in the source file of a class (any class, really, at the top), we’d rather keep global metadata in a separate file. Pack- age-level annotations are a good choice; they’re in a file called package-info.java in a particular package directory. You can see an example of global named query declara- tions in the following listing.
@org.hibernate.annotations.NamedQueries({ @org.hibernate.annotations.NamedQuery( name = "findItemsOrderByName",
query = "select i from Item i order by i.name asc"
) ,
@org.hibernate.annotations.NamedQuery( name = "findItemBuyNowPriceGreaterThan",
query = "select i from Item i where i.buyNowPrice > :price", timeout = 60,
comment = "Custom SQL comment"
) })
package org.jpwh.model.querying;
Unless you’ve used package-level annotations before, the syntax of this file with the package and import declarations at the bottom is probably new to you.
There is a reason the previous code example only includes annotations from the Hibernate package and no Java Persistence annotations. We ignored the standardized JPA @org.javax.persistence.NamedQuery annotation and used the Hibernate alter- native. The JPA annotations don’t have package applicability—we don’t know why. In fact, JPA doesn’t allow annotations in a package-info.java file. The native Hibernate annotations offer the same, and sometimes even more, functionality, so this shouldn’t be too much of a problem. If you don’t want to use the Hibernate annotations, you’ll have to either put the JPA annotations at the top of any class (you could have an
Listing 3.5 Global metadata in a package-info.java file
PATH: /model/src/main/java/org/jpwh/model/querying/package-info.java
otherwise empty MyNamedQueries class as part of your domain model) or use an XML file, as you’ll see later in this section.
Annotations will be our primary tool throughout this book for ORM metadata, and there is much to learn about this subject. Before we look at some alternative mapping styles with XML files, let’s use some simple annotations to improve the domain model classes with validation rules.