Bidirectional @OneToMany / @ManyToOne association

One of goals the in programming is representation of models from the real world. Very often an application needs to model some relationship between entities. In the last article about Hibernate associations I described the rules of setting up a “one to one” relationship. Today I’m going to show you how to setup a bidirectional “one to many” and “many to one” association. This example will be based on previous Hibernate tutorials.

At the start I need to say that my code example will be based on a simple situation. Let’s imagine a football league. Every league has teams, and in the team can play some players. So the summary is following: one team has many players, one player can play for one team. In this way we get obvious “one to many” and “many to one” relationships.
I use MySQL as a database in this example. Here are scripts for the table’s creation:

CREATE TABLE `teams` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

CREATE TABLE `players` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `lastname` varchar(20) NOT NULL,
  `team_id` int(6) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `player's team` (`team_id`),
  CONSTRAINT `player's team` FOREIGN KEY (`team_id`) REFERENCES `teams` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

The next step is creation of POJOs:

import java.util.Set;

import javax.persistence.*;

@Entity
@Table(name = "teams")
public class Team {

	@Id
	@GeneratedValue
	private Integer id;
	
	private String name;
	
	@OneToMany(mappedBy="team", cascade=CascadeType.ALL)
	private Set players;
	
	public Team(String name) {
		this.name = name;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Set getPlayers() {
		return players;
	}

	public void setPlayers(Set players) {
		this.players = players;
	}
}

I have used @OneToMany because one team can have many players. In the next POJO, the association will be @ManyToOne since many players can play for one team.

import javax.persistence.*;

@Entity
@Table(name = "players")
public class Player {

	@Id
	@GeneratedValue
	private Integer id;

	private String lastname;

	@ManyToOne
	@JoinColumn(name = "team_id")
	private Team team;

	public Player(String lastname) {
		this.lastname = lastname;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLastname() {
		return lastname;
	}

	public void setLastname(String lastname) {
		this.lastname = lastname;
	}

	public Team getTeam() {
		return team;
	}

	public void setTeam(Team team) {
		this.team = team;
	}
}

Here I specify the column (team_id) which will be joined from the owning side (Teams). Notice that I don’t declare team_id field in the POJO. If I need to change a team for a player I just need to use setTeam(Team team) setter.

After POJOs were declared, I can demonstrate how to persist them:

...
	public static void main(String[] args) {

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();
		session.beginTransaction();
		
		Team team = new Team("Barcelona");
		Set players = new HashSet();
		
		Player p1 = new Player("Messi");
		Player p2 = new Player("Xavi");
		
		p1.setTeam(team);
		p2.setTeam(team);
		
		players.add(p1);
		players.add(p2);
		
		team.setPlayers(players);
		
		session.save(team);
		
		session.getTransaction().commit();
		
		session.close();

	}
...

The result of the code execution is:

Hibernate: insert into teams (name) values (?)
Hibernate: insert into players (lastname, team_id) values (?, ?)
Hibernate: insert into players (lastname, team_id) values (?, ?)

That’s it, in this tutorial I have demonstrated how to setup “one to many” and “many to one” bidirectional association. I don’t see any sense in the same tutorial with an example of unidirectional association. Because Hibernate has its own best practices:

Unidirectional associations are more difficult to query. In a large application, almost all associations must be navigable in both directions in queries.

Share and Enjoy

  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS
  • pingpingSong

    hello, hibernate4 select Entity ? why do?

  • pingpingSong

    hibernate4 对于 onetomany 的查询时候该如何去做? 刚才的例子只是给出来了insert 表中的。

    取数该注意什么? 比如说延迟加载>?

    我做的查询例子就报以下错误:

    org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.ecoinfo.teaching.vo.Users.article, could not initialize proxy – no Session

  • my2 koseli

    Hi

    I have another question regarding the one to many mappings if you have a moment:
    I have two tables for example:

    1) test_number
    columns:
    id
    name

    2) test_result
    columns:

    test_number (FK)
    result_key
    result_value

    Test_number and Test_result have one to many mappings with (test_number column in Test_result acting as a FK)

    I have defined following annotations in the model classes.

    CLASS TEST_NUMBER

    created
    @Entity
    @Table(name=”test_number”)

    public class TestNumber {
    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(fetch = FetchType.LAZY, mappedBy=”testnumber”)
    @JoinColumn(name = “ID”)
    private Set testresults = new HashSet(0);

    public Set getTestresults() {
    return testresults;
    }

    public void setTestresults(Set testresults) {
    this.testresults = testresults;
    }

    CLASS: TestResult

    @Entity
    @Table(name=”test_result”)
    public class TestResult {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = “TEST_number”)
    private TestNumber test_number;

    public TestNumber getTestNumber() {
    return this.test_number;
    }

    public void setTestNumber(TestResult test_number) {
    this.test_number = test_number;
    }

    When i try to compile and upload I get following error:

    Caused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: com.model.TestNumber.testesult
    at org.hibernate.cfg.annotations.CollectionBinder.bind(CollectionBinder.java:526)
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1954)
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:766)
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:685)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:3456)
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3410)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1336)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1788)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:247)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:373)
    at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:358)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1547)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1485)

    Thank you

    • my2 koseli

      Hi
      I figured the problem in this case. I was incorrectly mapping the column name when I should have been mapping the property name.

      thank you

  • Gabriel Andrade

    Does this solution works if i want to get players from a selected team with team.getPlayers() method?
    Or i necessarily need to have a method with a JPQL to get the players from that team??