How to fix the Hibernate “No Dialect mapping for JDBC type” issue
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
Introduction
Recently, stumbled on this question on the Hibernate forum, and since I’ve been seeing it before on StackOverflow and bumped into it myself while working with JPA and Hibernate, I decided to turn the answer into an article.
Therefore, in this article, you are going to find out how you can fix the “No Dialect mapping for JDBC type” Hibernate issue.
Domain Model
Considering we have a Book
entity that defines a properties
attribute which is associated with a JSON column in the database.
The Book
entity can be mapped as follows:
@Entity(name = "Book") @Table(name = "book") public class Book { @Id @GeneratedValue private Long id; @NaturalId private String isbn; @Type(JsonType.class) @Column(columnDefinition = "jsonb") private JsonNode properties; //Getters and setters omitted for brevity }
The
JsonType
is provided by the Hypersistence Utils project, so if you want to persist JSON properties, you don’t need to write your own Hibernate Types.Just add the Hypersistence Utils dependency to your project and map the JSON properties accordingly.
Persisting and fetching the Book entity
Now, let’s assume we have added the following Book
entity in our database:
Book book = new Book(); book.setIsbn("978-9730228236"); book.setProperties( JacksonUtil.toJsonNode(""" { "title": "High-Performance Java Persistence", "author": "Vlad Mihalcea", "publisher": "Amazon", "price": 44.99 } """ ) ); entityManager.persist(book);
When persisting the Book
entity, Hibernate will issue the proper SQL INSERT statement:
INSERT INTO book ( isbn, properties, id ) VALUES ( '978-9730228236', { "title":"High-Performance Java Persistence", "author": "Vlad Mihalcea", "publisher":"Amazon", "price":44.99 }, 1 )
Now, we fetching the Book
entity by its natural identifier, we can see that the properties
JSON attribute is fetched as a JsonNode
:
Book book = entityManager .unwrap(Session.class) .bySimpleNaturalId(Book.class) .load("978-9730228236"); assertEquals( "High-Performance Java Persistence", book.getProperties().get("title").asText() );
Fetching the JSON attribute using JPQL
Now, if we want to fetch the properties
entity attribute using JPQL, we can execute the following query:
JsonNode properties = entityManager.createQuery(""" select b.properties from Book b where b.isbn = :isbn """, JsonNode.class) .setParameter("isbn", "978-9730228236") .getSingleResult(); assertEquals( "High-Performance Java Persistence", properties.get("title").asText() );
And everything works properly since the JPQL query is parsed, and the underlying Hibernate Type
that handles the properties
attribute is going to be known when building the result.
Fetching the JSON attribute using native SQL
However, if we try to do the same using a native SQL query:
JsonNode properties = (JsonNode) entityManager.createNativeQuery(""" SELECT properties FROM book WHERE isbn = :isbn """) .setParameter("isbn", "978-9730228236") .getSingleResult(); assertEquals( "High-Performance Java Persistence", properties.get("title").asText() );
Hibernate will throw the following MappingException
:
javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
The 1111
JDBC type corresponds to Types.OTHER
which is what the PostgreSQL JDBC Driver uses for jsonb
column types.
Mapping Types.OTHER to JsonNodeBinaryType
There are several ways you can address this issue. You can register a Hibernate Type
to handle the JDBC Types.OTHER
either globally or on a per-query basis.
Mapping the JDBC Types.OTHER to JsonNodeBinaryType at the Dialect level
You can map a given JDBC Type
code to a Hibernate Type
using the database-specific Dialect
.
Therefore, for PostgreSQL, we could define a PostgreSQL10JsonDialect
that looks as follows:
public class PostgreSQL10JsonDialect extends PostgreSQL10Dialect { public PostgreSQL10JsonDialect() { super(); this.registerHibernateType( Types.OTHER, JsonNodeBinaryType.class.getName() ); } }
And if we provide the custom PostgreSQL10JsonDialect
via the hibernate.dialect
configuration property:
<property name="hibernate.dialect" value="com.vladmihalcea.book.hpjp.hibernate.type.json.PostgreSQL10JsonDialect" />
The native SQL query will run just fine.
Mapping the JDBC Types.OTHER to JsonNodeBinaryType at the NativeQuery level
Another option is to provide Hibernate Type
associated with the current JDBC ResultSet
:
JsonNode properties = (JsonNode) entityManager.createNativeQuery(""" SELECT properties FROM book WHERE isbn = :isbn """) .setParameter("isbn", "978-9730228236") .unwrap(org.hibernate.query.NativeQuery.class) .addScalar("properties", JsonNodeBinaryType.INSTANCE) .getSingleResult(); assertEquals( "High-Performance Java Persistence", properties.get("title").asText() );
Notice the addScalar
method call, which provides the Hibernate Type
to be used when handling the Types.Other
JDBC column type.
That’s it!
I'm running an online workshop on the 11th of October about High-Performance SQL.If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
Conclusion
Handling the No Dialect mapping for JDBC type
issue is not very complicated, and it can be done either globally or on a per-query basis.
