Exemplo de Mapeamento One To Many com Anotações no Hibernate

Hoje vamos analisar o mapeamento de um para muitos no Hibernate. Vamos analisar um exemplo de mapeamento de um para muitos no Hibernate usando anotação e configuração XML.

Mapeamento de um para muitos no Hibernate

Em termos simples, o mapeamento de um para muitos significa que uma linha em uma tabela pode ser mapeada para várias linhas em outra tabela. Por exemplo, pense em um sistema de carrinho de compras onde temos outra tabela para os itens. Um carrinho pode ter vários itens, então aqui temos um mapeamento de um para muitos. Vamos usar o cenário de Carrinho-Itens para nosso exemplo de mapeamento de um para muitos no Hibernate.

Mapeamento de um para muitos no Hibernate – Configuração do Banco de Dados

Podemos usar uma restrição de chave estrangeira para o mapeamento de um para muitos. Abaixo está nosso script de banco de dados para as tabelas Cart e Items. Estou usando o banco de dados MySQL para o exemplo de mapeamento de um para muitos no Hibernate. setup.sql

CREATE TABLE `Cart` (
  `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `total` decimal(10,0) NOT NULL,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE `Items` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `cart_id` int(11) unsigned NOT NULL,
  `item_id` varchar(10) NOT NULL,
  `item_total` decimal(10,0) NOT NULL,
  `quantity` int(3) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `cart_id` (`cart_id`),
  CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

Abaixo está o diagrama ER da tabela Carrinho e Itens. Nossa configuração do banco de dados está pronta, vamos avançar para a criação do projeto de exemplo de mapeamento Um para Muitos com Hibernate. Primeiro, vamos usar a configuração baseada em XML e depois implementaremos o mapeamento um para muitos usando Hibernate e anotação JPA.

Estrutura do Projeto de Mapeamento Um para Muitos com Hibernate

Crie um projeto Maven simples no Eclipse ou no seu IDE favorito, a estrutura final do projeto ficará como na imagem abaixo.

Dependências do Maven para Hibernate

Nosso arquivo pom.xml final contém as dependências para o Hibernate e o driver MySQL. O Hibernate usa o logging do JBoss e ele é automaticamente adicionado como dependências transitivas.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.journaldev.hibernate</groupId>
  <artifactId>HibernateOneToManyMapping</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <dependencies>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>4.3.5.Final</version>
  	</dependency>
  	<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>5.0.5</version>
  	</dependency>
  </dependencies>
  
</project>

Note que estou usando a última versão do Hibernate 4.3.5.Final e a versão do driver MySQL baseada na instalação do meu banco de dados.

Classes de Modelo de Mapeamento Um Para Muitos do Hibernate

Para nossas tabelas Cart e Items, temos classes de modelo para refleti-las. Cart.java

package com.journaldev.hibernate.model;

import java.util.Set;

public class Cart {

	private long id;
	private double total;
	private String name;
	private Set<Items> items;
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public double getTotal() {
		return total;
	}
	public void setTotal(double total) {
		this.total = total;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Set<Items> getItems() {
		return items;
	}
	public void setItems(Set<Items> items) {
		this.items = items;
	}
	
}

I am using Set of Items, so that every record is unique. We can also use List or Array for one to many mapping in hibernate. Items.java

package com.journaldev.hibernate.model;

public class Items {

	private long id;
	private String itemId;
	private double itemTotal;
	private int quantity;
	private Cart cart;
	
	// O Hibernate requer um construtor sem argumentos
	public Items(){}
	
	public Items(String itemId, double total, int qty, Cart c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart=c;
	}
	public String getItemId() {
		return itemId;
	}
	public void setItemId(String itemId) {
		this.itemId = itemId;
	}
	public double getItemTotal() {
		return itemTotal;
	}
	public void setItemTotal(double itemTotal) {
		this.itemTotal = itemTotal;
	}
	public int getQuantity() {
		return quantity;
	}
	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}
	public Cart getCart() {
		return cart;
	}
	public void setCart(Cart cart) {
		this.cart = cart;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
}

Os Items têm uma relação de muitos para um com Cart, então não precisamos ter uma coleção para o objeto Cart.

Classe de Utilidade SessionFactory do Hibernate

Temos uma classe de utilidade para criar a SessionFactory do Hibernate. HibernateUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Crie a SessionFactory a partir do hibernate.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate.cfg.xml");
        	System.out.println("Hibernate Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Arquivo XML de Configuração do Hibernate

Nosso arquivo de configuração xml do hibernate contém informações do banco de dados e detalhes do recurso de mapeamento. hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping resource="cart.hbm.xml"/>
        <mapping resource="items.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

Exemplo de Mapeamento Um para Muitos com Hibernate – Configuração XML

Esta é a parte mais importante do tutorial, vamos ver como mapear as classes Cart e Items para um para muitos no hibernate. cart.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
	"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd">
	
<hibernate-mapping package="com.journaldev.hibernate.model">
	<class name="Cart" table="CART" >
		<id name="id" type="long">
			<column name="cart_id" />
			<generator class="identity" />
		</id>
		<property name="total" type="double">
			<column name="total" />
		</property>
		<property name="name" type="string">
			<column name="name" />
		</property>
		<set name="items" table="ITEMS" fetch="select">
			<key>
				<column name="cart_id" not-null="true"></column>
			</key>
			<one-to-many class="Items"/>
		</set>
	</class>
	
</hibernate-mapping>

A parte importante é o elemento set e o elemento one-to-many dentro dele. Observe que estamos fornecendo a chave a ser usada para o mapeamento de um para muitos, ou seja, cart_id. items.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
"https://hibernate.org/dtd/hibernate-mapping-3.0.dtd" >

<hibernate-mapping package="com.journaldev.hibernate.model">

	<class name="Items" table="ITEMS">
		<id name="id" type="long">
			<column name="id" />
			<generator class="identity" />
		</id>
		<property name="itemId" type="string">
			<column name="item_id"></column>
		</property>
		<property name="itemTotal" type="double">
			<column name="item_total"></column>
		</property>
		<property name="quantity" type="integer">
			<column name="quantity"></column>
		</property>
		
		<many-to-one name="cart" class="Cart">
			<column name="cart_id" not-null="true"></column>
		</many-to-one>
	</class>

</hibernate-mapping>

Observe que de itens para carrinho, é uma relação de muitos para um. Portanto, precisamos usar o elemento many-to-one para o carrinho e estamos fornecendo o nome da coluna que será mapeada com a chave. Portanto, com base na configuração de mapeamento hibernate do Carrinho, sua chave cart_id será usada para mapeamento. Nosso projeto para Exemplo de Mapeamento Um para Muitos com Hibernate usando mapeamento XML está pronto, vamos escrever um programa de teste e verificar se está funcionando bem ou não.

Exemplo de Mapeamento Um para Muitos com Hibernate – Programa de Teste

HibernateOneToManyMain.java

package com.journaldev.hibernate.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart;
import com.journaldev.hibernate.model.Items;
import com.journaldev.hibernate.util.HibernateUtil;

public class HibernateOneToManyMain {

	public static void main(String[] args) {

		Cart cart = new Cart();
		cart.setName("MyCart");
		
		Items item1 = new Items("I1", 10, 1, cart);
		Items item2 = new Items("I2", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obter Sessão
		sessionFactory = HibernateUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//iniciar transação
		tx = session.beginTransaction();
		
		//Salvar os objetos do Modelo
		session.save(cart);
		session.save(item1);
		session.save(item2);
		
		//Confirmar transação
		tx.commit();
		System.out.println("Cart ID="+cart.getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

Observe que precisamos salvar tanto os objetos Carrinho quanto os Itens um por um. O Hibernate cuidará de atualizar as chaves estrangeiras na tabela Itens. Quando executamos o programa acima, obtemos a seguinte saída.

Hibernate Configuration loaded
Hibernate serviceRegistry created
Session created
Hibernate: insert into CART (total, name) values (?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (item_id, item_total, quantity, cart_id) values (?, ?, ?, ?)
Hibernate: update ITEMS set cart_id=? where id=?
Hibernate: update ITEMS set cart_id=? where id=?
Cart ID=6
Closing SessionFactory

Observe que o Hibernate está usando uma consulta de atualização para definir o cart_id na tabela ITENS.

Mapeamento de Um Para Muitos do Hibernate com Anotações

Agora que vimos como implementar o mapeamento de Um Para Muitos no Hibernate usando configurações baseadas em XML, vejamos como podemos fazer a mesma coisa usando anotações JPA.

Exemplo de Mapeamento de Um Para Muitos do Hibernate com Anotações

O arquivo de configuração do Hibernate é quase o mesmo, exceto que o elemento de mapeamento muda porque estamos usando Classes para o mapeamento de um para muitos do hibernate usando anotação. hibernate-annotation.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"https://hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pankaj123</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/TestDB</property>
        <property name="hibernate.connection.username">pankaj</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>
        
        <mapping class="com.journaldev.hibernate.model.Cart1"/>
        <mapping class="com.journaldev.hibernate.model.Items1"/>
    </session-factory>
</hibernate-configuration>

Classe de Utilidade do Hibernate SessionFactory

A classe de utilidade do SessionFactory é quase a mesma, só precisamos usar o novo arquivo de configuração do hibernate. HibernateAnnotationUtil.java

package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class HibernateAnnotationUtil {

	private static SessionFactory sessionFactory;
	
	private static SessionFactory buildSessionFactory() {
        try {
            // Cria o SessionFactory a partir do hibernate-annotation.cfg.xml
        	Configuration configuration = new Configuration();
        	configuration.configure("hibernate-annotation.cfg.xml");
        	System.out.println("Hibernate Annotation Configuration loaded");
        	
        	ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        	System.out.println("Hibernate Annotation serviceRegistry created");
        	
        	SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        	
            return sessionFactory;
        }
        catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }
	
	public static SessionFactory getSessionFactory() {
		if(sessionFactory == null) sessionFactory = buildSessionFactory();
        return sessionFactory;
    }
}

Modelo de Anotação de Mapeamento One To Many do Hibernate

Como não temos arquivos de mapeamento baseados em xml, todas as configurações relacionadas ao mapeamento serão feitas usando anotações JPA nas classes de modelo. Se você entende o mapeamento baseado em xml, é muito simples e semelhante. Cart1.java

package com.journaldev.hibernate.model;

import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="CART")
public class Cart1 {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="cart_id")
	private long id;
	
	@Column(name="total")
	private double total;
	
	@Column(name="name")
	private String name;
	
	@OneToMany(mappedBy="cart1")
	private Set items1;
	
// Métodos Getter Setter para propriedades
}

Ponto importante a notar é a anotação OneToMany onde a variável mappedBy é usada para definir a propriedade na classe Items1 que será usada para o propósito de mapeamento. Então, devemos ter uma propriedade chamada “cart1” na classe Items1. Não se esqueça de incluir todos os métodos getter-setter. Items1.java

package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="ITEMS")
public class Items1 {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id")
	private long id;
	
	@Column(name="item_id")
	private String itemId;
	
	@Column(name="item_total")
	private double itemTotal;
	
	@Column(name="quantity")
	private int quantity;
	
	@ManyToOne
	@JoinColumn(name="cart_id", nullable=false)
	private Cart1 cart1;
	
	// O Hibernate requer um construtor sem argumentos
	public Items1(){}
	
	public Items1(String itemId, double total, int qty, Cart1 c){
		this.itemId=itemId;
		this.itemTotal=total;
		this.quantity=qty;
		this.cart1=c;
	}
// Métodos Getter Setter
}

O ponto mais importante na classe acima é a anotação ManyToOne na variável de classe Cart1 e a anotação JoinColumn para fornecer o nome da coluna para mapeamento. Isso é tudo para o mapeamento de um para muitos no Hibernate usando anotações em classes de modelo. Compare com as configurações baseadas em XML, você as encontrará muito semelhantes. Vamos escrever um programa de teste e executá-lo.

Exemplo de Programa de Teste de Mapeamento Um para Muitos com Anotação do Hibernate

Nosso programa de teste é como a configuração baseada em XML, estamos apenas usando as novas classes para obter a Sessão do Hibernate e salvar os objetos de modelo no banco de dados. HibernateOneToManyAnnotationMain.java

package com.journaldev.hibernate.main;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import com.journaldev.hibernate.model.Cart1;
import com.journaldev.hibernate.model.Items1;
import com.journaldev.hibernate.util.HibernateAnnotationUtil;

public class HibernateOneToManyAnnotationMain {

	public static void main(String[] args) {

		Cart1 cart = new Cart1();
		cart.setName("MyCart1");
		
		Items1 item1 = new Items1("I10", 10, 1, cart);
		Items1 item2 = new Items1("I20", 20, 2, cart);
		Set itemsSet = new HashSet();
		itemsSet.add(item1); itemsSet.add(item2);
		
		cart.setItems1(itemsSet);
		cart.setTotal(10*1 + 20*2);
		
		SessionFactory sessionFactory = null;
		Session session = null;
		Transaction tx = null;
		try{
		//Obter Sessão
		sessionFactory = HibernateAnnotationUtil.getSessionFactory();
		session = sessionFactory.getCurrentSession();
		System.out.println("Session created");
		//iniciar transação
		tx = session.beginTransaction();
		//Salvar o objeto de modelo
		session.save(cart);
		session.save(item1);
		session.save(item2);
		//Cometer transação
		tx.commit();
		System.out.println("Cart1 ID="+cart.getId());
		System.out.println("item1 ID="+item1.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		System.out.println("item2 ID="+item2.getId()+", Foreign Key Cart ID="+item1.getCart1().getId());
		
		}catch(Exception e){
			System.out.println("Exception occured. "+e.getMessage());
			e.printStackTrace();
		}finally{
			if(!sessionFactory.isClosed()){
				System.out.println("Closing SessionFactory");
				sessionFactory.close();
			}
		}
	}

}

Ao executarmos o exemplo de teste de mapeamento um para muitos com anotação do Hibernate acima, obtemos a seguinte saída.

Hibernate Annotation Configuration loaded
Hibernate Annotation serviceRegistry created
Session created
Hibernate: insert into CART (name, total) values (?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Hibernate: insert into ITEMS (cart_id, item_id, item_total, quantity) values (?, ?, ?, ?)
Cart1 ID=7
item1 ID=9, Foreign Key Cart ID=7
item2 ID=10, Foreign Key Cart ID=7
Closing SessionFactory

Isso é tudo para o mapeamento um para muitos com Hibernate, baixe o projeto de exemplo no link abaixo e faça mais algumas experiências.

Baixar Projeto de Mapeamento Um para Muitos do Hibernate

Source:
https://www.digitalocean.com/community/tutorials/hibernate-one-to-many-mapping-annotation