Hibernate: mapeo de muchos a muchos

En RDBMS, podemos ver un uso muy común de las relaciones padre-hijo. Se puede lograr en Hibernate a través de 

  1. Relación de uno a muchos
  2. Relación de muchos a uno
  3. Relación uno a uno
  4. Relación de muchos a muchos

Aquí discutiremos cómo realizar Hibernate: mapeos de muchos a muchos . A continuación se muestran las tablas de ejemplo para demostrar las asignaciones de muchos a muchos como se indica a continuación:

  • geekEmployeeData
  • SkillsetData
  • geekEmpleadoHabilidad

Implementación:

A. Los pasos para la creación de tablas son los siguientes:

-- Table containing employeedata
create table geeksforgeeks.geekEmployeeData (
   id INT NOT NULL auto_increment,
   firstName VARCHAR(20) default NULL,
   lastName  VARCHAR(20) default NULL,
   salary     INT  default NULL,
   PRIMARY KEY (id)
);
-- Table containing skillsets
create table geeksforgeeks.SkillsetData (
   id INT NOT NULL auto_increment,
   skillName VARCHAR(30) default NULL,
   PRIMARY KEY (id)
);

-- Table which can combine employeedata and skillsets
create table geekEmployeeSkill (
   geekEmpId INT NOT NULL,
   skillId INT NOT NULL,
   PRIMARY KEY (geekEmpId,skillId)
);

creación de la tabla geekEmployeeSkill

Se debe implementar una asignación de muchos a muchos mediante una colección Set java y no debe contener ningún elemento duplicado. Un Conjunto se puede mapear con un elemento <set> en la tabla de mapeo. Se puede inicializar con java.util.HashSet. 

B. Clases POJO

Para las operaciones CRUD de datos de la tabla geekEmployeeData, esto es obligatorio

Ejemplo:

Java

// Java Program to Illustrate GeekEmployeeData Class
 
// Importing required classes
import java.util.Set;
 
// Class
public class GeekEmployeeData {
 
    // Attributes are mapping to geekEmployeeData
 
    // geekEmployeeData.id
    private int id;
   
    // geekEmployeeData.firstName
    private String firstName;
   
    // geekEmployeeData.lastName
    private String lastName;
   
    // geekEmployeeData.salary
    private int salary;
 
    // Collection of skillsets in Set variable
    private Set skillSets;
 
    // Method
    public Set getSkillSets() { return skillSets; }
 
    public void setSkillSets(Set skillSets)
    {
 
        // This keyword refers to current instance itself
        this.skillSets = skillSets;
    }
 
    // Constructor 1
    public GeekEmployeeData() {}
 
    // Constructor 2
    public GeekEmployeeData(String firstName,
                            String lastName, int salary)
    {
 
        // This keyword refers to current instance itself
        this.firstName = firstName;
        this.lastName = lastName;
        this.salary = salary;
    }
 
    // Getter  and Setters
 
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName)
    {
        this.firstName = firstName;
    }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName)
    {
        this.lastName = lastName;
    }
    public int getSalary() { return salary; }
    public void setSalary(int salary)
    {
        this.salary = salary;
    }
}

Ahora definamos otra clase POJO para la tabla «SkillsetData». Esta clase debe tener el método equals() y hashcode() y definitivamente se requiere para verificar si dos elementos/objetos son iguales.

C. SkillsetData.java 

Para las operaciones CRUD de datos de la tabla SkillsetData, esto es obligatorio

Ejemplo:

Java

public class SkillsetData {
    private int id;
    private String skillName;
 
    public SkillsetData() {}
 
    public SkillsetData(String skillName)
    {
        this.skillName = skillName;
    }
 
    public int getId() { return id; }
 
    public void setId(int id) { this.id = id; }
 
    public String getSkillName() { return skillName; }
 
    public void setSkillName(String skillName)
    {
        this.skillName = skillName;
    }
 
    public boolean equals(Object obj)
    {
       
        // This method is definitely required in order to
        // check whether any two elements/objects are equal
        if (obj == null)
            return false;
        if (!this.getClass().equals(obj.getClass()))
            return false;
 
        SkillsetData obj2 = (SkillsetData)obj;
        if ((this.id == obj2.getId())
            && (this.skillName.equals(
                obj2.getSkillName()))) {
            return true;
        }
        return false;
    }
    // collections calculate the hash value for a given key
    // using the hashCode() method.
    public int hashCode()
    {
       
        // This method is definitely required in order to
        // check whether any two elements/objects are equal
        int tmp = 0;
        tmp = (id + skillName).hashCode();
        return tmp;
    }
}

D. Archivo: Mapeo de Hibernate

Como tenemos relaciones de muchos a muchos, se requiere el elemento <set> para definir la regla.

El elemento <set> establece la relación entre dos tablas. Aquí está entre las clases » GeekEmployeeData » y » SkillsetData «. Necesitamos configurar el » atributo en cascada para guardar-actualizar » para decirle a Hibernate que persista los objetos «SkillsetData» para GUARDAR. es decir, las operaciones «CREATE» y «UPDATE» deben ocurrir al mismo tiempo siempre que haya cambios en los objetos «GeekEmployeeData».

  • El atributo «nombre» se establece en la variable Set definida en la clase principal, en nuestro caso, tenemos que referirnos a «SkillsetData».
  • Para cada variable de «conjunto», necesitamos definir un elemento de conjunto separado en el archivo de mapeo.
  • Aquí usamos el atributo «nombre» para establecer el nombre de la tabla intermedia en » geekEmployeeSkill «.

E. geekEmployeeData.hbm.xml

XML

<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping>  <!-- Root element -->
  <!-- Specific mapping from java class to corresponding table -->
  <class name = "com.geeksforgeeks.GeekEmployeeData" table = "geekEmployeeData">
      <!-- Optional, we can keep description of the information -->
      <meta attribute = "class-description">
         This class contains the geekEmployeeData detail.
      </meta>
      <!--To represent Primary key of the table -->
      <id name = "id" type = "int" column = "id">
         <generator class="native"/>
      </id>
       
      <!--  This is very important
       The <set> element sets the relationship between two tables. Here it is between GeekEmployeeData and SkillsetData classes
       We need to set "cascade attribute to save-update" to tell Hibernate to persist the SkillsetData objects for SAVE
       i.e. CREATE and UPDATE operations at the same time as the GeekEmployeeData objects.
       The name attribute is set to the defined Set variable in the parent class, in our case it is SkillsetData.
       For each set variable, we need to define a separate set element in the mapping file.
       Here we used name attribute to set the intermediate table name to geekEmployeeSkill.
        -->
      <set name = "skillSets" cascade="save-update" table="geekEmployeeSkill">
         <key column = "geekEmpId"/>
         <many-to-many column = "skillId" class="com.geeksforgeeks.SkillsetData"/>
      </set>     
       
      <property name = "firstName" column = "first_name" type = "string"/>
      <property name = "lastName" column = "last_name" type = "string"/>
      <property name = "salary" column = "salary" type = "int"/>
       
   </class>
    
   <class name = "com.geeksforgeeks.SkillsetData" table = "SkillsetData">
       
      <meta attribute = "class-description">
         This class contains the skillset records.
      </meta>
       
      <id name = "id" type = "int" column = "id">
         <generator class="native"/>
      </id>
       
      <property name = "skillName" column = "skillName" type = "string"/>
       
   </class>
</hibernate-mapping>
<mapeo de hibernación> Elemento raíz
<clase> Asignaciones respectivas de una clase Java a una tabla de base de datos
<meta> Uno opcional. Usado para descripción 
<identificación>

mapeo del atributo de ID único en una clase a la clave principal de la tabla de base de datos correspondiente. 

atributo de nombre del elemento id -> A la propiedad en la clase 

atributo de  columna -> A la columna en la tabla de la base de datos.

atributo de tipo -> Contiene el tipo de mapeo de hibernación, este tipo de mapeo solo se convertirá de Java a tipo de datos SQL.

<generador> de <id> Genera valores de clave principal automáticamente.
<propiedad> mapeo de una propiedad de clase Java a su columna correspondiente en la tabla de la base de datos.
<conjunto>

Este es muy importante ya que solo establece la relación entre «geekEmployeeData» y «SkillsetData».

El atributo » cascada » se ha configurado para guardar-actualizar-> Hibernate conservará los objetos «SkillsetData» para las operaciones GUARDAR, es decir, CREAR y ACTUALIZAR, al mismo tiempo que los objetos «geekEmployeeData». La concurrencia debe obtenerse a través de este

El atributo » name » se establece en la variable Set definida en la clase principal, aquí representa «SkillsetData». 

Para cada variable establecida, el atributo «nombre» es para establecer el nombre de la tabla intermedia en «geekEmployeeSkill».

<clave> Representa una columna en la tabla «geekEmployeeSkill». Allí tenemos una clave externa para el objeto principal, es decir. tabla «geekEmployeeData» y enlaces a «skillId» en la tabla «SkillsetData».
<muchos a muchos>

Un objeto «geekEmployeeData» se relaciona con muchos «SkillsetData» 

El atributo de columna se usa para vincular el intermedio «geekEmployeeSkill»

Veamos el archivo de configuración principal donde necesitamos especificar la conectividad MySQL y el archivo hbm de referencia

F. Archivo: hibernate.cfg.xml

XML

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.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.url">jdbc:mysql://localhost:3306/geeksforgeeks</property>
        <property name="hibernate.connection.username">root</property>
        <!-- Change your appropriate password here -->
        <property name="hibernate.connection.password">xxxx</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">update </property>
        <mapping resource="geekEmployeeData.hbm.xml" />
    </session-factory>
</hibernate-configuration>

G. Archivo: GeekEmployeeManytoManyExample.java

Dividamos el archivo con métodos individuales. Necesitamos agregar algunos registros a la tabla, a saber, la tabla «geekEmployeeData, SkillsetData y geekEmployeeSkill». Al usar HashSet, podemos agregar los conjuntos de habilidades uno por uno como se muestra en el siguiente ejemplo de la siguiente manera: 

Ejemplo:

Java

// Java Program to Illustrate GeekEmployeeManytoManyExample
// Class
 
// Importing required classes
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.LogicalExpression;
import org.hibernate.criterion.Restrictions;
 
// Class
public class GeekEmployeeManytoManyExample {
 
    // Class data member
    private static SessionFactory factory;
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // Try block to check for exceptions
        try {
 
            factory = new Configuration()
                          .configure()
                          .buildSessionFactory();
        }
 
        // Catch block to handle exceptions
        catch (Throwable ex) {
 
            // Display command when exception occurred
            System.err.println(
                "Failed to create sessionFactory object."
                + ex);
            throw new ExceptionInInitializerError(ex);
        }
 
        GeekEmployeeManytoManyExample geekEmployeeObject
            = new GeekEmployeeManytoManyExample();
 
        // Let us have a set of skills for few employees
        HashSet skillSets = new HashSet();
 
        skillSets.add(new SkillsetData("Java"));
        skillSets.add(new SkillsetData("Python"));
        skillSets.add(new SkillsetData("R Programming"));
 
        HashSet databaseSkillSets = new HashSet();
 
        databaseSkillSets.add(new SkillsetData("MySQL"));
        databaseSkillSets.add(
            new SkillsetData("SQL Server"));
        databaseSkillSets.add(new SkillsetData("MongoDB"));
 
        HashSet generalSkillset = new HashSet();
        generalSkillset.add(skillSets);
        generalSkillset.add(databaseSkillSets);
 
        // Add few employee records in database
        Integer empID1 = geekEmployeeObject.addEmployee(
            "GeekA", "GeekA", 100000, skillSets);
        Integer empID2 = geekEmployeeObject.addEmployee(
            "GeekB", "GeekB", 50000, databaseSkillSets);
        Integer empID3 = geekEmployeeObject.addEmployee(
            "GeekC", "GeekC", 50000, skillSets);
    }
 
    /* Method to CREATE an employee in the database. Inserts
data in
geekEmployeeData, SkillsetData and geekEmployeeSkill table
*/
    public Integer addEmployee(String fname, String lname,
                               int salary, Set skillSets)
    {
        Session session = factory.openSession();
        Transaction tx = null;
        Integer employeeID = null;
 
        try {
            tx = session.beginTransaction();
            GeekEmployeeData employee
                = new GeekEmployeeData(fname, lname,
                                       salary);
            employee.setSkillSets(skillSets);
            employeeID = (Integer)session.save(employee);
            tx.commit();
        }
        catch (HibernateException e) {
            if (tx != null)
                tx.rollback();
            e.printStackTrace();
        }
 
        // finally block
        finally {
 
            // Closing the sessions
            // using close() method
            session.close();
        }
 
        return employeeID;
    }
}

Al ejecutar el código anterior, podemos ver los resultados en la consola.

Ejecución de código:

ParalelamenteComprobando los valores de la tabla: 

datos de geekemployeeskill

Hagamos una lista de los registros utilizando la consulta de criterios de hibernación.

Al usar «Establecer», los datos se almacenan, mientras que la recuperación también necesita usar «Establecer» para lograrlo. Como hemos usado el mapeo de hibernación para tener <set>, es más fácil obtener los conjuntos de habilidades. Hibernate ayuda a lograrlo fácilmente. No necesitamos escribir consultas internas complejas para lograrlo. Como hemos configurado “show_sql” como verdadero, en la consola podemos ver la consulta generada. Se muestra en la captura de pantalla a continuación.

Ejemplo:

Java

// Java Program to List the Records by
// Using Hibernate Criteria Query
 
// Method
public void listGeekEmployeeData()
{
 
    Session session = factory.openSession();
    Transaction tx = null;
 
    // Try block to check for exceptions
    try {
        tx = session.beginTransaction();
 
        Criteria geekEmployeeCriteria
            = session.createCriteria(
                GeekEmployeeData.class);
        List geekEmployeeList = geekEmployeeCriteria.list();
 
        for (Iterator iterator
             = geekEmployeeList.iterator();
             iterator.hasNext();) {
 
            GeekEmployeeData employeeData
                = (GeekEmployeeData)iterator.next();
            System.out.print("First Name: "
                             + employeeData.getFirstName());
            System.out.print("  Last Name: "
                             + employeeData.getLastName());
            System.out.println("  Salary: "
                               + employeeData.getSalary());
 
            // As we have used Set to store data, during
            // display
            // also we need to use the set
 
            // Iterate and get the skills one by one.
            // An employee may have more than 1 skill
            Set skillSets = employeeData.getSkillSets();
 
            for (Iterator it = skillSets.iterator();
                 it.hasNext();) {
                SkillsetData skillName
                    = (SkillsetData)it.next();
                System.out.println(
                    "SkillName: "
                    + skillName.getSkillName());
            }
        }
 
        tx.commit();
    }
 
    // Catch block to handle exceptions
    catch (HibernateException e) {
 
        if (tx != null)
            tx.rollback();
        e.printStackTrace();
    }
 
    // finally block which will execute for sure
    finally {
 
        // Closing all sessions
        // using close() method
        session.close();
    }
}

Java

/* List down all the employees by using Hibernate criteria */
geekEmployeeObject.listGeekEmployeeData();

Al enumerar los datos de los empleados, incluso podemos filtrar los datos que tienen relaciones de muchos a muchos. A través del siguiente código, podemos obtener los detalles de geekemployee que gana más de 50000 y su nombre que debe comenzar con Geek, y el resto puede ser cualquier cosa. Las “restricciones” se están acostumbrando para lograr eso. Como estamos agregando 2 condiciones, se establece con «Y» en el medio. Aquí también, al usar «Conjunto», podemos enumerar los conjuntos de habilidades de los empleados filtrados solos.

Ejemplo

Java

// Java Program to illustrate Listing Employee Data
 
// Method
// Listing gfgEmployee Data
// Whose firstname like certain name
// and salary > certain value
 
// We can combine expressions using 'And','Or'
public void listGeekEmployeesByNameAndSalaryCriteria()
{
    Session session = factory.openSession();
    Transaction tx = null;
 
    // Try block to check for exceptions
    try {
        tx = session.beginTransaction();
 
        // This will simply return every object that
        // corresponds to the GeekEmployee class.
 
        Criteria geekEmployeeCriteria
            = session.createCriteria(
                GeekEmployeeData.class);
 
        // Here 2 expectations are there one with salary and
        // second one is name.
        // Both are expected to be present. Let us see how
        // to do that
        Criterion salaryExpectation
            = Restrictions.gt("salary", 50000);
 
        Criterion nameExpectation
            = Restrictions.ilike("firstName", "Geek%");
 
        LogicalExpression logicalAndExpression
            = Restrictions.and(salaryExpectation,
                               nameExpectation);
        geekEmployeeCriteria.add(logicalAndExpression);
 
        // As a list we can collect them and can iterate
        List geekEmployeeList = geekEmployeeCriteria.list();
 
        for (Iterator iterator
             = geekEmployeeList.iterator();
             iterator.hasNext();) {
            GeekEmployeeData employeeData
                = (GeekEmployeeData)iterator.next();
 
            // Print statements
            System.out.print("First Name: "
                             + employeeData.getFirstName());
            System.out.print("  Last Name: "
                             + employeeData.getLastName());
            System.out.println("  Salary: "
                               + employeeData.getSalary());
 
            Set skillSets = employeeData.getSkillSets();
 
            for (Iterator it = skillSets.iterator();
                 it.hasNext();) {
                SkillsetData skillName
                    = (SkillsetData)it.next();
 
                // Print statement
                System.out.println(
                    "SkillName: "
                    + skillName.getSkillName());
            }
        }
        tx.commit();
    }
 
    // Catch block to handle exceptions
    catch (HibernateException e) {
        if (tx != null)
            tx.rollback();
 
        // Display exception along with line number
        // using printStacktrace() method
        e.printStackTrace();
    }
 
    // finally block which will execute for sure
    finally {
 
        // Closing the connections
        // to avoid memory leakage
        session.close();
    }
}

Java

// Let us add filter data by using Restrictions
geekEmployeeObject.listGeekEmployeesByNameAndSalaryCriteria();

Vídeo explicación:

Conclusión: al escribir consultas complejas, múltiples uniones internas hacen que la recuperación de datos en RDBMS. Lo mismo se puede lograr en hibernación de una manera más fácil y fácil de usar. Siempre que exista la necesidad de conectar más de 1 mesa, se deben formar relaciones. Hibernate hace que la relación sea más fácil.

Publicación traducida automáticamente

Artículo escrito por priyarajtt y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *