Monday, 21 November 2022

Hibernate 6: How to handle string data

Hibernate maps string data to JDBC VARCHAR by default.

// will be mapped using VARCHAR
private String str1;

How to map a String variable to CLOB column?

CLOB data type used to store large textual data. Using @Lob annotation, we can map the string type data to a CLOB column.

@Lob
String clobString;

How to specify the maximum length of the string?

Using ‘length’ attribute of @Column annotation, we can specify the maximum length of this string.

@Column(length=10)
private String strSpecifyLength;

 

How to specify the string length to use maximum?

Using length attribute of @Column annotation.

@Column(length=Length.LONG)
private String strLengthMax1;

Length.LONG32 Used to select a variable-length SQL type large enough to contain values of maximum length 32600.

 

Alternatively, you can specify the maximum length using JDBC type LONGVARCHAR.
@JdbcTypeCode(Types.LONGVARCHAR)
private String strLengthMax2;

 

'Types.LONGVARCHAR' is equivalent to a VARCHAR mapping with default length=Length.LONG.

 

 

What happen when the length I specified to a String type doesn’t fit in the largest VARCHAR column supported by your database?

Hibernate automatically upgrade the column type to TEXT, CLOB, or whatever is the equivalent type for your database.

 

For example,

 

@Entity
@Table(name = "string_type_demo")
public class StringTypeDemo {

    @Id
    private Integer id;

    // will be mapped using VARCHAR
    private String str1;

    @Lob
    private String clobStr;

    @Column(length = 10)
    private String strLength10;

    @Column(length = Length.LONG)
    private String strLengthMax1;

    @JdbcTypeCode(Types.LONGVARCHAR)
    private String strLengthMax2;

    @Column(length = Length.LONG32)
    private String strLengthMax3;

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

 

Above snippet generates below DDL.

create table string_type_demo (
    id integer not null,
    clobStr oid,
    str1 varchar(255),
    strLength10 varchar(10),
    strLengthMax1 varchar(32600),
    strLengthMax2 varchar(32600),
    strLengthMax3 text,
    primary key (id)
)

 

Find the below working application.

 

Step 1: Create new maven project ‘hibernate-string-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-string-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 entity class.

 

StringTypeDemo.java
package com.sample.app.entity;

import java.sql.Types;

import org.hibernate.Length;
import org.hibernate.annotations.JdbcTypeCode;

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

@Entity
@Table(name = "string_type_demo")
public class StringTypeDemo {

    @Id
    private Integer id;

    // will be mapped using VARCHAR
    private String str1;

    @Lob
    private String clobStr;

    @Column(length = 10)
    private String strLength10;

    @Column(length = Length.LONG)
    private String strLengthMax1;

    @JdbcTypeCode(Types.LONGVARCHAR)
    private String strLengthMax2;

    @Column(length = Length.LONG32)
    private String strLengthMax3;

    public StringTypeDemo(Integer id, String str1, String clobStr, String strLength10, String strLengthMax1,
            String strLengthMax2, String strLengthMax3) {
        this.id = id;
        this.str1 = str1;
        this.clobStr = clobStr;
        this.strLength10 = strLength10;
        this.strLengthMax1 = strLengthMax1;
        this.strLengthMax2 = strLengthMax2;
        this.strLengthMax3 = strLengthMax3;
    }

    public Integer getId() {
        return id;
    }

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

    public String getStr1() {
        return str1;
    }

    public void setStr1(String str1) {
        this.str1 = str1;
    }

    public String getClobStr() {
        return clobStr;
    }

    public void setClobStr(String clobStr) {
        this.clobStr = clobStr;
    }

    public String getStrLength10() {
        return strLength10;
    }

    public void setStrLength10(String strLength10) {
        this.strLength10 = strLength10;
    }

    public String getStrLengthMax1() {
        return strLengthMax1;
    }

    public void setStrLengthMax1(String strLengthMax1) {
        this.strLengthMax1 = strLengthMax1;
    }

    public String getStrLengthMax2() {
        return strLengthMax2;
    }

    public void setStrLengthMax2(String strLengthMax2) {
        this.strLengthMax2 = strLengthMax2;
    }

    public String getStrLengthMax3() {
        return strLengthMax3;
    }

    public void setStrLengthMax3(String strLengthMax3) {
        this.strLengthMax3 = strLengthMax3;
    }

    @Override
    public String toString() {
        return "StringTypeDemo [id=" + id + ", str1=" + str1 + ", clobStr=" + clobStr + ", strLength10=" + strLength10
                + ", strLengthMax1=" + strLengthMax1 + ", strLengthMax2=" + strLengthMax2 + ", strLengthMax3="
                + strLengthMax3 + "]";
    }

}

 

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

    </session-factory>

</hibernate-configuration>

 

Step 5: Define main application class.

 

App.java
package com.sample.app;

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 com.sample.app.entity.StringTypeDemo;

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> type, Session session) {
        CriteriaBuilder builder = session.getCriteriaBuilder();
        CriteriaQuery<T> criteria = builder.createQuery(type);
        criteria.from(type);
        List<T> data = session.createQuery(criteria).getResultList();
        return data;
    }

    public static void main(final String args[]) {

        String str1 = "Hello!!!";
        StringTypeDemo p1 = new StringTypeDemo(1, str1, str1, str1, str1, str1, str1);

        try (final Session session = SESSION_FACTORY.openSession()) {
            session.beginTransaction();

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

            List<StringTypeDemo> records = loadAllData(StringTypeDemo.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 16, 2022 3:14:14 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Aug 16, 2022 3:14:15 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Aug 16, 2022 3:14:15 PM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.StringTypeDemo (class must be instantiated by Interceptor)
Aug 16, 2022 3:14:15 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@7c2dfa2] 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 string_type_demo (
       id integer not null,
        clobStr oid,
        str1 varchar(255),
        strLength10 varchar(10),
        strLengthMax1 varchar(32600),
        strLengthMax2 varchar(32600),
        strLengthMax3 text,
        primary key (id)
    )
Aug 16, 2022 3:14:15 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
        string_type_demo
        (clobStr, str1, strLength10, strLengthMax1, strLengthMax2, strLengthMax3, id) 
    values
        (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    select
        s1_0.id,
        s1_0.clobStr,
        s1_0.str1,
        s1_0.strLength10,
        s1_0.strLengthMax1,
        s1_0.strLengthMax2,
        s1_0.strLengthMax3 
    from
        string_type_demo s1_0
StringTypeDemo [id=1, str1=Hello!!!, clobStr=Hello!!!, strLength10=Hello!!!, strLengthMax1=Hello!!!, strLengthMax2=Hello!!!, strLengthMax3=Hello!!!]

 

Query PostgreSQL to confirm the same.

test=# \d
              List of relations
 Schema |       Name       | Type  |  Owner   
--------+------------------+-------+----------
 public | string_type_demo | table | postgres
(1 row)

test=# 
test=# \d+ string_type_demo
                                                 Table "public.string_type_demo"
    Column     |           Type           | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
---------------+--------------------------+-----------+----------+---------+----------+-------------+--------------+-------------
 id            | integer                  |           | not null |         | plain    |             |              | 
 clobstr       | oid                      |           |          |         | plain    |             |              | 
 str1          | character varying(255)   |           |          |         | extended |             |              | 
 strlength10   | character varying(10)    |           |          |         | extended |             |              | 
 strlengthmax1 | character varying(32600) |           |          |         | extended |             |              | 
 strlengthmax2 | character varying(32600) |           |          |         | extended |             |              | 
 strlengthmax3 | text                     |           |          |         | extended |             |              | 
Indexes:
    "string_type_demo_pkey" PRIMARY KEY, btree (id)
Access method: heap

test=# 
test=# 
test=# SELECT * FROM string_type_demo;
 id | clobstr |   str1   | strlength10 | strlengthmax1 | strlengthmax2 | strlengthmax3 
----+---------+----------+-------------+---------------+---------------+---------------
  1 |   24934 | Hello!!! | Hello!!!    | Hello!!!      | Hello!!!      | Hello!!!
(1 row)

 

As you see above snippet, PostgresSQL modelled the column ‘clobstr’ as oid. We can use the function ‘lo_get’ to extract the data from oid.

test=# SELECT lo_get(clobstr) FROM string_type_demo;
       lo_get       
--------------------
 \x48656c6c6f212121
(1 row)

 

Decoded version of hexa decimal string ‘\x48656c6c6f212121’ is equivalent to Hello!!!.

 

You can download complete working application from this link.


 

Previous                                                    Next                                                    Home

No comments:

Post a Comment