Red Hat

In Relation To

The Hibernate team blog on everything data.

Hibernate Community Newsletter 15/2016

Posted by    |       |    Tagged as Discussions Hibernate ORM

Welcome to the Hibernate community newsletter in which we share blog posts, forum, and StackOverflow questions that are especially relevant to our users.

Articles

Null and not-null @DiscriminatorValue options

Posted by    |       |    Tagged as Discussions Hibernate ORM

Inheritance and discriminator columns

Although it can be used for JOINED table inheritance, the @DiscriminatorValue is more common for SINGLE_TABLE inheritance. For SINGLE_TABLE, the discriminator column tells Hibernate the subclass entity type associated with each particular database row.

Without specifying a discriminator column, Hibernate is going to use the default DTYPE column. To visualize how it works, consider the following Domain Model inheritance hierarchy:

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public static class Account {

    @Id
    private Long id;

    private String owner;

    private BigDecimal balance;

    private BigDecimal interestRate;

    public Long getId() {
        return id;
    }

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

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public BigDecimal getBalance() {
        return balance;
    }

    public void setBalance(BigDecimal balance) {
        this.balance = balance;
    }

    public BigDecimal getInterestRate() {
        return interestRate;
    }

    public void setInterestRate(BigDecimal interestRate) {
        this.interestRate = interestRate;
    }
}

@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {

    private BigDecimal overdraftFee;

    public BigDecimal getOverdraftFee() {
        return overdraftFee;
    }

    public void setOverdraftFee(BigDecimal overdraftFee) {
        this.overdraftFee = overdraftFee;
    }
}

@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {

    private BigDecimal creditLimit;

    public BigDecimal getCreditLimit() {
        return creditLimit;
    }

    public void setCreditLimit(BigDecimal creditLimit) {
        this.creditLimit = creditLimit;
    }
}

For this mode, Hibernate generates the following database table:

create table Account (
    DTYPE varchar(31) not null,
    id bigint not null,
    balance decimal(19,2),
    interestRate decimal(19,2),
    owner varchar(255),
    overdraftFee decimal(19,2),
    creditLimit decimal(19,2),
    primary key (id)
)

So when inserting two subclass entities:

DebitAccount debitAccount = new DebitAccount();
debitAccount.setId( 1L );
debitAccount.setOwner( "John Doe" );
debitAccount.setBalance( BigDecimal.valueOf( 100 ) );
debitAccount.setInterestRate( BigDecimal.valueOf( 1.5d ) );
debitAccount.setOverdraftFee( BigDecimal.valueOf( 25 ) );

CreditAccount creditAccount = new CreditAccount();
creditAccount.setId( 2L );
creditAccount.setOwner( "John Doe" );
creditAccount.setBalance( BigDecimal.valueOf( 1000 ) );
creditAccount.setInterestRate( BigDecimal.valueOf( 1.9d ) );
creditAccount.setCreditLimit( BigDecimal.valueOf( 5000 ) );

Hibernate will populate the DTYPE column with the subclass class name:

INSERT INTO Account (balance, interestRate, owner, overdraftFee, DTYPE, id)
VALUES (100, 1.5, 'John Doe', 25, 'DebitAccount', 1)

INSERT INTO Account (balance, interestRate, owner, creditLimit, DTYPE, id)
VALUES (1000, 1.9, 'John Doe', 5000, 'CreditAccount', 2)

While this is rather straightforward for most use cases, when having to integrate a legacy database schema, it might be that the discriminator column contains NULL(s) or some values that are not associated to any entity subclass.

Consider that our database contains records like these:

INSERT INTO Account (DTYPE, balance, interestRate, owner, id)
VALUES (NULL, 300, 0.9, 'John Doe', 3)

INSERT INTO Account (DTYPE, active, balance, interestRate, owner, id)
VALUES ('Other', true, 25, 0.5, 'Johnny Doe', 4)

INSERT INTO Account (DTYPE, active, balance, interestRate, owner, id)
VALUES ('Unsupported', false, 35, 0.6, 'John Doe Jr.', 5)

With the previous mappings, when trying to fetch all Account(s):

Map<Long, Account> accounts = entityManager.createQuery(
        "select a from Account a", Account.class )
.getResultList()
.stream()
.collect( Collectors.toMap( Account::getId, Function.identity()));

We’d bump into the following kind of issues:

org.hibernate.WrongClassException: Object [id=3] was not of the specified subclass
[org.hibernate.userguide.inheritance.Account] : Discriminator: null

org.hibernate.WrongClassException: Object [id=4] was not of the specified subclass
[org.hibernate.userguide.inheritance.Account] : Discriminator: Other

org.hibernate.WrongClassException: Object [id=5] was not of the specified subclass
[org.hibernate.userguide.inheritance.Account] : Discriminator: Unsupported

Fortunately, Hibernate allows us to handle these mappings by using NULL and NOT NULL discriminator value mapping.

For the NULL values, we can annotate the base class Account entity as follows:

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorValue( "null" )
public static class Account {

    @Id
    private Long id;

    private String owner;

    private BigDecimal balance;

    private BigDecimal interestRate;

    // Getter and setter omitted for brevity
}

For the Other and Unsupported discriminator values, we can have a miscellaneous entity that handles all values that were not explicitly mapped:

@Entity(name = "MiscAccount")
@DiscriminatorValue( "not null" )
public static class MiscAccount extends Account {

    private boolean active;

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}

This way, the aforementioned polymorphic query works and we can even validate the results:

assertEquals(5, accounts.size());
assertEquals( DebitAccount.class, accounts.get( 1L ).getClass() );
assertEquals( CreditAccount.class, accounts.get( 2L ).getClass() );
assertEquals( Account.class, accounts.get( 3L ).getClass() );
assertEquals( MiscAccount.class, accounts.get( 4L ).getClass() );
assertEquals( MiscAccount.class, accounts.get( 5L ).getClass() );

I have also updated the Hibernate 5.0, 5.1, and 5.2 documentations with these two very useful mapping options.

How we fixed all database connection leaks

Posted by    |       |    Tagged as Discussions Hibernate ORM

The context

By default, all Hibernate tests are run on H2. However, we have a lots of database-specific tests as well, so we should be testing on Oracle, PostgreSQL, MySQL, and possibly SQL Server as well.

When we tried to set up a Jenkins job that uses PostgreSQL, we realized that the job fails because we ran out of connections. Knowing that the PostgreSQL server has a max_connections setting of 30, we realized the connection leak issue was significant.

Needle in a haystack

Just the hibernate-core module alone has over 5000 tests, and hibernate-envers has around 2500 tests as well. But there are many mode modules: hibernate-c3p0, hibernate-ehcache, hibernate-jcache, etc. All in all, we couldn’t just browse the code and spot issues. We needed an automated connection leak detector.

That being said, I came up with a solution that works on H2, Oracle, PostgreSQL, and MySQL as well. Luckily, no problem was spotted in the actual framework code base. All issues were caused by unit tests which did not handle database resources properly.

The most common issues

One of the most widespread issue was caused by improper bootstrapping logic:

@Test
public void testInvalidMapping() {
    try {
        new MetadataSources( )
                .addAnnotatedClass( TheEntity.class )
                .buildMetadata();
        fail( "Was expecting failure" );
    }
    catch (AnnotationException ignore) {
    }
}

The issue here is that MetadataSources creates a BootstrapServiceRegistry behind the scenes, which in turn triggers the initialization of the underlying ConnectionProvider. Without closing the BootstrapServiceRegistry explicitly, the ConnectionProvider will not get a chance to close all the currently pooled JDBC Connection(s).

The fix is as simple as that:

@Test
public void testInvalidMapping() {
    MetadataSources metadataSources = new MetadataSources( )
        .addAnnotatedClass( TheEntity.class );
    try {
        metadataSources.buildMetadata();
        fail( "Was expecting failure" );
    }
    catch (AnnotationException ignore) {
    }
    finally {
        ServiceRegistry metaServiceRegistry = metadataSources.getServiceRegistry();
        if(metaServiceRegistry instanceof BootstrapServiceRegistry ) {
            BootstrapServiceRegistryBuilder.destroy( metaServiceRegistry );
        }
    }
}

Another recurring issue was improper transaction handling such as in the following example:

protected void cleanup() {
    Session s = getFactory().openSession();
    s.beginTransaction();

    TestEntity testEntity = s.get( TestEntity.class, "foo" );
    Assert.assertTrue( testEntity.getParams().isEmpty() );

    TestOtherEntity testOtherEntity = s.get( TestOtherEntity.class, "foo" );
    Assert.assertTrue( testOtherEntity.getParams().isEmpty() );

    s.getTransaction().commit();
    s.clear();
    s.close();
}

The first thing to notice is the lack of a try/finally block which should be closing the session even if there is an exception being thrown. But that’s not all.

Not a long time ago, I had fixed HHH-7412, meaning that, for RESOURCE_LOCAL (e.g. JDBC Connection-bound transactions), the logical or physical Connection is closed only when the transaction is ended (either commit or rollback).

Before HHH-7412 was fixed, the Connection was closed automatically when the Hibernate Session was closed as well, but this behavior is not supported anymore. Nowadays, aside from closing the underlying Session, you have to commit/rollback the current running Transaction as well:

protected void cleanup() {
    Session s = getFactory().openSession();
    s.beginTransaction();

    try {
        TestEntity testEntity = s.get( TestEntity.class, "foo" );
        Assert.assertTrue( testEntity.getParams().isEmpty() );

        TestOtherEntity testOtherEntity = s.get( TestOtherEntity.class, "foo" );
        Assert.assertTrue( testOtherEntity.getParams().isEmpty() );

        s.getTransaction().commit();
    }
    catch ( RuntimeException e ) {
        s.getTransaction().rollback();
        throw e;
    }
    finally {
        s.close();
    }
}

If you are curious of all the changes that were required, you can check the following two commits: da9c6e1 and f5e10c2. The good news is that the PostgreSQL job is running fine now, and soon we will add jobs for Oracle and a MySQL too.

Updating Hibernate ORM in WildFly

Posted by    |       |    Tagged as Hibernate ORM

In this post I’ll show you how easy it is to use the latest and greatest version of Hibernate ORM with WildFly 10.

Traditionally, updating Hibernate in WildFly required some good knowledge of the server’s module system and the structure of the ORM modules. It certainly was doable, but it involved search/replace in existing module descriptors and generally wasn’t very convenient.

This has become much simpler with last week’s release of Hibernate ORM 5.2.1!

We now provide a ZIP archive containing all the required modules, making it a breeze to add the latest version of Hibernate to an existing WildFly instance. And what’s best: the version of Hibernate packaged with the application server remains untouched; switching between this and the new version is just a matter of setting one small configuration option, and you can go back at any time.

Preparations

The ZIP file is available in Maven Central, so you can automate its download as part of your build if needed. These are the GAV coordinates:

  • groupId: org.hibernate

  • artifactId: hibernate-orm-modules

  • version: 5.2.1.Final

  • classifier: wildfly-10-dist

  • type: zip

Unzip the archive into the modules directory of your WildFly instance. If done correctly, you should see two sub-directories under modules: system (the server’s original modules) and org (the new Hibernate ORM modules).

Choosing the right version of Hibernate ORM

Having added the Hibernate ORM 5.2 modules to the server, you need to configure your application so it uses that specific Hibernate version instead of the default one coming with the server. To do so, just add the following property to your META-INF/persistence.xml file:

...
<properties>
    <property name="jboss.as.jpa.providerModule" value="org.hibernate:5.2" />
    ...
</properties>
...

In case you have several Hibernate releases added to your WildFly server, you also can define a specific micro version:

<property name="jboss.as.jpa.providerModule" value="org.hibernate:5.2.1.Final" />

Example project

As an example for using the module ZIP, I’ve created a small Maven project. You can find it in the hibernate-demos repository. To run the example project, simply execute mvn clean verify. Let’s take a quick look at some interesting parts.

First, in the Maven POM file, the maven-dependency-plugin is used to

  • download the WildFly server and unpack it into the target directory

  • download the Hibernate ORM module ZIP and extract its contents into the modules directory of WildFly:

pom.xml
...
<properties>
    <hibernate.version>5.2.1.Final</hibernate.version>
    <wildfly.version>10.0.0.Final</wildfly.version>
    <jboss.home>${project.build.directory}/wildfly-${wildfly.version}</jboss.home>
    ...
</properties>
...
<plugin>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>unpack</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>unpack</goal>
            </goals>
            <configuration>
                <artifactItems>
                    <!-- WildFly server; Unpacked into target/wildfly-10.0.0.Final -->
                    <artifactItem>
                        <groupId>org.wildfly</groupId>
                        <artifactId>wildfly-dist</artifactId>
                        <version>${wildfly.version}</version>
                        <type>zip</type>
                        <overWrite>false</overWrite>
                        <outputDirectory>${project.build.directory}</outputDirectory>
                    </artifactItem>
                    <!-- Hibernate ORM modules; Unpacked into target/wildfly-10.0.0.Final/modules -->
                    <artifactItem>
                        <groupId>org.hibernate</groupId>
                        <artifactId>hibernate-orm-modules</artifactId>
                        <version>${hibernate.version}</version>
                        <classifier>wildfly-10-dist</classifier>
                        <type>zip</type>
                        <overWrite>false</overWrite>
                        <outputDirectory>${jboss.home}/modules</outputDirectory>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>
...

Next, Hibernate ORM 5.2 must be enabled for the application using the jboss.as.jpa.providerModule property as discussed above:

META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence
    xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
    version="2.1">

    <persistence-unit name="testPu" transaction-type="JTA">
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>

        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
            <property name="jboss.as.jpa.providerModule" value="org.hibernate:5.2"/>
        </properties>
    </persistence-unit>
</persistence>

This persistence unit is using the example datasource configured by default in WildFly, which is using an in-memory H2 database.

Finally, we need a test to ensure that actually Hibernate ORM 5.2 is used and not the 5.0 version coming with WildFly. To do so, we can simply invoke one of the methods of EntityManager which are exposed through Hibernate’s classic Session API as of 5.2:

HibernateModulesOnWildflyIT.java
@RunWith(Arquillian.class)
public class HibernateModulesOnWildflyIT {

    @Deployment
    public static WebArchive createDeployment() {
        return ShrinkWrap.create( WebArchive.class )
                .addClass( Kryptonite.class )
                .addAsWebInfResource( EmptyAsset.INSTANCE, "beans.xml" )
                .addAsResource( "META-INF/persistence.xml" );
    }

    @PersistenceContext
    private EntityManager entityManager;

    @Test
    @Transactional(value=TransactionMode.ROLLBACK)
    public void shouldUseHibernateOrm52() {
        Session session = entityManager.unwrap( Session.class );

        Kryptonite kryptonite1 = new Kryptonite();
        kryptonite1.id = 1L;
        kryptonite1.description = "Some Kryptonite";
        session.persist( kryptonite1 );

        session.flush();
        session.clear();

        // EntityManager methods exposed through Session only as of 5.2
        Kryptonite loaded = session.find( Kryptonite.class, 1L );

        assertThat( loaded.description, equalTo( "Some Kryptonite" ) );
    }
}

The call to find() would fail with a NoSuchMethodError if Hibernate ORM 5.0 instead of 5.2 was used. To try it out, simply remove the jboss.as.jpa.providerModule property from persistence.xml.

The test itself is executed using the great Arquillian tool. It creates a test WAR file with the contents configured in the createDeployment() method, starts up the WildFly server, deploys the WAR file and runs the test within the running container. Using the Arquillian Transactional Extension, the test method is executed within a transaction which is rolled back afterwards.

Feedback welcome!

The Hibernate ORM module ZIP file is a very recent addition to Hibernate. Should you run into any issues, please let us know and we’ll be happy to help.

You can learn more in this topical guide. Hibernate ORM 5.2.1 is the first release to provide a module ZIP, the next 5.1.x release will provide one, too.

Give it a try and let us know about your feedback!

First Hibernate OGM 5 maintenance release

Posted by    |       |    Tagged as Hibernate OGM Releases

We released Hibernate OGM 5.0.1.Final!

What’s new?

Here some of the most interesting bug fixes and improvements in this release:

  • OGM-818 - Autodetection support for @Entity annontated classes will now work

  • OGM-356 - Object comparison in JPQL queries for MongoDB and Neo4j

  • OGM-1065 - You can now use Hibernate OGM with Cassandra 3 (Thanks joexner!)

You can find all the details in the changelog.

This release is backward compatible with Hibernate OGM 5.0.0.Final but if you need to upgrade from a previous version, you can find help on the migration notes.

Where can I get it?

You can get Hibernate OGM 5.0.1.Final core via Maven using the following coordinates:

  • org.hibernate.ogm:hibernate-ogm-core:5.0.1.Final

and these are the back-ends currently available:

  • Cassandra: org.hibernate.ogm:hibernate-ogm-cassandra:5.0.1.Final

  • CouchDB: org.hibernate.ogm:hibernate-ogm-couchdb:5.0.1.Final

  • Infinispan: org.hibernate.ogm:hibernate-ogm-infinispan:5.0.1.Final

  • Ehcache: org.hibernate.ogm:hibernate-ogm-ehcache:5.0.1.Final

  • MongoDB: org.hibernate.ogm:hibernate-ogm-mongodb:5.0.1.Final

  • Neo4j: org.hibernate.ogm:hibernate-ogm-neo4j:5.0.1.Final

  • Redis: org.hibernate.ogm:hibernate-ogm-redis:5.0.1.Final

Alternatively, you can download archives containing all the binaries, source code and documentation from SourceForge.

How can I get in touch?

You can find us through the following channels:

We are looking forward to hear your feedback!

First bug-fix release for ORM 5.2

Posted by    |       |    Tagged as Hibernate ORM Releases

The first bug-fix release for Hibernate ORM 5.2 has just been published. It is tagged at https://github.com/hibernate/hibernate-orm/releases/tag/5.2.1

The complete list of changes can be found here.

For information on consuming the release via your favorite dependency-management-capable build tool, see http://hibernate.org/orm/downloads/

The release bundles can be obtained from SourceForge or BinTray.

We are making good progress on our next major release which focuses on Elasticsearch integration but we don’t forget our beloved users of Hibernate Search 5.5.x and here is a new stable release to prove it!

This bugfix release is entirely based on user feedback so keep it coming!

Hibernate Search version 5.5.4.Final is available now and fixes the following issues:

  • HSEARCH-2301 - CriteriaObjectInitializer is suboptimal when we query only one subtype of a hierarchy

  • HSEARCH-2286 - DistanceSortField should support reverse sorting

  • HSEARCH-2306 - Upgrade 5.5.x to Hibernate ORM 5.0.9

  • HSEARCH-2307 - Documentation shouldn’t suggest need for @Indexed of embedded association fields

Small focus on HSEARCH-2301 as it might significantly improve your performances if you index complex hierarchy of objects. Prior to this fix, when querying the database to hydrate the objects, Hibernate Search was using the root type of the hierarchy potentially leading to queries with a lot of joins. Hibernate now builds the most efficient query possible depending on the effective results.

You can see two instances of this issue on Stack Overflow here and here.

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-search-orm</artifactId>
   <version>5.5.4.Final</version>
</dependency>
<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>5.0.9.Final</version>
</dependency>
<dependency>
   <groupId>org.apache.lucene</groupId>
   <artifactId>lucene-core</artifactId>
   <version>5.3.1</version>
</dependency>

How to get this release

Everything you need is available on Hibernate Search’s web site. Download the full distribution from here, or get it from Maven Central using the above coordinates, and don’t hesitate to reach us in our forums or mailing lists.

We also monitor closely the hibernate-search tag on Stack Overflow.

Hibernate Community Newsletter 13/2016

Posted by    |       |    Tagged as Discussions Hibernate ORM

Welcome to the Hibernate community newsletter in which we share blog posts, forum, and StackOverflow questions that are especially relevant to our users.

Articles

Introduction

When I started writing High-Performance Java Persistence, I decided to install four database systems on my current machine:

  • Oracle XE

  • SQL Server Express Edition

  • PostgreSQL

  • MySQL

These four relational databases are the most commonly referred ones on our forum, StackOverflow, as well as on most JIRA issues. However, these four top-ranked databases are not enough because, from time to time, we need to integrate Pull Requests for other database systems, like Informix or DB2.

Since installing a plethora of databases on a single machine is not very practical, we can do better than that. Many database providers have generated Docker images for their products, and this post is going to show you haw easy we can start an Informix database.

Running Informix on Docker

IBM offers Docker images for both Informix Innovator-C and DB2 Express-C.

As explained on Docker Hub, you have to start the container using the following command:

docker run -it --name iif_innovator_c --privileged -p 9088:9088 -p 27017:27017 -p 27018:27018 -p 27883:27883 -e LICENSE=accept ibmcom/informix-innovator-c:latest

To run the Informix Docker container, you have to execute the following command:

docker start iif_innovator_c

After the Docker container is started, we can attach a new shell to it:

docker exec -it iif_innovator_c bash

We have a databases.gradle configuration file which contains the connection properties for all databases we use for testing, and, for Informix, we have the following entry:

informix : [
    'db.dialect' : 'org.hibernate.dialect.InformixDialect',
    'jdbc.driver': 'com.informix.jdbc.IfxDriver',
    'jdbc.user'  : 'informix',
    'jdbc.pass'  : 'in4mix',
    'jdbc.url'   : 'jdbc:informix-sqli://192.168.99.100:9088/sysuser:INFORMIXSERVER=dev;user=informix;password=in4mix'
]

With this configuration in place, I only need to setup the current hibernate.properties configuration file to use Informix:

gradle clean testClasses -Pdb=informix

Now I can run any Informix integration test right from my IDE.

When I’m done, I stop the Docker container with the following command:

docker stop iif_innovator_c

As simple as that!

back to top