Saturday, 31 December 2022

Hibernate 6: Working with bags

What is bag?

Bag is an unordered collection, which can contain duplicate elements.

 

Bag vs Set

Bag can contain duplicates, whereas set do not contain duplicates.

 

Bag vs List

Bag is an unordered collection, whereas list is a ordered collection.

 

You can considered a bag as unordered list or a set with duplicates.

 

Hibernate provide two ways to handle bags.

 

Approach 1: Without any additional mapping influencers, java.util.Collection is interpreted using BAG by default.

@ElementCollection
private Collection<NickName> nickNames1;

 

Approach 2: You can model a List of elements as Bag using @Bag annotation.

@ElementCollection
@Bag
private List<NickName> nickNames2;

 

Even though the nickNames2 attribute is defined as List, Hibernate will treat it using the BAG semantics.

 

Find the below working application.

 

Step 1: Create new maven project ‘hibernate-map-bag’.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample.app</groupId>
    <artifactId>hibernate-map-bag</artifactId>
    <version>1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <java.version>15</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>

    </properties>

    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.1.2.Final</version>
        </dependency>


    </dependencies>
</project>

 

Step 3: Define NickName model class.

 

NickName.java

 

package com.sample.app.dto;

import java.util.Objects;

import jakarta.persistence.Embeddable;

@Embeddable
public class NickName {

    private String name;

    public NickName() {
    }

    public NickName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "NickName [name=" + name + "]";
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        NickName other = (NickName) obj;
        return Objects.equals(name, other.name);
    }

}

 

Step 4: Define Employee entity class.

 

Employee.java

package com.sample.app.entity;

import java.util.Collection;
import java.util.List;

import org.hibernate.annotations.Bag;

import com.sample.app.dto.NickName;

import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @Column(name = "employee_id")
    private int id;

    private String name;

    @ElementCollection
    private Collection<NickName> nickNames1;

    @ElementCollection
    @Bag
    private List<NickName> nickNames2;

    public Employee() {
    }

    public Employee(int id, String name, Collection<NickName> nickNames1, List<NickName> nickNames2) {
        this.id = id;
        this.name = name;
        this.nickNames1 = nickNames1;
        this.nickNames2 = nickNames2;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Collection<NickName> getNickNames1() {
        return nickNames1;
    }

    public void setNickNames1(Collection<NickName> nickNames1) {
        this.nickNames1 = nickNames1;
    }

    public List<NickName> getNickNames2() {
        return nickNames2;
    }

    public void setNickNames2(List<NickName> nickNames2) {
        this.nickNames2 = nickNames2;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", nickNames1=" + nickNames1 + ", nickNames2=" + nickNames2
                + "]";
    }

}

Step 5: Create hibernate.cfg.xml file under src/main/resources folder.

 

hibernate.cfg.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>

        <!-- Database Connection settings -->
        <property name="connection.driver_class">org.postgresql.Driver</property>
        <property name="connection.url">jdbc:postgresql://127.0.0.1:5432/test</property>
        <property name="connection.username">postgres</property>
        <property name="connection.password">postgres</property>

        <!-- Enable the logging of all the generated SQL statements to the console -->
        <property name="show_sql">true</property>

        <!-- Format the generated SQL statement to make it more readable, -->
        <property name="format_sql">true</property>

        <!-- Hibernate will put comments inside all generated SQL statements to 
            hint what’s the generated SQL trying to do -->
        <property name="use_sql_comments">false</property>

        <!-- This property makes Hibernate generate the appropriate SQL for the 
            chosen database. -->
        <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>

        <!-- mappings for annotated classes -->
        <mapping class="com.sample.app.entity.Employee" />

    </session-factory>

</hibernate-configuration>

Step 6: Define main application class.

 

App.java

package com.sample.app;

import java.util.*;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import com.sample.app.dto.NickName;
import com.sample.app.entity.Employee;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;

public class App {
    private static SessionFactory sessionFactory = buildSessionFactory();

    private static <T> List<T> loadAllData(Class<T> clazz, Session session) {
        final CriteriaBuilder builder = session.getCriteriaBuilder();
        final CriteriaQuery<T> criteria = builder.createQuery(clazz);
        criteria.from(clazz);
        return session.createQuery(criteria).getResultList();
    }

    private static SessionFactory buildSessionFactory() {
        try {
            if (sessionFactory == null) {
                StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
                        .configure("hibernate.cfg.xml").build();

                Metadata metaData = new MetadataSources(standardRegistry).getMetadataBuilder().build();

                sessionFactory = metaData.getSessionFactoryBuilder().build();
            }
            return sessionFactory;
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static void main(String args[]) {

        try (Session session = sessionFactory.openSession()) {

            session.beginTransaction();

            List<NickName> nickNames = new ArrayList<>();
            nickNames.add(new NickName("Nandakumara"));
            nickNames.add(new NickName("Rishikesha"));
            nickNames.add(new NickName("Rishikesha"));
            nickNames.add(new NickName("Sri Krishna"));
            nickNames.add(new NickName("Nandakumara"));

            Employee emp = new Employee(1, "Krishna", nickNames, nickNames);
            session.persist(emp);
            session.flush();
            session.getTransaction().commit();

            List<Employee> employees = loadAllData(Employee.class, session);
            for (Employee emp1 : employees) {
                System.out.println(emp1);
            }

        }

    }
}

Total project structure looks like below.




 

Run App.java, you will see below messages in the console.

 

Sep 13, 2022 2:19:22 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Sep 13, 2022 2:19:22 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Hibernate: 
    
    alter table if exists Employee_nickNames1 
       drop constraint if exists FKcvwmfmag06lrw3km01evx4x15
Sep 13, 2022 2:19:23 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@45e9b12d] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: 
    
    alter table if exists Employee_nickNames2 
       drop constraint if exists FKkm3itq30094na7fgtd956xf7d
Hibernate: 
    
    drop table if exists employee cascade
Hibernate: 
    
    drop table if exists Employee_nickNames1 cascade
Hibernate: 
    
    drop table if exists Employee_nickNames2 cascade
Hibernate: 
    
    create table employee (
       employee_id integer not null,
        name varchar(255),
        primary key (employee_id)
    )
Sep 13, 2022 2:19:23 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@513b52af] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
Hibernate: 
    
    create table Employee_nickNames1 (
       Employee_employee_id integer not null,
        name varchar(255)
    )
Hibernate: 
    
    create table Employee_nickNames2 (
       Employee_employee_id integer not null,
        name varchar(255)
    )
Hibernate: 
    
    alter table if exists Employee_nickNames1 
       add constraint FKcvwmfmag06lrw3km01evx4x15 
       foreign key (Employee_employee_id) 
       references employee
Hibernate: 
    
    alter table if exists Employee_nickNames2 
       add constraint FKkm3itq30094na7fgtd956xf7d 
       foreign key (Employee_employee_id) 
       references employee
Sep 13, 2022 2:19:23 PM org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Hibernate: 
    insert 
    into
        employee
        (name, employee_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames1
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames1
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames1
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames1
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames1
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames2
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames2
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames2
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames2
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee_nickNames2
        (Employee_employee_id, name) 
    values
        (?, ?)
Hibernate: 
    select
        e1_0.employee_id,
        e1_0.name 
    from
        employee e1_0
Employee [id=1, name=Krishna, nickNames1=[NickName [name=Nandakumara], NickName [name=Rishikesha], NickName [name=Rishikesha], NickName [name=Sri Krishna], NickName [name=Nandakumara]], nickNames2=[NickName [name=Nandakumara], NickName [name=Rishikesha], NickName [name=Rishikesha], NickName [name=Sri Krishna], NickName [name=Nandakumara]]]

Query database to confirm the DDL.

test=# \d+ employee;
                                                   Table "public.employee"
   Column    |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
-------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 employee_id | integer                |           | not null |         | plain    |             |              | 
 name        | character varying(255) |           |          |         | extended |             |              | 
Indexes:
    "employee_pkey" PRIMARY KEY, btree (employee_id)
Referenced by:
    TABLE "employee_nicknames1" CONSTRAINT "fkcvwmfmag06lrw3km01evx4x15" FOREIGN KEY (employee_employee_id) REFERENCES employee(employee_id)
    TABLE "employee_nicknames2" CONSTRAINT "fkkm3itq30094na7fgtd956xf7d" FOREIGN KEY (employee_employee_id) REFERENCES employee(employee_id)
Access method: heap

test=# 
test=# 
test=# 
test=# 
test=# \d+ employee_nicknames1;
                                                  Table "public.employee_nicknames1"
        Column        |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
----------------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 employee_employee_id | integer                |           | not null |         | plain    |             |              | 
 name                 | character varying(255) |           |          |         | extended |             |              | 
Foreign-key constraints:
    "fkcvwmfmag06lrw3km01evx4x15" FOREIGN KEY (employee_employee_id) REFERENCES employee(employee_id)
Access method: heap

test=# 
test=# 
test=# 
test=# 
test=# \d+ employee_nicknames2;
                                                  Table "public.employee_nicknames2"
        Column        |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
----------------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 employee_employee_id | integer                |           | not null |         | plain    |             |              | 
 name                 | character varying(255) |           |          |         | extended |             |              | 
Foreign-key constraints:
    "fkkm3itq30094na7fgtd956xf7d" FOREIGN KEY (employee_employee_id) REFERENCES employee(employee_id)
Access method: heap

Query tables to confirm the data.

test=# SELECT * FROM employee;
 employee_id |  name   
-------------+---------
           1 | Krishna
(1 row)

test=# 
test=# 
test=# SELECT * FROM employee_nicknames1;
 employee_employee_id |    name     
----------------------+-------------
                    1 | Nandakumara
                    1 | Rishikesha
                    1 | Rishikesha
                    1 | Sri Krishna
                    1 | Nandakumara
(5 rows)

test=# 
test=# 
test=# SELECT * FROM employee_nicknames2;
 employee_employee_id |    name     
----------------------+-------------
                    1 | Nandakumara
                    1 | Rishikesha
                    1 | Rishikesha
                    1 | Sri Krishna
                    1 | Nandakumara
(5 rows)

You can download this application from this link.

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment