In depth analysis of the one to one association mapping in Java's Hibernate framework

  • 2020-04-01 04:33:47
  • OfStack

As an ORM framework, hibernate definitely needs to satisfy our need to implement the association between tables. Hibernate is easy to implement in association methods. Here's a look at the one-to-one approach:
  Without further ado, we directly on the code:
  Two entity classes, TUser and TPassport:


public class TUser implements Serializable{ 
 
 private static final long serialVersionUID = 1L; 
 private int id; 
 private int age; 
 private String name; 
 private TPassport passport; 
  //Omit the Get/Set method
} 
public class TPassport implements Serializable{ 
 
 private static final long serialVersionUID = 1L; 
 private int id; 
 private String serial; 
 private int expiry; 
 private TUser user; 
  //Omit the Get/Set method
} 

    Let's see what's different about the mapping file:


<hibernate-mapping package="org.hibernate.tutorial.domain4"> 
 <class name="TUser" table="USER4"> 
  <id name="id" column="id"> 
   <generator class="native" /> 
  </id> 
  <property name="name" type="java.lang.String" column="name"/> 
  <property name="age" type="java.lang.Integer" column="age"/> 
  <one-to-one name="passport" class="TPassport" 
     cascade="all" outer-join="true" /> 
 </class> 
</hibernate-mapping> 

    Here we see a new tag, one-to-one, which indicates that the current class has a one-to-one relationship with its corresponding class. Cascade is a cascade relationship.
  Let's look at another mapping file for TPassport:


<hibernate-mapping package="org.hibernate.tutorial.domain4"> 
 <class name="TPassport" table="passport4"> 
  <id name="id" column="id"> 
   <generator class="foreign" > 
    <param name="property">user</param> 
   </generator> 
  </id> 
  <property name="serial" type="java.lang.String" column="serial"/> 
  <property name="expiry" type="java.lang.Integer" column="expiry"/> 
  <one-to-one name="user" class="TUser" constrained="true" /> 
 </class> 
</hibernate-mapping>

   
    Here we focus on the class value of the generator, which is foreign indicating the reference foreign key, and which reference is specified by param, indicating the reference User class id. There is also a constrained attribute in the one-to-one tag, which tells hibernate that the current class has a foreign key constraint, that is, the ID of the current class is generated according to the ID of TUser.
  Now let's go directly to the test class, and instead of using JUnit, we'll use the Main method:


public static void main(String[] args) { 
   
  Configuration cfg = new Configuration().configure(); 
  SessionFactory sessionFactory = cfg.buildSessionFactory(); 
  Session session = sessionFactory.openSession(); 
   
  session.beginTransaction(); 
   
  TUser user = new TUser(); 
  user.setAge(20); 
  user.setName("shunTest"); 
   
  TPassport passport = new TPassport(); 
  passport.setExpiry(20); 
  passport.setSerial("123123123"); 
   
  passport.setUser(user); 
  user.setPassport(passport); 
   
  session.save(user); 
   
  session.getTransaction().commit(); 
   
 } 

    The code is very simple. Here we look:


session.save(user); 

    The reason why we only call save here is the cascade attribute in our TUser mapping file, which is set to all, which means that when we save, update, delete, and so on for TUser, TPassport will do the same, so we don't have to say session.save(passport). We see backstage:


Hibernate: insert into USER4 (name, age) values (?, ?) 
Hibernate: insert into passport4 (serial, expiry, id) values (?, ?, ?) 
Hibernate:       It prints out two statements that prove that hibernate has definitely done the job for us.
 
 
  Now let's have another test class to test the query:

public static void main(String[] args) { 
  Configuration cfg = new Configuration().configure(); 
  SessionFactory sessionFactory = cfg.buildSessionFactory(); 
  Session session = sessionFactory.openSession(); 
   
  TUser user = (TUser)session.load(TUser.class,new Integer(3)); 
  System.out.println(user.getName()+":"+user.getPassport().getSerial()); 
   
 } 
    So here we are querying out the TUser class and getting the TPassport object.
  We can see that hibernate's SQL statement is:

Hibernate:
select tuser0_.id as id0_1_, tuser0_.name as name0_1_, tuser0_.age as age0_1_, tpassport1_.id as id1_0_, tpassport1_.serial as serial1_0_, tpassport1_.expiry as expiry1_0_ from USER4 tuser0_ left outer join passport4 tpassport1_ on tuser0_.id=tpassport1_.id where tuser0_.id=?

    We see the statement left outer join, this is because we set outer-join="true" in one-to-one, we try to change it to false, we see the SQL statement as follows:

Hibernate:
select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.age as age0_0_ from USER4 tuser0_ where tuser0_.id=?
Hibernate:
Select tpassport0_. Id as id1_0_, tpassport0_. Serial as serial1_0_, tpassport0_. Expiry as expiry1_0_ from passport4 tpassport0_ where tpassport0_. Id =?

    Now it is divided into two check, according to the ID of the first check to the second check.
 
  A lot of people might ask why you don't use TPassport to look up TUser, but you can, because it's a one-to-one relationship, and it's the same for whoever looks up who.

Foreign key link
Now let's look at the one-to-one association with foreign keys.
  As always, we wrote two entity classes, TGroup and TUser


public class TGroup implements Serializable{ 
 
 private static final long serialVersionUID = 1L; 
 private int id; 
 private String name; 
 private TUser user; 
  //Omit the Get/Set method
} 
public class TUser implements Serializable{ 
 
 private static final long serialVersionUID = 1L; 
 private int id; 
 private int age; 
 private String name; 
 private TGroup group; 
  //Omit the Get/Set method
  
} 

    When we are done with the entity class, let's take a look at the mapping file:


<hibernate-mapping package="org.hibernate.tutorial.domain5"> 
 <class name="TUser" table="USER5"> 
  <id name="id" column="id"> 
   <generator class="native" /> 
  </id> 
  <property name="name" type="java.lang.String" column="name"/> 
  <property name="age" type="java.lang.Integer" column="age"/> 
  <many-to-one name="group" class="TGroup" column="group_id" unique="true" /> 
 </class> 
</hibernate-mapping> 

    Here we see the many-to-one tag instead of one-to-one, why?
  I didn't pay much attention to it when I used it before, but I can use it anyway. However, after reading xia xin's book, I finally understand that in fact, the way of association by foreign keys is just a special way of many-to-one. We limited it to only one by unique = "true", that is, we realized one-to-one association.
  Next let's look at the TGroup mapping file:


<hibernate-mapping package="org.hibernate.tutorial.domain5"> 
 <class name="TGroup" table="group5"> 
  <id name="id" column="id"> 
   <generator class="native" /> 
  </id> 
  <property name="name" type="java.lang.String" column="name"/> 
  <one-to-one name="user" class="TUser" property-ref="group" /> 
 </class> 
</hibernate-mapping> 

    Here, notice that we use one-to-one again to indicate that the current entity and TUser have a one-to-one relationship. Instead of many-to-one, we use one-to-one to specify which attribute in the TUser entity is associated with the current class TGroup. Here we specify that TUser is associated with TUser through the group attribute. Property-ref specifies which property to associate through.
  Now let's look at the test class:


public class HibernateTest { 
 
 public static void main(String[] args) { 
 
  Configuration cfg = new Configuration().configure(); 
  SessionFactory sessionFactory = cfg.buildSessionFactory(); 
  Session session = sessionFactory.openSession(); 
   
  session.beginTransaction(); 
   
  TGroup group = new TGroup(); 
  group.setName("testGroup"); 
   
  TUser user = new TUser(); 
  user.setAge(23); 
  user.setName("test"); 
   
  user.setGroup(group); 
  group.setUser(user); 
   
  session.save(group); 
  session.save(user); 
   
  session.getTransaction().commit(); 
  session.close(); 
 } 
 
} 

    Note that we need to save twice in our code this time, because they have a corresponding pair, and saving only one won't do anything to the other. So we need to call the save operation twice. Submit at the end.
  Hibernate prints out statements:


Hibernate: insert into group5 (name) values (?) 
Hibernate: insert into USER5 (name, age, group_id) values (?, ?, ?) 

    This shows that we have correctly stored two object values.
 
  We write an additional test class to query:


public static void main(String[] args) { 
 
  Configuration cfg = new Configuration().configure(); 
  SessionFactory sessionFactory = cfg.buildSessionFactory(); 
  Session session = sessionFactory.openSession(); 
   
  TUser user = (TUser)session.load(TUser.class,new Integer(1)); 
  System.out.println("From User get Group:"+user.getGroup().getName()); 
   
   
  TGroup group = (TGroup)session.load(TGroup.class,new Integer(1)); 
  System.out.println("From Group get User:" + group.getUser().getName()); 
   
  session.close(); 
   
 } 

    We can both get the right result, which means that we can achieve our goal by taking the value of each other out of the two objects.
  The TGroup and TUser used in this example are just examples. In fact, users in real life generally correspond to multiple groups.


Related articles: