Thursday 3 November 2022

Hibernate 6: Embedded and Embeddable example

In this post, I am going to explain how to map an entity that contain embedded properties to a single database table using Embedded and Embeddable annotation.

 

For example, I have a contact table with below ddl.

create table contact (
    id integer not null,
    age integer,
    firstName varchar(255),
    lastName varchar(255),
    middleName varchar(255),
    primary key (id)
)

 

As you see above ddl, contact has 6 properties. There are two ways to define the entity class.

a.   Mention all the six properties in Contact class (or)

b.   You can define a Name class with properties and firstName, lastName, middleName and embed the Name variable in Contact class.

Since first approach is straight forward, let’s try the second one.

 

Step 1: Define Name class and mark it as embeddable. @Embeddable annotation tells that this class is embeddable by other entities.

 

@Embeddable
public class Name {

    private String firstName;

    private String middleName;

    private String lastName;

    ...........
    ...........
}

Step 2: Define Contact entity.

@Entity
@Table(name = "contact")
public class Contact {

    @Id
    private Integer id;

    @Embedded
    private Name name;

    private Integer age;

    ...........
    ...........
}

As you see the Contact class definition, I embedded the Name variable using @Embedded annotation.

 

As a result, we have our entity Contact, embedding name details and mapping to a single database table.

 

Find the below working application.

 

Step 1: Create new maven project 'hibernate-embeddable-embedded-demo'.

 

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-embeddable-embedded-demo</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>

        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>javax.persistence-api</artifactId>
            <version>2.2</version>
        </dependency>


    </dependencies>
</project>

Step 3: 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>

        <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.Contact" />

    </session-factory>

</hibernate-configuration>

Step 4: Define Name and Contact classes.

 

Name.java

package com.sample.app.entity;

import jakarta.persistence.Embeddable;

@Embeddable
public class Name {

    private String firstName;

    private String middleName;

    private String lastName;
    
    public Name() {}

    public Name(String firstName, String middleName, String lastName) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

Contact.java

package com.sample.app.entity;

import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "contact")
public class Contact {

    @Id
    private Integer id;

    @Embedded
    private Name name;

    private Integer age;

    public Contact(Integer id, Name name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

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

    public Name getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

Step 5: Define HelloWorld application class.

 

HelloWorld.java

package com.sample.app;

import java.io.IOException;
import java.net.URISyntaxException;

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.entity.Contact;
import com.sample.app.entity.Name;

public class HelloWorld {
    private static final SessionFactory SESSION_FACTORY = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {

            final StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
                    .configure("hibernate.cfg.xml").build();

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

            return metaData.getSessionFactoryBuilder().build();

        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }

    }

    public static void main(final String args[]) throws ClassNotFoundException, IOException, URISyntaxException {
        final Contact contact1 = new Contact(1, new Name("Krishna", null, "G"), 34);
        final Contact contact2 = new Contact(2, new Name("Ram", "krishna", "Geeta"), 39);

        try (final Session session = SESSION_FACTORY.openSession()) {
            session.beginTransaction();
            session.persist(contact1);
            session.persist(contact2);
            session.getTransaction().commit();
        }

    }
}

Total project structure looks like below.




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

Aug 08, 2022 8:30:46 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 08, 2022 8:30:46 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Aug 08, 2022 8:30:47 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Aug 08, 2022 8:30:47 PM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Contact (class must be instantiated by Interceptor)
Hibernate: 
    
    drop table if exists contact cascade
Aug 08, 2022 8:30:47 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@95eb320] 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 contact (
       id integer not null,
        age integer,
        firstName varchar(255),
        lastName varchar(255),
        middleName varchar(255),
        primary key (id)
    )
Aug 08, 2022 8:30:47 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@4fe64d23] 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.
Aug 08, 2022 8:30:47 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
        contact
        (age, firstName, lastName, middleName, id) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        contact
        (age, firstName, lastName, middleName, id) 
    values
        (?, ?, ?, ?, ?)

You can confirm the same by querying postgres.

test=# \d+ contact;
                                                   Table "public.contact"
   Column   |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
------------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id         | integer                |           | not null |         | plain    |             |              | 
 age        | integer                |           |          |         | plain    |             |              | 
 firstname  | character varying(255) |           |          |         | extended |             |              | 
 lastname   | character varying(255) |           |          |         | extended |             |              | 
 middlename | character varying(255) |           |          |         | extended |             |              | 
Indexes:
    "contact_pkey" PRIMARY KEY, btree (id)
Access method: heap

test=# 
test=# SELECT * FROM contact;
 id | age | firstname | lastname | middlename 
----+-----+-----------+----------+------------
  1 |  34 | Krishna   | G        | 
  2 |  39 | Ram       | Geeta    | krishna
(2 rows)

You can download the complete working application from this link.

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment