If you’re using Hibernate for several years, you might remember the time when you had to define all your mappings in the orm.xml file. You had a bunch of Java classes that represented your domain model and a long XML file with the mapping and query information. One of the main challenges with that approach was that you had the code and the mapping information in two different files which you had to keep in sync.
All that started to change with the introduction of annotations in Java 5. JPA and Hibernate offered annotations to define the entity mappings and queries. The mapping definitions and queries became part of the entities. That puts all information into one place and makes it easier to understand. You also don’t have to keep multiple files in sync (which I think is an incredible benefit…).
2 valid options
OK, so now we have 2 options to define mappings and queries. And before you ask, I think that both of them are ok. I personally prefer annotations over XML. But there are enough projects out there which show that XML mappings are still a valid option. And splitting your domain model classes and mapping definitions can provide its own benefits. If you need to, you can even store the mapping definitions in a configuration file outside of the deployed jar file.
So, let’s take a more detailed look at both approaches. And after that, I want to show you how you can use both methods in the same project and store the XML configuration in an external file on the classpath.
Let’s begin with the mapping definitions. As I said, you can either define them via annotations or in an XML file.
Basic entity mapping
The following code snippet shows the simplest, annotation based mapping. You just add an @Entity annotation to the class and an @Id annotation to the primary key attribute. Hibernate maps the entity to a database table with the same name and uses a default mapping for each attribute.
You can define the same configuration with the following XML file.
Customized entity mapping
You can adapt this default mapping with a set of annotations. The following example tells Hibernate to map the Author entity to the author table in the bookstore schema, to map the id attribute to the author_id column and to use the sequence author_seq to generate its primary key value.
And as in the previous example, you can, of course, define the same mapping in XML.
You can also define named queries, result set mappings, entity graphs, etc. via annotations and in XML.
You can define a named JPQL query with a @NamedQuery and a named native SQL query with a @NamedNativeQuery annotation. Both of them follow the same idea. You define the query once and reference the definition by its name to instantiate it in your business code.
The following shows an example of a named JPQL query. You can learn more about native queries in my free ebook.
And you can define the same query in your orm.xml file.
Result set mappings
Native SQL queries allow you to use all SQL features supported by your database but they return an Object or a List<Object> instead of the mapped objects you get from a JPQL query. You can either map these results programmatically or define a result set mapping and let Hibernate do the work. I will only show a quick example of such a mapping in this post. If you want to dive deeper, you should take a look at my Result Set Mapping series. The following code snippet shows a simple @SqlResultSetMapping definition that maps the columns authorId, firstName, lastName and version of the native query result to the attributes of the Author entity.If you don’t want to define this with a set of annotations, you can also do this with XML configuration.
Entity Graphs define a graph of entities which Hibernate fetches within 1 query. This is a good approach to avoid n+1 select issues and improve the performance of your application. The following code snippet shows the annotation based definition of a named entity graph which tells Hibernate to fetch the Order entity together with all associated entities mapped by the items attribute.
As you can see in the following code snippet, you can do the same with an XML configuration.
Annotations and XML in the same project
As you’ve seen, you can define your mappings and queries via annotations or XML. But what happens, when you use XML and annotations in the same project?
Just to be clear, I don’t recommend this approach. In general, you should only use one of them to keep your code readable and avoid any confusion.
But it’s supported by the JPA specification, and there are some situations in which it can be useful, like during the transition from an XML based to an annotation based mapping definition or to override the mapping of a shared component.
When you use both approaches within the same project, the mapping definitions defined by the annotations and in the orm.xml are used together. The XML configuration overrides the annotations if you define the same mapping via annotations and in the orm.xml file.
External XML configuration
By default, JPA and Hibernate check if an orm.xml file exists in the META-INF directory and load the mapping definitions from there. But if you want to use the XML configuration to override the entity mappings of a shared component, you might also want to store the XML configuration in an external file. You can do that by referencing the mapping configuration in the mapping-file
attribute in the persistence.xml file.
The following example loads the mapping definitions from the myMappings.xml file.
As you’ve seen, JPA and Hibernate support annotation based and XML based mapping definitions.
By default, JPA and Hibernate load the XML based mapping definitions from the orm.xml file in the META-INF directory. You can change the name and path of that file with the mapping-file attribute in the persistence.xml file.
You can use both of them within the same project. If you do that, the XML mapping overrides the annotations.