Hibernate 5 Kotlin Quick Start

This tutorial will get you using Hibernate with Kotlin quickly!

I have run into situations where I just simply want to write a simple application that has Hibernate support, without having to use a full stack such as Spring Boot. It’s not hard to get going, but there are a few things to think about such as configuring Hibernate properly and managing your own transactions. The good thing is that this doesn’t take a lot of effort, as I will show you in the following tutorial.

Dependencies

To get started, here is a basic pom.xml file that will show you what dependencies you need.

dependencies {
    compile 'org.hibernate:hibernate-java8:5.4.4.Final'
    compile 'org.hibernate:hibernate-c3p0:5.4.4.Final'
    compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.41'
    compile 'org.jetbrains.kotlin:kotlin-reflect:1.3.41'
    compile 'com.h2database:h2:1.4.199'
    compile 'commons-io:commons-io:2.6'
    compile 'org.apache.logging.log4j:log4j-api:2.12.1'
    compile 'org.apache.logging.log4j:log4j-core:2.12.1'
    testCompile 'org.jetbrains.kotlin:kotlin-test:1.3.41'
}

Properties File

Next we need a properties file that will hold our Hibernate configuration

driver=org.h2.Driver
url=jdbc:h2:~/roms
user=root
pass=root
dialect=org.hibernate.dialect.H2Dialect
showSql=false
formatSql=true
currentSessionContextClass=thread
ddlAuto=validate

# Needed for a connection pool
hibernate.c3p0.min_size=1000
hibernate.c3p0.max_size=2000
hibernate.c3p0.timeout=120
hibernate.c3p0.max_statements=2000

This properties file will create an embedded H2 database. It also defines a connection pool, which is required in programs that use multiple threads, which mine often  do. If you want to see the SQL that is generated, then you should flip the showSql property to true.

Entity Class

Hibernate maps objects to database tables, which means that we need an entity class.

data class AnEntity (
        @Id @GeneratedValue
        val id: Long? = null,
        val name: String? = null
)

Boot Strapping Hibernate

At this point, we are ready to begin configuring Hibernate.

Reading the Properties File

Let’s begin by reading our properties file into memory. Here is a nice little Kotlin function that we pull a properties file from the resource folder.

fun propertiesFromResource(resource: String): Properties {
    val properties = Properties()
    properties.load(Any::class.java.getResourceAsStream(resource))
    return properties
}

Convert Properties file to Hibernate Properties

Our next step is to read the information in the properties file and turn it into a Properties object that can be consumed by Hibernate. We can use Kotlin’s extension function feature to make this easy.

fun Properties.toHibernateProperties(): Properties {
    val hibernateProperties = Properties()
    hibernateProperties[Environment.DRIVER] = this["driver"]
    hibernateProperties[Environment.URL] = this["url"]
    hibernateProperties[Environment.USER] = this["user"]
    hibernateProperties[Environment.PASS] = this["pass"]
    hibernateProperties[Environment.DIALECT] = this["dialect"]
    hibernateProperties[Environment.SHOW_SQL] = this["showSql"]
    hibernateProperties[Environment.FORMAT_SQL] = this["formatSql"]
    hibernateProperties[Environment.CURRENT_SESSION_CONTEXT_CLASS] = this["currentSessionContextClass"]
    hibernateProperties[Environment.HBM2DDL_AUTO] = this["ddlAuto"]

    //C3PO
    hibernateProperties["hibernate.c3p0.min_size"] = this["hibernate.c3p0.min_size"]
    hibernateProperties["hibernate.c3p0.max_size"] = this["hibernate.c3p0.max_size"]
    hibernateProperties["hibernate.c3p0.timeout"] = this["hibernate.c3p0.timeout"]
    hibernateProperties["hibernate.c3p0.max_statements"] = this["hibernate.c3p0.max_statements"]

    return hibernateProperties
}

Building a Hibernate Configuration

Hibernate requires a configuration. We can use the Properties object that we made in the last step as input for creating a Hibernate configuration. Additionally, we need to supply class objects for any classes that we need Hibernate to manage. It’s best to supply this as a vararg.

fun buildHibernateConfiguration(hibernateProperties: Properties, vararg annotatedClasses: Class<*>): Configuration {
    val configuration = Configuration()
    configuration.properties = hibernateProperties
    annotatedClasses.forEach { configuration.addAnnotatedClass(it) }
    return configuration
}

Build a SessionFactory

The SessionFactory is the outcome of all of this work. It requires a Configuration in order to be created, but once you have a Configuration, it’s easy to get going in Hibernate.

fun buildSessionFactory(configuration: Configuration): SessionFactory {
    val serviceRegistry = StandardServiceRegistryBuilder().applySettings(configuration.properties).build()
    return configuration.buildSessionFactory(serviceRegistry)
}

Transactions

Hibernate is often used in environments where a container, such as Spring, manages your transactions automatically. In this case, we need to manually manage transactions, but Kotlin makes it really easy to eliminate the boiler plate code that would normally be required. Here is a nice little function that allows you to manage your transactions.

fun <T> SessionFactory.transaction(block: (session: Session) -> T): T {
    val session = openSession()
    val transaction = session.beginTransaction()

    return try {
        val rs = block.invoke(session)
        transaction.commit()
        rs
    } catch (e: Exception){
        logger.error("Transaction failed! Rolling back...", e)
        throw e
    }
}

Shutdown Hook

The final house keeping item is to make sure that we close our SessionFactory when we are finished. We can tap into the JVM’s shutdown hooks to make sure that our database connection has been closed properly.

fun addHibernateShutdownHook(sessionFactory: SessionFactory)  {
    Runtime.getRuntime().addShutdownHook(object: Thread() {
        override fun run() {
            logger.debug("Closing the sessionFactory...")
            sessionFactory.close()
            logger.info("sessionFactory closed successfully...")
        }
    })
}

Demonstration

Let’s wrap this up with a nice little demonstration program that puts all of this into action!

fun main(){
    val properties = propertiesFromResource("/database.properties")
    properties["url"] = "jdbc:h2:mem:test" //Override the properties to make an in memory db

    val configuration = buildHibernateConfiguration(properties.toHibernateProperties(), AnEntity::class.java)
    val sessionFactory = buildSessionFactory(configuration)
    addHibernateShutdownHook(sessionFactory)

    sessionFactory.transaction { session ->
        session.save(AnEntity(name = "Birdie"))
    }

    val entity = sessionFactory.transaction { session ->
        session.createQuery("from AnEntity").uniqueResult() as AnEntity
    }

    println(entity)
}

Sources

https://hibernate.org/orm/documentation/5.0/

https://kotlinlang.org/docs/reference/