How to Use Annotations to Define Different Types of Relationships in Hibernate 4 and Spring

How do I use annotations to define different types of relationships in Hibernate 4 and Spring?

Creating Relationships with Annotations

Assume all classes annotated with @Entity and @Table

Uni-directional One to One Relationship

public class Foo{
private UUID fooId;

@OneToOne
private Bar bar;
}

public class Bar{
private UUID barId;
//No corresponding mapping to Foo.class
}

Bi-Directional One to One Relationship managed by Foo.class

public class Foo{
private UUID fooId;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "barId")
private Bar bar;
}

public class Bar{
private UUID barId;

@OneToOne(mappedBy = "bar")
private Foo foo;
}

Uni-Directional One to Many Relationship using user managed join table

public class Foo{
private UUID fooId;

@OneToMany
@JoinTable(name="FOO_BAR",
joinColumns = @JoinColumn(name="fooId"),
inverseJoinColumns = @JoinColumn(name="barId"))
private List<Bar> bars;
}

public class Bar{
private UUID barId;

//No Mapping specified here.
}

@Entity
@Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;

@ManyToOne
@JoinColumn(name = "fooId")
private Foo foo;

@ManyToOne
@JoinColumn(name = "barId")
private Bar bar;

//You can store other objects/fields on this table here.
}

Very commonly used with Spring Security when setting up a User object who has a list of Role's that they can perform. You can add and remove roles to a user without having to worry about cascades deleting Role's.

Bi-directional One to Many Relationship using foreign key mapping

public class Foo{
private UUID fooId;

@OneToMany(mappedBy = "bar")
private List<Bar> bars;
}

public class Bar{
private UUID barId;

@ManyToOne
@JoinColumn(name = "fooId")
private Foo foo;
}

Bi-Directional Many to Many using Hibernate managed join table

public class Foo{
private UUID fooId;

@OneToMany
@JoinTable(name="FOO_BAR",
joinColumns = @JoinColumn(name="fooId"),
inverseJoinColumns = @JoinColumn(name="barId"))
private List<Bar> bars;
}

public class Bar{
private UUID barId;

@OneToMany
@JoinTable(name="FOO_BAR",
joinColumns = @JoinColumn(name="barId"),
inverseJoinColumns = @JoinColumn(name="fooId"))
private List<Foo> foos;
}

Bi-Directional Many to Many using user managed join table object

Commonly used when you want to store extra information on the join object such as the date the relationship was created.

public class Foo{
private UUID fooId;

@OneToMany(mappedBy = "bar")
private List<FooBar> bars;
}

public class Bar{
private UUID barId;

@OneToMany(mappedBy = "foo")
private List<FooBar> foos;
}

@Entity
@Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;

@ManyToOne
@JoinColumn(name = "fooId")
private Foo foo;

@ManyToOne
@JoinColumn(name = "barId")
private Bar bar;

//You can store other objects/fields on this table here.
}

Determining which side of the bi-directional relationship 'owns' the relationship:

This is one of the trickier aspects of working out Hibernate relationships because Hibernate will operate correctly no matter which way to set up the relationship. The only thing that will change is which table the foreign key is stored on. Generally the object that you have a collection of will own the relationship.

Example: A User object has a list of Roles declared on it. In most applications, the system will be manipulating instances of the User object more often than instances of the Roles object. Hence I would make the Role object the owning side of the relationship and manipulate the Role objects through the list of Role's on a User by cascade. For a practical example see the bi-directional One to Many example. Typically you will cascade all changes in this scenario unless you have a specific requirement to do otherwise.

Determining your fetchType

Lazily fetched collections have resulted in more issues on SO than I care to look at because by default Hibernate will load related objects lazily. It doesn't matter if the relationship is a one-to-one or many-to-many as per the Hibernate docs:

By default, Hibernate uses lazy select fetching for collections and lazy proxy fetching for single-valued associations. These defaults make sense for most associations in the majority of applications.

Consider this my two cents on when to use fetchType.LAZY vs fetchType.EAGER on your objects. If you know that 50% of the time you won't need to access the collection on your parent object, I'd be using fetchType.LAZY.

The performance benefits are of this are huge and only grow as you add more objects to your collection. This is because for an eagerly loaded collection, Hibernate does a ton of behind the scenes checking to ensure that none of your data is out of date. While I do advocate using Hibernate for collections, be aware that there is a performance penalty** for using fetchType.EAGER. However, take our Person object example. Its fairly likely that when we load a Person we will want to know what Roles they perform. I will usually mark this collection as fetchType.EAGER. DON'T REFLEXIVELY MARK YOUR COLLECTION AS fetchType.EAGER SIMPLY TO GET AROUND A LazyInitializationException. Not only is it bad for performance reasons, it generally indicates that you have a design issue. Ask yourself, should this collection actually be an eagerly loaded collection, or am I doing this just to access the collection in this one method. Hibernate has ways around this, that doesn't impact the performance of your operations quite as much. You can use the following code in your Service layer if you want to initialize a lazily loaded collection just for this one call.

//Service Class
@Override
@Transactional
public Person getPersonWithRoles(UUID personId){
Person person = personDAO.find(personId);

Hibernate.initialize(person.getRoles());

return person;
}

The call to Hibernate.initialize forces the creation and loading of the collection object. However, be careful, if you only pass it the Person instance, you will get a proxy of your Person back. See the documentation for more information. The only downside to this method is that you have no control over how Hibernate will actually fetch your collection of objects. If you want to control this, then you can do so in your DAO.

//DAO
@Override
public Person findPersonWithRoles(UUID personId){
Criteria criteria = sessionFactory.getCurrentSession().createCritiera(Person.class);

criteria.add(Restrictions.idEq(personId);
criteria.setFetchMode("roles", FetchMode.SUBSELECT);
}

The performance here depends on what FetchMode you specify. I've read answers that say to use FetchMode.SUBSELECT for performance reasons. The linked answer goes into more detail if you are really interested.

If you want to read me as I repeat myself, feel free to check out my other answer here

Determining Cascade Direction

Hibernate can cascade operations either or both ways in a bi-directional relationship. So if you have a List of Role's on a User you can cascade changes to Role's in both directions. If you change the name of a particular Role on a User Hibernate can automatically update the associated Role on the Role Table.

However this is not always desired behaviour. If you think about it, in this case, making changes to Role's based on changes to User doesn't make any sense. However it makes sense going in the opposite direction. Change a Role's name on the Role object itself, and that change can be cascaded to all User objects that have that Role on it.

In terms of efficiency, it makes sense to create/update Role objects by saving the User object that they belong to. This means you would mark your @OneToMany annotation as the cascading one. I'll give an example:

public User saveOrUpdate(User user){
getCurrentSession.saveOrUpdate(user);
return user;
}

In the above example, Hibernate will generate a INSERT query for the User object, and then cascade the creation of the Role's once the User has been inserted into the database. These insert statements will then be able to use the PK of the User as their foreign key, so you would end up with N + 1 insert statements, where N is the number of Role objects in the list of users.

Conversely if you wanted to save the individual Role objects cascading back to the User object, could be done:

//Assume that user has no roles in the list, but has been saved to the
//database at a cost of 1 insert.
public void saveOrUpdateRoles(User user, List<Roles> listOfRoles){
for(Role role : listOfRoles){
role.setUser(user);
getCurrentSession.saveOrUpdate(role);
}
}

This results in N + 1 inserts where N is the number of Role's in the listOfRoles, but also N update statements being generated as Hibernate cascades the addition of each Role to the User table. This DAO method has a higher time complexity than our previous method, O(n) as opposed to O(1) because you have to iterate through the list of roles. Avoid this if at all possible.

In practice however, usually the owning side of the relationship will be where you mark your cascades, and you will usually cascade everything.

Orphan Removal

Hibernate can work out for you if you remove all associations to an object. Suppose you have a User who has a list of Role's and in this list are links to 5 different roles. Lets say you remove a Role called ROLE_EXAMPLE and it happens that the ROLE_EXAMPLE doesn't exist on any other User object. If you have orphanRemoval = true set on the @OneToMany annotation, Hibernate will delete the now 'orphaned' Role object from the database by cascade.

Orphan removal should not be enabled in every case. In fact, having orphanRemoval in our example above makes no sense. Just because no User can perform whatever action the ROLE_EXAMPLE object is representing, that doesn't mean that any future User will never be able to perform the action.

This Q&A is intended to complement the official Hibernate documentation, which has a large amount of XML configuration for these relationships.

These examples are not meant to be copy-pasted into production code. They are generic examples of how to create and manage various objects and their relationships using JPA annotations to configure Hibernate 4 within the Spring Framework. The examples assume that all classes have ID fields declared in the following format: fooId. The type of this ID field is not relevant.

** We recently had to abandon using Hibernate for an insert job where we were inserting <80,000+ objects into the database through a collection. Hibernate ate up all the heap memory doing checking on the collection and crashed the system.

DISCLAIMER:
I DO NOT KNOW IF THESE EXAMPLES WILL WORK WITH STANDALONE HIBERNATE

I am not in any way affiliated with Hibernate or the Hibernate dev team. I'm providing these examples so I have a reference to point to when I'm answering questions on the Hibernate tag. These examples and discussions are my based on my own opinion as well as how I develop my applications using Hibernate. These examples are in no way comprehensive. I'm basing them on the common situations I've used Hibernate for in the past.

If you encounter issues trying to implement these examples, do not comment and expect me to fix your problem. Part of learning Hibernate is learning the in's and out
's of its API. If there is a mistake with the examples, please feel free to edit them.

Minimal Hibernate 4 XML configuration with Spring 3 for annotation based transaction management and object mapping?

I find using Hibernate with Spring's XML config fairly intuitive, but if you've never added it to a project before, it can be a pain to get working correctly. Using Spring's XML config is my preferred option for Hibernate 4.

So you've set up a Spring project and got everything working. You now want to add Hibernate.

I always like to configure Hibernate in a separate XML file called something like database-servlet.xml inside your WEB-INF directory, but the name doesn't really matter as long as its on the classpath.

My new database-servlet.xml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">

</beans>

You'll notice I imported the tx and jdbc Spring namespaces. This is because we are going to use them quite heavily in this config file.

First thing you want to do is enable annotation based transaction management (@Transactional). The main reason that people use Hibernate in Spring is because Spring will manage all your transactions for you. Add the following line to your configuration file:

<tx:annotation-driven />

We need to create a data source. The data source is basically the database that Hibernate is going to use to persist your objects. Generally one transaction manager will have one data source. If you want Hibernate to talk to multiple data sources then you have multiple transaction managers.

The type of data source will depend on what you want it to accomplish. You can specify an existing database, or you can create a new in-memory HSQL/Derby/H2 database that comes prepackaged with Spring. Personally I have an existing database that Hibernate connects to when I deploy my project for physical testing, but I use an in-memory database for unit/integration testing.

I'll show how to create an in-memory database first.

<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:/setup.sql" />
.
.
.
<!-- As many scripts can run as you like -->
</jdbc:embedded-database>

The above config, will create an embedded (in-memory) HSQL database as a bean, run the script setup.sql and then make the dataSource bean available to the application context. You don't have to specify the database type as HSQL is the default, but I always like to be clear. The setup.sql can be located anywhere within the classpath (WEB-INF directory usually). You can specify as many SQL scripts as you like. You can also set if they should be run on creation or destruction of the database.

This database will live and die with your application. DO NOT USE AN EMBEDDED DATABASE ON A PRODUCTION PROJECT, one power outage, and all your data is gone.

To connect a data source to an existing database the configuration is slightly different.

<bean id="dataSource" 
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value="" />
</bean>

The class of this bean can be anything that implements (I think) javax.sql.DataSource so you could write your own. This example class is provided by Spring, but doesn't have its own thread pool. A popular alternative is the Apache Commons org.apache.commons.dbcp.BasicDataSource, but there are many others. I'll explain each of the properties below:

  • driverClassName: The path to your JDBC driver. This is a database specific JAR that should be available on your classpath. Ensure that you have the most up to date version. If you are using an Oracle database, you'll need a OracleDriver. If you have a MySQL database, you'll need a MySQLDriver. See if you can find the driver you need here but a quick google should give you the correct driver.

  • url: The URL to your database. Usually this will be something like jdbc\:oracle\:thin\:\path\to\your\database or jdbc:mysql://path/to/your/database. If you google around for the default location of the database you are using, you should be able to find out what this should be. If you are getting a HibernateException with the message org.hibernate.HibernateException: Connection cannot be null when 'hibernate.dialect' not set and you are following this guide, there is a 90% chance that your URL is wrong, a 5% chance that your database isn't started and a 5% chance that your username/password is wrong.

  • username: The username to use when authenticating with the database.

  • password: The password to use when authenticating with the database.

The next thing, is to set up the SessionFactory. This is the thing that Hibernate uses to create and manage your transactions, and actually talks to the database. It has quite a few configuration options that I will try to explain below.

<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="au.com.project />
<property name="hibernateProperties">
<props>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
</props>
</property>
</bean>
  • dataSource: Your data source bean. If you changed the Id of the dataSource, set it here.

  • packagesToScan: The packages to scan to find your JPA annotated objects. These are the objects that the session factory needs to manage, will generally be POJO's and annotated with @Entity. For more information on how to set up object relationships in Hibernate see here.

  • annotatedClasses (not shown): You can also provide a list of classes for Hibernate to scan if they are not all in the same package. You should use either packagesToScan or annotatedClasses but not both. The declaration looks like this:

<property name="annotatedClasses">
<list>
<value>foo.bar.package.model.Person</value>
<value>foo.bar.package.model.Thing</value>
</list>
</property>
  • hibernateProperties: There are a myriad of these all lovingly documented here. The main ones you will be using are as follows:

    • hibernate.hbm2ddl.auto: One of the hottest Hibernate questions details this property. See it for more info. I generally use validate, and set up my database using either SQL scripts (for an in-memory), or create the database beforehand (existing database).
    • hibernate.show_sql: Boolean flag, if true Hibernate will print all the SQL it generates to stdout. You can also configure your logger to show you the values that are being bound to the queries by setting log4j.logger.org.hibernate.type=TRACE log4j.logger.org.hibernate.SQL=DEBUG in your log manager (I use log4j).
    • hibernate.format_sql: Boolean flag, will cause Hibernate to pretty print your SQL to stdout.
    • hibernate.dialect (Not shown, for good reason): A lot of old tutorials out there show you how to set the Hibernate dialect that it will use to communicate to your database. Hibernate can auto-detect which dialect to use based on the JDBC driver that you are using. Since there are about 3 different Oracle dialects and 5 different MySQL dialects, I'd leave this decision up to Hibernate. For a full list of dialects Hibernate supports see here.

The last 2 beans you need to declare are:

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"
id="PersistenceExceptionTranslator" />

<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

The PersistenceExceptionTranslator translates database specific HibernateException or SQLExceptions into Spring exceptions that can be understood by the application context.

The TransactionManager bean is what controls the transactions as well as roll-backs.

Note: You should be autowiring your SessionFactory bean into your DAO's.

Once you've done this. All you have to do is add your new database-servlet.xml to your web.xml file.

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/database-servlet.xml
.
.
.
</param-value>
</context-param>

This should be all you need to actually get Hibernate working. You will still need to add annotations to your objects, and add @Transactional to your service layer methods that interact with the DAO.

Handy hint: If you are using a dependency manager like Ivy or maven, and you pull in all the Spring & Hibernate javadocs, you can actually view them in the STS XML editor by hovering over properties.

How all this works in practice

In your service class, when you annotate a method with @Transactional and then call that method from elsewhere a few things happen. The Hibernate TransactionManager uses an AOP pointcut to inject code before the method gets invoked. This is where the TransactionManager will do the following things (in no particular order):

  • Attempts to ascertain what persistent objects (that it knows about) are in memory.

  • Check the SessionFactory for an existing transactional session, and use the SessionFactory to create a new one if one doesn't exist, depending on your parameters inside the annotation.

From this point the transaction manager records all the changes that you make to any persistent objects that it discovered, as well as any queries run through the current session. It does this so that if an exception is thrown, its a simple matter to roll back everything that has occurred since the method was invoked.

The SessionFactory bean is responsible for creating, maintaining, closing and flushing all the database sessions that the TransactionManager asks it to create. That's why we autowire the SessionFactory into DAO's and make run all queries through it.

One of the biggest questions that new Hibernate users ask is "When do my changes get committed?" and the answer makes sense when you think how the TransactionManager works with the SesisonFactory. Your database changes will be flushed and committed when you exit the service method that was annotated with @Transactional. The reason for this is, that a transaction is supposed to represent a single 'unit' of unbroken work. If something goes wrong with the unit, then it is assumed that the unit failed and all changes should be rolled back. So the SessionFactory will flush and clear the session when you exit the service method that you called originally.

That's not to say that it won't also flush and clear the session while your transaction is going on. For example, if I call a service method to add a collection of 5 objects and return the total count of objects in the database, the SessionFactory would realise that the query (SELECT COUNT(*)) requires an updated state to be accurate, and so would flush the addition of the 5 objects before running the count query. The execution could look something like this:

//Service
@Override
@Transactional
public long saveAndCount(List<Foo> listOfFoo){
for(Foo foo : listOfFoo){
//Doesn't get instantly saved to the database.
fooDAO.saveOrUpdate(foo);
}
/*
* Before the actual SELECT COUNT(*) query was run, the SessionFactory would
* flush the save operation of the 5 Foo objects.
*/
return fooDAO.count();
}

To be clear, the DAO would have no session management code at all. It would have something along the lines of sessionFactory.getCurrentSession().buildCriteria(Foo.class); and that's it. No manipulating instances of a Session object, no calls to flush() or clear(). That's the beauty of using Hibernate with Spring.

DISCLAIMER:
I DO NOT KNOW IF THESE EXAMPLES WILL WORK WITH STANDALONE HIBERNATE

I am not in any way affiliated with Hibernate or the Hibernate dev team. I'm providing these examples so I have a reference to point to when I'm answering questions on the Hibernate tag. These examples and discussions are my based on my own opinion as well as how I develop my applications using Hibernate. These examples are in no way comprehensive. I'm basing them on the common situations I've used Hibernate for in the past.

If you encounter issues trying to implement these examples, do not comment and expect me to fix your problem. Part of learning Hibernate is learning the in's and out
's of its API. If there is a mistake with the examples, please feel free to edit them.

Spring Data JPA Relationship Annotations

For the situation you mentioned in your question, your entities should be like:

@Entity
public class Product {

@Id
@GeneratedValue
private Long id;

private String name;

@ManyToOne
private ProductType type;

// Getters and setters
}
@Entity
public class ProductType {

@Id
@GeneratedValue
private Long id;

private String name;

@OneToMany(mappedBy = "type")
private List<Product> products;

// Getters and setters
}

Hibernate creating relation on @JoinTable

Bi-Directional Many to Many using user managed join table object (Relatively common)

Commonly used when you want to store extra information on the join object such as the date the relationship was created.

public class Foo{
private UUID fooId;

@OneToMany(mappedBy = "bar", cascade=CascadeType.ALL)
private List<FooBar> bars;
}

public class Bar{
private UUID barId;

@OneToMany(mappedBy = "foo", cascade=CascadeType.ALL)
private List<FooBar> foos;
}

@Entity
@Table(name="FOO_BAR")
public class FooBar{
private UUID fooBarId;

@ManyToOne
@JoinColumn(name = "fooId")
private Foo foo;

@ManyToOne
@JoinColumn(name = "barId")
private Bar bar;

//You can store other objects/fields on this table here.
}

Spring-Hibernate Many-to-Many : Save an entity, or create a relationship if it exists in the database

I found a solution

    @Cascade({
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.MERGE,
org.hibernate.annotations.CascadeType.PERSIST
})
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "server_users",
joinColumns = @JoinColumn(name = "server_sn"),
inverseJoinColumns = @JoinColumn(name = "user_sn")
)
private Set<User> users = new HashSet<>();

I added CascadeTypes SAVE_UPDATE, MERGE and PERSIST to Server and User code

In addition, I redesigned the architecture of the base, making snowflakes as Primary Keys, because values are always unique



Related Topics



Leave a reply



Submit