Enterprise Java

JPA One-to-One Example

1. Introduction

In this article we will explore the JPA @OneToOne association in a SQL and NoSQL fashion. A @OneToOne association occurs when there is exactly one record in a table that corresponds to exactly one record in a related table. In this case, both tables will contain the same number of records and each row of the first table is linked to another row in the second table. Moreover, in bidirectional associations, the mappedBy element can be used on the non-owning side of a @OneToOne annotation to specify the association field or property of the owning side. Alternatively, @OneToOne can be decorated with lazy loading, cascading or orphan removal.

For developing the applications presented in this article we used NetBeans IDE 8.1, Payara 4.1.1.154 (Full Java EE) application server, Apache Derby Server 10.11.1.2 (that comes bundled with Payara), and MongoDB 3.0.7. You will also need a JDK environment, 1.7 or 1.8.

2. Problem and use-case

Let’s suppose we have the following two tables: Players and Websites. To illustrate the @OneToOne unidirectional association, each player entity corresponds to exactly one website entity. Going further, adding the mappedBy element by modifying the Websites entity will result in transforming the @OneToOne unidirectional association into a bidirectional one. The two relationships are illustrated below:

  • one-to-one unidirectional association

SQL one-to-one unidirectional association
SQL one-to-one unidirectional association

  • one-to-one bidirectional association

SQL one-to-one bidirectional association
SQL one-to-one bidirectional association

3. @OneToOne in a SQL database

3.1 Introduction

In this section, we have developed an EAR application, called OneToOne_EclipseLink_and_ApacheDerby, which aims to illustrate the use-case presented in the previous section. The application contains two modules, an EJB module in which we will develop our EJB beans and entities and a WAR module needed to simply display our data in a web page. To create the application, we used NetBeans IDE 8.1 and Payara 4.1 as the application server. We also used Apache Derby, which comes bundled with Payara, as the database layer. You can change to use GlassFish application server instead of Payara.

You can download the complete application from here. Now, let’s focus on the relevant parts!

3.2 Creating the @OneToOne relationship

Inside the EJB module, in the eclipselink.apachederby.entity package, we have two entities. The first one is Players, which looks like below:

package eclipselink.apachederby.entity;

// Imports

@Entity
@Table(name = "atp_players")
public class Players implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "player_name")
    private String name;

    @Column(name = "player_surname")
    private String surname;

    @Column(name = "player_age")
    private int age;

    @Temporal(javax.persistence.TemporalType.DATE)
    @Column(name = "player_birth")
    private Date birth;

    @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
    @JoinColumn(name = "website_fk")
    private Websites website;

    // Getters and setters
}

We have highlighted the @OneToOne relationship which in simple words says that:

  • the Players entity is the owner entity of our bidirectional one-to-one relationship
  • the annotation @JoinColumn indicates that this entity is the owner of our one-to-one relationship and the corresponding table has a column named website_fk with a foreign key to the referenced table
  • CascadeType.PERSIST means that save() or persist() operations cascade to related entities
  • CascadeType.REMOVE means it will remove all related entities association when the owning entity is deleted

The second entity we see is called Websites and it looks like below:

package eclipselink.apachederby.entity;

// Imports

@Entity
@Table(name = "players_websites")
public class Websites implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private String http_address;

    @OneToOne(mappedBy = "website")
    private Players player_website;

    // Getters and setters
}

We have highlighted the @OneToOne relationship which in simple words says that:

  • the entity class that is the target of the association is the Players entity
  • the field that owns the relationship is called website, and we saw above that it is a field in the Players entity

3.3 Configuring the database connection

Our next step is the persistence.xml file, which contains several configurations specific to Apache Derby that are highlighted below:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="EclipseLink_OneToOne-ejbPU" transaction-type="JTA">
  <class>eclipselink.apachederby.entity.Players</class>
  <class>eclipselink.apachederby.entity.Websites</class>
  <exclude-unlisted-classes>false</exclude-unlisted-classes>
  <properties>
   <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver"/>
   <property name="javax.persistence.jdbc.url" value="jdbc:derby:memory:mapping_entities_db;create=true"/>
   <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
   <property name="javax.persistence.schema-generation.create-source" value="metadata"/>
   <property name="javax.persistence.schema-generation.drop-source" value="metadata"/>
  </properties>
 </persistence-unit>
</persistence>

These configurations specifies that the necessary tables will be created on the default schema (named sun-appserv-samples) when running our application. You can explore them by navigating to the Services tab in NetBeans IDE and connecting to the sun-appserv-samples database:

JPA-one-to-one-derby-db
sun-appserv-samples database

3.4 Creating the web page

Now let’s have a quick look to the WAR module. We will use the JavaServer Faces technology for the presentation layer of our application. There is nothing fancy here, there are no managed beans, just a simple .xhtml page which looks like below:

<?xml version='1.0' encoding='UTF-8' ?>
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>@OneToOne</title>
    </h:head>
    <h:body>
        <h1>@OneToOne</h1>
        <h:form>
            <h:commandButton action="#{bean.persistAction()}" 
			value="Insert Player" style="width:300px;"/>
        </h:form>
        <h:form>
            <h:commandButton action="#{bean.findAction()}" 
			value="List Players (first 1000)" style="width:300px;"/>
        </h:form>
        <h:form>
            <h:commandButton action="#{bean.removeAction()}" 
			value="Remove First Player (_id:1 - _id:1000)" style="width:300px;"/>
        </h:form>
    </h:body>
</html>

Notice that CDI is used in order to reference #{bean} to the SampleBean session bean located in the eclipselink.apachederby.ejb package of our EJB module. When you press the “Insert player” button, it will call the persistAction() method and use a helper class (the Helper class inside the eclipselink.apachederby.helper package) in order to randomly generate some data and insert one player into the database. Similarly, the “List Players (first 1000)” button and “Remove First Player (_id:1 – _id:1000)” button will search for our list of players or remove the first it finds.

3.5 Testing the application

As mentioned above, pressing the “Insert player” button will insert a player into the database and then navigate to the same web page. If you check the IDE log, you should see a message like Info: PLAYER INSERTED ....
Now, press the “List Players (first 1000)” button to see what was inserted into our database. In our case, it showed:

Info:   PLAYERS INFORMATION ...
Info:   ************** PLAYER WITH ID: 43 *****************
Info:   PLAYER: Name:Tipsarevic, Surname:Janko, Age:28, Birth:6/22/84 12:00 AM
Info:   WEBSITE: eclipselink.apachederby.entity.Players@59e2f048
Info:   ****************************************************
Info:   NO MORE PLAYERS AVAILABLE ...
Info:   WEBSITES INFORMATION ...
Info:   ************** WEBSITE WITH ID: 44 *****************
Info:   WEBSITE: Url:http://www.jtipsarevic.com, This website belongs to :Tipsarevic Janko
Info:   ****************************************************
Info:   NO MORE WEBSITES AVAILABLE ...

This is the player information currently hosted in the database with his related website information. You should check the eclipselink.apachederby.ejb.SampleBean.findAction() method to see what’s happening behind the scene, but in simple words, we start from 1 and use a while loop to search the first 1000 players. To get a player website information, we simply call the player.getWebsite() method.

Now, pressing the “Remove First Player (_id:1 – _id:1000)” button will remove the player and display the following message in the IDE log:

Info:   REMOVING FIRST PLAYER (_id:1 - _id:1000) ...
Info:   PLAYER SUCCESSFULLY REMOVED ...

To make sure that the player (and his related website information) were removed from the database, press the “List Players (first 1000)” again. This will output something like:

Info:   PLAYERS INFORMATION ...
Info:   NO MORE PLAYERS AVAILABLE ...
Info:   WEBSITES INFORMATION ...
Info:   NO MORE WEBSITES AVAILABLE ...

The complete application is called OneToOne_EclipseLink_and_ApacheDerby.

4. @OneToOne in a NoSQL database

4.1 Introduction

Similarly to the case presented in the previous section, in this section we have developed an EAR application, called OneToOne_HOGM_and_MongoDB, which aims to illustrate the JPA @OneToOne associations in a NoSQL database. In developing the application we have used Hibernate Object/Grid Mapper (OGM), which provides the JPA support for some of the common NoSQL databases, and MongoDB to serve for the NoSQL database.

You can download the complete application from here.

4.2 Hibernate OGM and JPA 2.1 annotations support

Hibernate OGM translates each entity in accordance with the official JPA specification, but adapted to MongoDB capabilities. Between the supported annotations we also have @OneToOne. Moreover, Hibernate OGM supports unidirectional and bidirectional associations. Hibernate OGM stores the association information in MongoDB using one of the following two strategies:

  1. IN_ENTITY: store association information within the entity (we will use this one)
  2. ASSOCIATION_DOCUMENT: store association information in a dedicated document per association

For ASSOCIATION_DOCUMENT, you can define how to store association documents. Possible strategies are:

  • GLOBAL_COLLECTION (default): stores the association information in a unique MongoDB collection for all associations
  • COLLECTION_PER_ASSOCIATION: stores the association in a dedicated MongoDB collection per association

Now, using the default strategy, IN_ENTITY, we can distinguish two use-cases:

  • one-to-one unidirectional association

In this case, OGM stores the navigation information for associations in the collection representing the owner side of the association and each document from this collection contains a field for storing the corresponding foreign key.

JPA one-to-one unidirectional association in MongoDB
JPA one-to-one unidirectional association in MongoDB

  • one-to-one bidirectional association

In this case, the collection representing the entity that uses mappedBy (the non-owner side of the association) contains fields that store one foreign key per embedded collection, while the collection representing the owner side of the association contains, in each document, a field that stores the corresponding foreign key.

JPA one-to-one bidirectional association in MongoDB
JPA one-to-one bidirectional association in MongoDB

4.3 Configuring the database connection

Supposing that you already have installed and configured MongoDB on localhost (127.0.0.1:27017), our next step is the persistence.xml file, which contains several configurations specific to MongoDB that are highlighted below:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="HOGM_OneToOne-ejbPU" transaction-type="JTA">
  <provider>org.hibernate.ogm.jpa.HibernateOgmPersistence</provider>
  <class>hogm.mongodb.entity.Players</class>
  <class>hogm.mongodb.entity.Websites</class>
  <properties>
   <property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
   <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.SunOneJtaPlatform"/>
   <property name="hibernate.ogm.datastore.provider" value="mongodb"/>
   <property name="hibernate.ogm.datastore.document.association_storage" value="IN_ENTITY"/>
   <property name="hibernate.ogm.datastore.database" value="mapping_entities_db"/>
   <property name="hibernate.ogm.datastore.create_database" value="true"/>
   <property name="hibernate.ogm.mongodb.host" value="127.0.0.1"/>
   <property name="hibernate.ogm.mongodb.port" value="27017"/>
  </properties>
 </persistence-unit>
</persistence>

Rest of our application remains the same as in the case presented for SQL, except from the data generated and inserted into the database.

4.4 Testing the application

After you start the MongoDB database server you can run the application and start testing. Now we can just repeat the steps we did for the SQL case. Press the “Insert player” button and then the “List Players (first 1000)” button to see what was inserted into our database. In our case, the IDE log showed:

Info:   PLAYER INSERTED ...
Info:   PLAYERS INFORMATION ...
Info:   ************** PLAYER WITH ID: 45 *****************
Info:   PLAYER: Name:Federer, Surname:Roger, Age:31, Birth:8/8/81 12:00 AM
Info:   WEBSITE: hogm.mongodb.entity.Players@3b5e4654
Info:   ****************************************************
Info:   NO MORE PLAYERS AVAILABLE ...
Info:   WEBSITES INFORMATION ...
Info:   ************** WEBSITE WITH ID: 46 *****************
Info:   WEBSITE: Url:http://www.rogerfederer.com, This website belongs to :Federer Roger
Info:   ****************************************************
Info:   NO MORE WEBSITES AVAILABLE ...

To remove a player (and his related photos), press the “Remove First Player (_id:1 – _id:1000)”. Now, press the “List Players (first 1000)” again. These two actions are illustrated into the IDE log as follows:

Info:   REMOVING FIRST PLAYER (_id:1 - _id:1000) ...
Info:   PLAYER SUCCESSFULLY REMOVED ... 
Info:   PLAYERS INFORMATION ...
Info:   NO MORE PLAYERS AVAILABLE ...
Info:   WEBSITES INFORMATION ...
Info:   NO MORE WEBSITES AVAILABLE ...

The complete application is called OneToOne_HOGM_and_MongoDB.

5. Conclusion

In this article we have explore the JPA @OneToOne unidirectional and bidirectional associations in a SQL and NoSQL fashion. For testing the associations, we have developed two EAR applications, one using Apache Derby as the database layer, the other using MongoDB.

Download
You can download the full source code of this example here: JPA One-to-One Examples.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button