34

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;
}
Karloss
  • 817
  • 3
  • 9
  • 27

1 Answers1

66

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.

Serge Wautier
  • 21,494
  • 13
  • 69
  • 110
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    While it might be considered a "bug" to use a bidirectional relationship, many unexpected issues can come up if you do not. Hibernate will make more query calls in a unidirectional mapping and could create unexpected tables. Read here for more info: https://thoughts-on-java.org/best-practices-many-one-one-many-associations-mappings/ – DavidR Apr 02 '19 at 18:50
  • No one's calling bidirectional relationships a bug. He said that repeating the mapping information on both sides of the bidirectional is redundant and a bug. He's saying you want @JoinColumn on one side (and only one side) and mappedBy="..." on the other side (and only the other side) – acat Jul 03 '21 at 17:45