Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Hibernate annotations @ManyToOne and @OneToMany for associations

Tags:

hibernate

I am learning Spring, Hibernate, Maven by using this tutorial: Chad Lung: A project using Netbeans 7, JUnit, Maven, HSQLDB, Spring and Hibernate. It works ok but I need to make one-to-many relationship (one Employee have many Tasks). I have tried many examples but still can't get idea how to make my code work:

Employee.java:

package com.giantflyingsaucer.simplespringhibernate.entity;  import javax.persistence.*; import java.io.Serializable; import java.util.List;  @Entity @Table(name = "Employees") public class Employee implements Serializable {      private Integer employeeId;     private List<Task> tasks;      @Id     @Column(name = "idEmployees", nullable=false)     public Integer getEmployeeId() {         return this.employeeId;     }      public void setEmployeeId(Integer employeeId) {         this.employeeId = employeeId;     }      @OneToMany(fetch = FetchType.LAZY)     @JoinColumn(name="idEmployees")     public List<Task> getTasks() {         return tasks;     } } 

Task.java:

package com.giantflyingsaucer.simplespringhibernate.entity;  import javax.persistence.*; import java.io.Serializable;  @Entity @Table(name = "Tasks") public class Task implements Serializable {      private Integer taskId;     private Employee employee;       @Id     @Column(name = "idTasks", nullable=false)     public Integer getTaskId() {         return this.taskId;     }      public void setTaskId(Integer taskId) {         this.taskId = taskId;     }      @ManyToOne(fetch = FetchType.LAZY)     @JoinColumn(name = "TasksIdEmployees")     public Employee getEmployee() {return employee;}  } 

db-config.xml:

<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"     destroy-method="close">      <property name="driverClass">         <value>${jdbc.driver.className}</value>     </property>     <property name="jdbcUrl">         <value>${jdbc.url}</value>     </property>     <property name="user">         <value>${jdbc.username}</value>     </property>     <property name="password">         <value>${jdbc.password}</value>     </property> </bean> <bean id="sessionFactory"     class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">     <property name="dataSource">         <ref bean="dataSource" />     </property>     <property name="packagesToScan" value="com.giantflyingsaucer.simplespringhibernate.entity" />     <property name="hibernateProperties">         <props>             <prop key="hibernate.dialect">${jdbc.hibernate.dialect}</prop>             <prop key="hibernate.hbm2ddl.auto">update</prop>             <prop key="hibernate.show_sql">false</prop>         </props>     </property> </bean> <bean id="transactionManager"     class="org.springframework.orm.hibernate3.HibernateTransactionManager">     <property name="sessionFactory">         <ref bean="sessionFactory" />     </property> </bean> <tx:annotation-driven /> 

MySQL tables:

CREATE TABLE employees ( `idEmployees` int(11) NOT NULL, PRIMARY KEY (`idEmployees`) );  CREATE TABLE tasks ( `idTasks` int(11) NOT NULL, `TasksIdEmployees` int(11) DEFAULT NULL, PRIMARY KEY (`idTasks`), KEY `FkTasksEmployees_idx` (`TasksIdEmployees`), CONSTRAINT `FkTasksEmployees` FOREIGN KEY (`TasksIdEmployees`) REFERENCES `employees`   (`idEmployees`) ON DELETE NO ACTION ON UPDATE NO ACTION ); 

Thanks a lot!

I found an answer by autogenerating Mapping files and POJOs in NetBeans:

// Employee.java:     @OneToMany(fetch = FetchType.LAZY, mappedBy = "employees")     public List<Task> getTasks() {         return this.tasks;     }      public void setTasks(List<Task> tasks) {         this.tasks = tasks;     }  // Task.java: @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "TasksIdEmployees") public Employee getEmployees() {     return this.employee; }  public void setEmployees(Employee employee) {     this.employee = employee; } 
like image 387
Karloss Avatar asked May 12 '13 19:05

Karloss


1 Answers

What's wrong is the following:

@OneToMany(fetch = FetchType.LAZY) @JoinColumn(name="idEmployees") public List<Task> getTasks() {     return tasks; } 

And it's wrong for two reasons.

  1. @JoinColumn(name="idEmployees") means: this OneToMany is mapped using a join column (i.e. a foreign key) named idEmployees. But the join column is not named idEmployees. idEmployees is the primary key of the Employee table. The join column name is TasksIdEmployees. Putting the right name would make the mapping correct for a unidirectional OneToMany association. But the association is bidirectional, which leads to the second reason...

  2. In a bidirectional association, there is no need (and it's a bug) to repeat the mapping information on both sides of the association. One side (the many side) must be the owner of the association and define the mapping. The other side must be the inverse side by simply saying: go see at the other side how this association is mapped. This is done using the mappedBy attribute, which tells Hibernate the name of the field or property on the other side which is the owner of the association:

    @OneToMany(mappedBy = "employee") public List<Task> getTasks() {     return tasks; } 

Note that LAZY is the default for toMany associations, so it's unnecessary to specify it.

like image 62
JB Nizet Avatar answered Sep 19 '22 21:09

JB Nizet