Friday 23 December 2022

Hibernate 6: Map a property to XML

Below snippet maps a property to SQL xml type.

@JdbcTypeCode(SqlTypes.SQLXML)
private Map<String, Object> config;

 

To use this feature, you need to specify the XML library that is used for serialization and deserialization.

 

How to specify the XML formatter implementation?

Set the XML formatter, by adding the property 'hibernate.type.xml_format_mapper' to hibernate config. At the time of writing this post, you can set jackson-xml or jaxb to this property.

<property name="hibernate.type.xml_format_mapper">jackson-xml</property>

 

Find the below working application.

 

Step 1: Create new maven project ‘hibernate-xml-mapping-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-xml-mapping-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>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.13.3</version>
        </dependency>


    </dependencies>
</project>

 

Step 3: Define AppConfig entity.

 

AppConfig.java

package com.sample.app.entity;

import java.util.Map;

import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

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

@Entity
@Table(name = "app_config")
public class AppConfig {
    @Id
    private Integer id;

    @JdbcTypeCode(SqlTypes.SQLXML)
    private Map<String, Object> config;

    public AppConfig(Integer id, Map<String, Object> config) {
        this.id = id;
        this.config = config;
    }

    public Integer getId() {
        return id;
    }

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

    public Map<String, Object> getConfig() {
        return config;
    }

    public void setConfig(Map<String, Object> config) {
        this.config = config;
    }

    @Override
    public String toString() {
        return "AppConfig [id=" + id + ", config=" + config + "]";
    }

}

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>
        
        <property name="hibernate.type.xml_format_mapper">jackson-xml</property>

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

    </session-factory>

</hibernate-configuration>

Step 5: Define main application class.

 

App.java

package com.sample.app;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.AppConfig;

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[]) {

        Map<String, Object> config = new HashMap<>();
        config.put("maxThreads", 10);
        config.put("storagePath", "/Users/krishna/my-docs");

        AppConfig appConfig = new AppConfig(1, config);

        try (Session session = sessionFactory.openSession()) {
            session.beginTransaction();
            session.persist(appConfig);
            session.flush();
            session.getTransaction().commit();

            List<AppConfig> appConfigs = loadAllData(AppConfig.class, session);
            for (AppConfig appConfig1 : appConfigs) {
                System.out.println(appConfig1);
            }

        }

    }
}

Total project structure looks like below.

 


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

Aug 19, 2022 3:15:33 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate ORM core version 6.1.2.Final
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using built-in connection pool (not intended for production use)
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: Loaded JDBC driver class: org.postgresql.Driver
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001012: Connecting with JDBC URL [jdbc:postgresql://127.0.0.1:5432/test]
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {password=****, user=postgres}
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections <init>
INFO: HHH10001115: Connection pool size: 20 (min=1)
Aug 19, 2022 3:15:34 PM org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl logSelectedDialect
INFO: HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
Aug 19, 2022 3:15:35 PM org.hibernate.metamodel.internal.EntityInstantiatorPojoStandard resolveConstructor
INFO: HHH000182: No default (no-argument) constructor for class: com.sample.app.entity.AppConfig (class must be instantiated by Interceptor)
Aug 19, 2022 3:15:35 PM org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl getIsolatedConnection
INFO: HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@de7e193] 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 app_config (
       id integer not null,
        config xml,
        primary key (id)
    )
Aug 19, 2022 3:15:35 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
        app_config
        (config, id) 
    values
        (?, ?)
Hibernate: 
    select
        a1_0.id,
        a1_0.config 
    from
        app_config a1_0
AppConfig [id=1, config={storagePath=/Users/krishna/my-docs, maxThreads=10}]

Query PostgreSQL and confirm the same.

test=# \d+ app_config
                                        Table "public.app_config"
 Column |  Type   | Collation | Nullable | Default | Storage  | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+----------+-------------+--------------+-------------
 id     | integer |           | not null |         | plain    |             |              | 
 config | xml     |           |          |         | extended |             |              | 
Indexes:
    "app_config_pkey" PRIMARY KEY, btree (id)
Access method: heap

test=# 
test=# 
test=# 
test=# SELECT * FROM app_config;
 id |                                         config                                          
----+-----------------------------------------------------------------------------------------
  1 | <Map><storagePath>/Users/krishna/my-docs</storagePath><maxThreads>10</maxThreads></Map>
(1 row)

You can download the application from this link.



Previous                                                    Next                                                    Home

No comments:

Post a Comment