Categories
Guides/HowTo

Integrating Hibernate 5 into a Java 8 application w/ Hikari CP.

Fair warning: You should check “Sec. 7.5 of the Hibernate ORM User Guide: Using HikariCP” first and use that, if that fits your needs.


I recently improved the database integration in the JeakBot-Framework using API interfaces for plugins to access different parts of the JPA.

For this, I wanted to have Hibernate available in the API and at the same time, provide the same persistence units using DataSources. Preferably with only one representation of persistence units and no “hacky” way of retrieving instances based on casted implementations.
As most results online tell users to just cast the Hibernate classes to their implementations, use unwrap or do other (in my opinion) convention-breaking stuff, I had a hard time finding a solution that I like.
But eventually, I found a very convenient way of doing this:
Bootstrapping Hibernate with HikariCP manually.

Here’s how I did it.


Preparations & Representation

First, I created some sort of PersistenceUnit representation class & api which will make the different configuration values available to others:

package de.fearnixx.jeak.service.database;

import javax.persistence.EntityManager;
import javax.sql.DataSource;

public interface IPersistenceUnit {

    String getUnitId();
    String getJdbcUrl();
    String getHost();
    String getPort();
    String getSchemaName();
    String getUsername();
    String getPassword();
    String getDriver();

    DataSource getDataSource();
    EntityManager getEntityManager();
}

(I’ve omitted spacing and JavaDoc as this is enough to get the drill. You can view the source on GitLab.)

Afterwards, I created the implementation class which will be responsible for initializing Hikari and finishing Hibernate bootstrapping using an initialize method.

public class HHPersistenceUnit extends Configurable implements IPersistenceUnit, AutoCloseable {
    private static final Logger logger = LoggerFactory.getLogger(HHPersistenceUnit.class);

    private final Map<String, String> dataSourceOpts = new HashMap<>();
    private final BootstrapServiceRegistry baseRegistry;
    private final String unitId;

    private boolean isClosed = false;
    private String jdbcUrl;
    private String host;
    private String port;
    private String schemaName;
    private String username;
    private String password;
    private String driver;

    private HikariDataSource hikariDS;
    private StandardServiceRegistry hibernateServiceRegistry;
    private SessionFactory hibernateSessionFactory;

    public HHPersistenceUnit(String unitId, BootstrapServiceRegistry baseRegistry) {
        this.unitId = unitId;
        this.baseRegistry = baseRegistry;
    }

    public boolean initialize() {
        if (hikariDS != null) {
            throw new IllegalStateException("Cannot re-initialize data source!");
        }

        if (loadConfig()) {
            readConfiguration();
            initializeHikariSource();
            return initializeHibernateServices();
        } else {
            return false;
        }
    }

    // === STEP ONE === //
    private void readConfiguration() {
        // Retrieve your configuration in some way.
    }

    // === STEP TWO === //
    private void initializeHikariSource() {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(getJdbcUrl());
        hikariConfig.setUsername(getUsername());
        hikariConfig.setPassword(getPassword());

        dataSourceOpts.forEach(hikariConfig::addDataSourceProperty);
        hikariDS = new HikariDataSource(hikariConfig);
    }

    // === STEP THREE === //
    private boolean initializeHibernateServices() {
        try {
            StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder(baseRegistry);
            registryBuilder.applySetting("hibernate.format_sql", "true");
            // Tell Hibernate to use Hikaris DataSource under the hood.
            registryBuilder.applySetting("hibernate.connection.datasource", hikariDS);

            hibernateServiceRegistry = registryBuilder.build();
            MetadataSources metaSources = new MetadataSources(hibernateServiceRegistry);
            for (Class<?> aClass : /* YOUR CLASSES HERE */) {
                metaSources.addAnnotatedClassName(aClass.getName());
            }
            hibernateSessionFactory = metaSources.getMetadataBuilder().build().buildSessionFactory();
            return true;
        } catch (HibernateException e) {
            logger.warn("Failed to initialize Hibernate for persistence unit: {}", unitId, e);
            return false;
        }
    }

    @Override
    public DataSource getDataSource() {
        return hikariDS;
    }

    @Override
    public EntityManager getEntityManager() {
        return hibernateSessionFactory.createEntityManager();
    }

    @Override
    public synchronized void close() throws Exception {
        if (isClosed) {
            throw new IOException("Persistence unit already closed!");
        }

        isClosed = true;
        hikariDS.close();
        hibernateSessionFactory.close();
        StandardServiceRegistryBuilder.destroy(hibernateServiceRegistry);
    }
}

(Again, I’ve omitted non-important stuff such as the getters. View the source here.)

Let me explain the workflow as I understood it and as shallow as is required just to get this running.

1. Read configuration value

The configuration is read to populate the data source settings (host, port, driver type, username, password, database name). You will have to choose your way on how to do that. The JeakBot-Framework uses my Confort library for this.

2. Initialize Hikari configuration & DS

Initializing Hikaris configuration and DataSource classes is pretty straight forward as it requires you only to set the URL in the form of jdbc:<driver>://<host>:<port>/<database-name> and username + password, in case you even need them.

Afterwards, you can let your administrators add custom datasource options by just passing them on to the configuration.

Lastly, construct a new HikariDataSource and pass the configuration to it. => That’s your DataSource implementation for low-level java.sql.Connection access.

3. Initialize Hibernate

Hibernate initialization is a little trickier, but when no customization is needed, this boils down to just a few more method and constructor calls.

First, we create a common base registry for all persistence units (which is then passed to their constructor).
The base registry is used to provide strategy selectors, class loaders and configure lookup precedence.

            BootstrapServiceRegistryBuilder baseRegistryBuilder = new BootstrapServiceRegistryBuilder();
            this.baseRegistry = baseRegistryBuilder.build();

Secondly and as the first part in the persistence unit class, we create a service registry for the current persistence unit which is based on the base registry.

Here, we provide the HikariDataSource to Hibernate and can also optionally set some other unit-specific values.

Now, Hibernate requires information about the entity classes available. This is done by creating a MetadataSources instance and passing the created registry to it. Additionally, we programmatically tell the metadata sources what entity classes we know of and want to be available to this persistence unit. (The JeakBot-Framework uses org.Reflections for this.)

Finally, we can create the session factory from the metadata sources which will be able to create instances of EntityManager for us.

4. Cleaning up afterwards

I made the persistence unit implement AutoClosable just so it complies with conventions and implemented the close method.

In that method, we close the HikaryDataSource (which will close its internal connection pool), close the session factory (which will clear Hibernate session caches) and destroy the service registry (which will stop services registered to it).

It appears that this is enough as I did not run into dead-threads issues upon application exit.


Thank you

for reading my first “guide” on this blog.

I sincerely hope this will help some other programmers integrating Hibernate and/or Hikari into their applications and allow them to let API users choose which level of abstraction is the best for their code.

If you have any questions, corrections and/or additions regarding this post, feel free to get in touch.

Best regards,

– Mark


Sources / Further reading

Categories
Dirty-Commits

DC: TeamSpeak 3 & Icons

Introducing “Dirty-Commits”

Welcome to “Dirty-Commits”.
The format/category where I talk about non-fancy (or even ugly) work-arounds I had to commit into my git history in order to get things working.

Background

The TeamSpeak 3 Server Query API provides a command to retrieve information on the channels of the server (channellist). Using the -icon flag includes the channel_icon_id property.
That icon ID is a CRC32 checksum which is used to identify the icon.

The problem

Earlier this year, I experimented with a plugin that would enforce channel icons for certain channels so they would not be changed even by permitted users.
I quickly noticed that the plugin went totally haywire, editing every watched channel in each check cycle.

At first, I thought that I had mistakenly messed up the check for the icon ID but that turned out to be false pretty quickly. Upon checking the messages returned by the server I discovered that the channel icon ID sent by the server looked weird in that it was negative.

Let’s take a look at the example I was examining:
The icon in question has the ID 3948173233 and setting the channel property to that ID did result in the correct icon being set.
But, for some reason, when returning the channel list, TeamSpeak always returns -346794063 which obviously is not correct.

I went into hours of googling for any hints of other people encountering that issue but it seemed that this issue was unique to our setup. At some point I realized that setups using MariaDB as the database do not seem to be that common. So I tried reproducing this issue with no database server configured.
And it went away? So something must be wrong with the data transition from the database to the query connection. Having previously checked that the data lands in the tables correctly that seemed like the only place where the issue could be caused.

Just having recently had to attend a technical fundamentals of computer science course I had the 2-complement swirling around in my head.
I thought myself: “Why not use this as an exercise for converting numbers between decimals and binary” although I was not really sure what to expect at that moment.

This however, showed me how to work around the issue:
-346794063 in 2-complementary representation is 111010110101010001010111101 which in turn is 3948173233 when interpreted as an unsigned integer.
But wait! That is the real icon ID!

So yeah. It appears that TeamSpeak messes up the integer representation when constructing the query message and reads the unsigned integer as a signed one.

The “solution”

Commit ff5a7631 in the server bot framework will make sure this issue will never be passed to any plugins of the framework.
Whenever a negative icon ID is passed to the framework, it will be re-interpreted as an unsigned integer, resulting in the correct ID being stored.

Categories
Introducing

Introducing: Me

Of course, one of the first posts of a blog must be an introduction of the author. So, this is me:

My name is Magnus Leßmann and I’m also known as “MarkL4YG” (and thus also just “Mark”) on the Internet.
I currently wield about 20 years of experience in “Life” and started developing interest in Software back in 2011 when a friend of mine and I started playing the one and only now well known Minecraft.

Apart from setting up the server, I also started coding in Lua via. Computer Craft to program more and more complex factories. (Theres also a brief foreplay with Delphi 1 during a course I attended where I met that friend but there’s nothing of any importance to write about.)

Since then, I have always been part of a gaming community as a developer and administrator.
At the moment of writing, that is FearNixx Gaming of which I am the lead developer, system administrator and a co-founder.

Up until now I have learned to use Pascal, Lua, Java, Shell, JavaScript, PHP, SCSS, some C#, aswell as some C++ for my Arduino board.
I have briefly touched many other languages but would not yet count any of them to this list.

I will not go into detail about my current and former projects as I am planning to dedicate a custom page to them.

I am currently studying applied computer sciences at the NORDAKADEMIE in Germany which accounts for the most of my time spent.
And sporadically showing up at the CCCHH, as well as voluntary firefighting also form occupations on my time table.

I am always excited to learn new features, languages, frameworks but especially people regardless of their hobbies as I feel like different opinions can enrich the overall view of things.
So, if you feel inclined to say “Hello”, don’t hesitate to contact me using any of the methods listed on the contact page.

Have a nice time!
Magnus

Categories
home Introducing

Introducing: My blog

Ever since I started programming web sites and components, I regularly stumble upon a particular opinion that – although pretty much compatible with my own thoughts – did never really hit open ears on my side.

Every developer should have an own blog to share their experiences to.

So here we are. I now have finally taken time to set up my own blog using WordPress. (Huge thanks you to WordPress and the ResponsiveBlogily theme for being free by the way!)

I’ll probably be posting previous experiences during the winter months as I can already think of some that might help during a Google search.

So yeah, have fun reading!