Skip to content

How To Use Native SQL Functions in JPA Queries With Hibernate

Avoid using native SQL queries by registering native functions

JPA supports native SQL natively. This is useful when you want to take advantage of the unique features of the database in use. On the other hand, native SQL queries are not database agnostic, and this might turn into a problem.

For example, when switching from MySQL to PostgreSQL or vice versa. If you employed native queries, the transition would not be straightforward because you had to actively translate them. This is why native queries should be used only when strictly required.

One of the main causes for opting for native SQL queries is native functions, such as MySQL’s GROUP_CONCAT. In fact, you might believe that using native queries is the only way you have to harness these functions. Actually, this is not true! In fact, JPA 2.1 introduced features allowing you to call any native function in your JPQL or Criteria queries.

Let’s see how to start using native SQL functions in your JPA queries with Hibernate in both Kotlin and Java.

Registering Native Functions

The native SQL functions you want to use must be registered in your Hibernate Dialect implementation class, which depends on your database. You can find a list of all the dialects supported by Hibernate here. In this example, you are going to see MySQL8Dialect and PostgreSQL9Dialect.

First, you need to create a class extending your specific Dialect implementation class. In the example below, this class was placed in the com.yourapp.configs package. Then, use it to register native functions as follows:

Java

package com.yourapp.configs;

import org.hibernate.dialect.MySQL8Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;

public class CustomSQLDialect extends MySQL8Dialect {
    public MySQLCustomDialect() {
        super();
        
        registerFunction(
            "STRING_CONCAT", 
            new StandardSQLFunction(
                "GROUP_CONCAT", 
                StandardBasicTypes.STRING
            )
       );
    }
}

Kotlin

package com.yourapp.configs

import org.hibernate.dialect.MySQL8Dialect
import org.hibernate.dialect.function.StandardSQLFunction
import org.hibernate.type.StandardBasicTypes

class CustomSQLDialect : MySQL8Dialect() {
    init {
        registerFunction(
            "STRING_CONCAT",
            StandardSQLFunction(
                "GROUP_CONCAT",
                StandardBasicTypes.STRING
            )
        )
    }
}

In this case, the MySQL GROUP_CONCAT function was added to MySQL8Dialect by passing a StandardSQLFunction class to the registerFunction method. The first string passed to this method represents the name to use to call the native function in your queries. Note that many other native SQL functions could be registered similarly.

Now, you need to make this class the default Hibernate dialect. You can achieve this by adding the following property to your application.properties file:

spring.jpa.properties.hibernate.dialect = com.myapp.util.CustomSQLDialect

Et voilà! You can start using the native SQL functions you just registered in your JPA queries.

Native Functions in Action

Let’s see an example of the just registered STRING_CONCAT function in action:

SELECT F, STRING_CONCAT(F.name) AS names
FROM Foo F 
GROUP BY F.birthDate

Let’s assume that the JPA Entity Foo maps a foo table having the columns id, name, birthDate.

Then, that JPQL query will be translated into the following MySQL query:

SELECT F.id, F.name, F.surname, GROUP_CONCAT(F.name) AS names
FROM foo F 
GROUP BY F.birthDate

As you can see, the MySQL GROUP_CONCAT function is now mapped by the STRING_CONCAT JPA function. This means that the MySQL native function can now be used without employing non-portable native SQL queries.

Migrating to a Different Database

Let’s say you wanted to migrate from MySQL to PostgreSQL. If every native MySQL function you registered has a counterpart in PostgreSQL, this will be easy. Plus, no JPA query will have to be changed.

The only thing you need to do is change your CustomSQLDialect class as follows:

Java

package com.yourapp.configs;

import org.hibernate.dialect.PostgreSQL9Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;

public class CustomSQLDialect extends PostgreSQL9Dialect {
    public MySQLCustomDialect() {
        super();
        
        registerFunction(
            "STRING_CONCAT", 
            new StandardSQLFunction(
                "STRING_AGG", 
                StandardBasicTypes.STRING
            )
       );
    }
}

Kotlin

package com.yourapp.configs

import org.hibernate.dialect.PostgreSQL9Dialect
import org.hibernate.dialect.function.StandardSQLFunction
import org.hibernate.type.StandardBasicTypes

class CustomSQLDialect : PostgreSQL9Dialect() {
    init {
        registerFunction(
            "STRING_CONCAT",
            StandardSQLFunction(
                "STRING_AGG",
                StandardBasicTypes.STRING
            )
        )
    }
}

Since the MYSQL GROUP_CONCAT function corresponds to the PostgreSQL STRING_AGG function, no other changes are required. Every JPA function you wrote will still be working as expected. Note that the same procedure can be applied when migrating to other databases.

Conclusion

Today, we looked at how to make Hibernate support native SQL functions in JPQL or Criteria queries. This is a useful workaround to avoid adopting native queries, which are not portable by definition. As you just saw, registering functions to your Hibernate Dialect class involves only a few lines of code. On the other hand, it reduces the need to use native queries, making the migration to a different database easier. This is an important goal, and showing how to achieve it was what this article was about.

Thanks for reading! I hope that you found this article helpful.

nv-author-image

Antonello Zanini

I'm a software engineer, but I prefer to call myself a Technology Bishop. Spreading knowledge through writing is my mission.View Author posts

Want technical content like this in your blog?