Friday, 25 November 2022

Hibernate 6: work with character arrays

Hibernate maps the character arrays to a VARCHAR by default.

// mapped as VARCHAR
char[] arr1;
Character[] arr2;

 

Can I map a character array to CLOB?

Yes, using LOB annotation, you can map a character array to CLOB.

// mapped as CLOB
@Lob
char[] arr3;
@Lob
Character[] arr4;

How to map a character array to NVARCHAR?

By annotating the character array with @Nationalized annotation, we can map a character array to NVARCHAR.

// mapped as NVARCHAR
@Nationalized
char[] arr5;
@Nationalized
Character[] arr6;

How to map a character array to NCLOB?

Any character array that is annotated with @Nationalized and @Lob annotation will be mapped using NCLOB.

// mapped as NCLOB
@Lob
@Nationalized
char[] arr7;
@Lob
@Nationalized
Character[] arr8;

@Nationalized annotation specifies that the annotated character data should be stored with nationalization support. This annotation depends on the Dialect#getNationalizationSupport() method.

 

a.   Some databases support storing nationalized data using their "normal" character data types CHAR, VARCHAR, CLOB. For these dialects, this annotation is effectively ignored.

b.   Other databases support storing nationalized data only via the specialized, standard SQL variants NCHAR, NVARCHAR, NCLOB. For these dialects, this annotation will adjust the JDBC type code to use the specialized variant.

 

Find the below working application.

 

Step 1: Create new maven project ‘hibernate-char-array-demo’.

 

Step 2: Create 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-char-array-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: Define Demo entity class.

 

Demo.java

package com.sample.app.entity;

import org.hibernate.annotations.Nationalized;

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

@Entity
@Table(name = "demo-table")
public class Demo {
    @Id
    private int id;

    private char[] arr1;

    @Nationalized
    private char[] arr2;

    @Lob
    private char[] arr3;

    @Lob
    @Nationalized
    private char[] arr4;

    public Demo(int id, char[] arr1, char[] arr2, char[] arr3, char[] arr4) {
        this.id = id;
        this.arr1 = arr1;
        this.arr2 = arr2;
        this.arr3 = arr3;
        this.arr4 = arr4;
    }

    public int getId() {
        return id;
    }

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

    public char[] getArr1() {
        return arr1;
    }

    public void setArr1(char[] arr1) {
        this.arr1 = arr1;
    }

    public char[] getArr2() {
        return arr2;
    }

    public void setArr2(char[] arr2) {
        this.arr2 = arr2;
    }

    public char[] getArr3() {
        return arr3;
    }

    public void setArr3(char[] arr3) {
        this.arr3 = arr3;
    }

    public char[] getArr4() {
        return arr4;
    }

    public void setArr4(char[] arr4) {
        this.arr4 = arr4;
    }

}

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

        <property name="hbm2ddl.auto">update</property>

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

    </session-factory>

</hibernate-configuration>

Step 5: Define main application class.

 

App.java

package com.sample.app;

import java.sql.Types;
import java.util.List;

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 org.hibernate.dialect.Dialect;
import org.hibernate.dialect.NationalizationSupport;
import org.hibernate.engine.spi.SessionFactoryImplementor;

import com.sample.app.entity.Demo;

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

public class App {
    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);
        }

    }

    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();
    }

    public static void main(final String args[]) {
        Dialect dialect = ((SessionFactoryImplementor) SESSION_FACTORY).getJdbcServices().getDialect();
        NationalizationSupport nationalizationSupport = dialect.getNationalizationSupport();

        System.out.println("Types.CHAR : " + Types.CHAR);
        System.out.println("Types.VARCHAR : " + Types.VARCHAR);
        System.out.println("Types.LONGVARCHAR : " + Types.LONGVARCHAR);
        System.out.println("Types.CLOB : " + Types.CLOB);

        System.out.println("Types.NCHAR : " + Types.NCHAR);
        System.out.println("Types.NVARCHAR : " + Types.NVARCHAR);
        System.out.println("Types.LONGNVARCHAR : " + Types.LONGNVARCHAR);
        System.out.println("Types.NCLOB : " + Types.NCLOB);
        
        System.out.println("nationalizationSupport.getCharVariantCode() : " + nationalizationSupport.getCharVariantCode());
        System.out.println("nationalizationSupport.getClobVariantCode() : " + nationalizationSupport.getClobVariantCode());
        System.out.println("nationalizationSupport.getLongVarcharVariantCode() : " + nationalizationSupport.getLongVarcharVariantCode());
        System.out.println("nationalizationSupport.getVarcharVariantCode() : " + nationalizationSupport.getVarcharVariantCode());


        char[] arr = {'h', 'e', 'l', 'l', 'o'};
        
        Demo e1 = new Demo(1, arr, arr, arr, arr);
  
        try (final Session session = SESSION_FACTORY.openSession()) {
            session.beginTransaction();

            session.persist(e1);

            session.flush();
            session.getTransaction().commit();

            List<Demo> records = loadAllData(Demo.class, session);
            records.forEach(System.out::println);
        }

    }
}

Total project structure looks like below.




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


Aug 17, 2022 11:30:27 AM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Aug 17, 2022 11:30:28 AM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Aug 17, 2022 11:30:28 AM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.Demo (class must be instantiated by Interceptor)
Aug 17, 2022 11:30:28 AM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@ef60710] 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 "demo-table" (
       id integer not null,
        arr1 varchar(255),
        arr2 varchar(255),
        arr3 oid,
        arr4 oid,
        primary key (id)
    )
Aug 17, 2022 11:30:28 AM org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator initiateService
INFO: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
Types.CHAR : 1
Types.VARCHAR : 12
Types.LONGVARCHAR : -1
Types.CLOB : 2005
Types.NCHAR : -15
Types.NVARCHAR : -9
Types.LONGNVARCHAR : -16
Types.NCLOB : 2011
nationalizationSupport.getCharVariantCode() : 1
nationalizationSupport.getClobVariantCode() : 2005
nationalizationSupport.getLongVarcharVariantCode() : -1
nationalizationSupport.getVarcharVariantCode() : 12
Hibernate: 
    insert 
    into
        "demo-table"
        (arr1, arr2, arr3, arr4, id) 
    values
        (?, ?, ?, ?, ?)
Hibernate: 
    select
        d1_0.id,
        d1_0.arr1,
        d1_0.arr2,
        d1_0.arr3,
        d1_0.arr4 
    from
        "demo-table" d1_0
com.sample.app.entity.Demo@443faa85

As you see the console output, both CLOB and NCLOB are modelled as oid in PostgreSQL. You can confirm the same by querying PostgreSQL.

test=# \d+ demo-table;
                                               Table "public.demo-table"
 Column |          Type          | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer                |           | not null |         | plain    |             |              | 
 arr1   | character varying(255) |           |          |         | extended |             |              | 
 arr2   | character varying(255) |           |          |         | extended |             |              | 
 arr3   | oid                    |           |          |         | plain    |             |              | 
 arr4   | oid                    |           |          |         | plain    |             |              | 
Indexes:
    "demo-table_pkey" PRIMARY KEY, btree (id)
Access method: heap

You can download this application from this link.


Previous                                                    Next                                                    Home

No comments:

Post a Comment