Spring I/O

JSON beyond the Blob - Data evolution with Hibernate ORM by Christian Beikov @ Spring I/O 2026

34:36 · 13 Apr 2026 – 15 Apr 2026 · YouTube

About this talk

This talk focuses on Hibernate ORM, a widely used Java framework for object-relational mapping that has been in development since 2001. The speaker, Christian Bykov, discusses significant changes introduced in Hibernate ORM 6, including performance enhancements and new features to better support advanced SQL types like JSON. He shares insights from his experience as a core maintainer on the Hibernate project and a contributor to the Blaze Persistence initiative. The presentation highlights practical applications of JSON data handling within Hibernate, including functions like JSON_VALUE and JSON_QUERY. Bykov also addresses challenges related to JSON extraction, database support, and the efficient querying of array-based data within JSON documents. The session concludes with a discussion on performance optimization tactics and the evolution of Hibernate's capabilities, particularly regarding JSON storage and manipulation.

Full transcript

All right. Hello everyone. Um so this is going to be about Hibernate ORM. Um but first quick disclaimer because I'm an IBM employee. So this is my personal opinion, blah blah blah, you know. Don't buy any stock or anything based on this presentation. Um first, who am I? I'm Christian Bykov. I live in Düsseldorf, uh Germany, but I'm originally from Austria. Close to Vienna. I'm a long-term

Hibernate ORM uh contributor. Um and I'm working for Red Hat and now IBM since like 2020 uh on the Hibernate project. So I'm a Hibernate core maintainer. I like to play uh tennis and basketball. So if you want to play, just ask me. And I'm also the founder of a company called Blaze Persistence, which is behind the framework in in Java called Blaze Persistence, which builds on

top of But today I'm going to talk about uh Hibernate ORM itself. I suppose every one of you knows Hibernate ORM, especially also because uh this is a more intermediate talk. Um but it's uh around since May 2001. So we're approaching 25 years next month. It's going to be a celebration. Um and it's also, as you may know, a big component of uh Spring, uh Quarkus, and

Jakarta EE uh because it's uh one of the or the major implementation of the Jakarta Persistence specification. Um and as you might know, ORM 6 uh introduced a couple of radical changes. So when you migrated to Hibernate ORM 6, you probably had a couple of problems. Um but we tried to to keep our migration guide up to date you know, adding new things as we uh found

out about them. Um and we did those changes for a couple of good reasons. Uh performance is one. But um you know, also being able to drive new features, uh we just had to restructure re-implement a couple of the internals. Um and then since uh version 7, we switched to the Apache license. Before we were using uh the old GPL version 2.1, I believe. Um so yeah,

now we're also more business-friendly. Um and like I was saying, like we we tried to do some of those radical internal changes to support new features like more advanced SQL or SQL types like structs, XML, or JSON. And today I'm going to talk about uh JSON specifically. Um in the past I have also been um participating I have been uh a co-founder of a startup uh who

was trying to do something uh like an app, Tinder for uh fashion it was called. That was our uh tagline. And we were using JSON as well uh in that startup. So yeah, once upon a time the startup uh wanted to do a fashion product platform. And uh we had a product search, so we were trying to aggregate some data and show offers from different stores. and

you know, probably looked something like this. I don't have the real pictures anymore uh reasons, but um essentially we tried to get data from product catalogs and those came from uh affiliate partners because we tried to uh monetize based on like the affiliate links. you know, um the JSON data itself might look something like this. So, you know, you get this big JSON thing or you might

have a WSDL endpoint where you get a big XML, something like that. But, let's focus on the JSON part here. And so, we had some initial requirements like we wanted to extract certain data. Um most importantly, the EAN number, uh price URL, and so on into columns because we were mostly used to relational data. Um we also wanted to match products based on EAN because we integrated

data from different uh affiliates uh stores. And sometimes you just have the same product multiple times, and you don't want to show that to users, right? We also need regular data synchronization because product prices are updated or products are removed, new products are added. Um and we also stored the JSON blob just in case because sometimes uh you mess up something up in the import code, and

you only realize that or um you have to like backtrack, you have to go to the JSON where the data come came from to understand if uh you know, what went wrong and uh what how you might fix it. We wanted quick time to market. Uh that's also very important um in a in a startup area. So, we took the fast and easy solution. We just, you

know, took that JSON, mapped it to a map, and uh extracted the data, put it into uh So, we we had a of course a scheduled a cron job for the synchronization. But, we put it into like a entity similar looking like that. Um and here you can see I don't know if the pointer is good enough, but there's this JSON string here um with this JDBC

type code annotation that is uh a new thing in Hibernate 6 where you can specify uh what the SQL type of this thing is. so, we had a first app version uh, after a couple of hours of import because, you know, you're also as a startup very limited uh, with respect to the money that you can put into your database. You have to pay for IOs. Um,

so you take the uh, you you you're okay with a couple of hours of import just to save some money, you know, uh, we published the app and we wanted to present it, but then came the big problem. Uh, we forgot to extract the field and we were like a couple of hours before our presentation and what do you do? Um, you know, we were quick, we

could quickly release a new version of the server. But, um, you sometimes, you know, have to for uh, your quick to quick time to market um, you you have to uh, do things that might not be ideal, right? And we had the JSON uh, data around, so the JSON blob is our friend. but the extraction codes, like the extraction of the JSON itself, was tied to our

import code. Um, and so, what could we do? We could add a new column and try to like refill all the data, um, do a re-import, but that took a couple of hours. We wouldn't have the time to make it uh, for our presentation. Um, or we could try to leverage the JSON blob directly. Uh, this is uh, where this presentation actually starts. Um, so the JSON

functions that are available in modern SQL databases can give you access into the details of uh, that that JSON document. So, um, yeah, as I said, it's it's painful because uh, re-importing takes a lot of time and also sometimes you don't know if the data that you're trying to use in your app is actually really useful. You know, you're just pivoting like um you just have these

needs at this point. Um and there's these two functions, JSON_VALUE and JSON_QUERY, which you can um to access that data. So, we thought let's try to adapt the query. But first, let's look at those functions. Uh the JSON_VALUE function is a thing that you can use to extract the scalar out of a JSON document. It's based on a JSON path. There's this RFC 9593 specification or uh

request for comments uh document that describes how a JSON path looks like. And you might have heard of JSON path before, as I will show in a couple of slides, the JSON path support is not equal on all databases. But the JSON_VALUE function also has this returning um clause which allows you to specify the return type that you want. It's like a cast, so to say. And

then there is a couple of possibilities like the passing clause to do a parameterization of the JSON path or the on error and on empty clause to react on what happens if your JSON path you know, has a function that produces an error or uh in the end you just don't get a result out of uh querying with that JSON path. The JSON_QUERY function on the other

hand is uh more focused on extracting JSON objects. So, there's no return type per se, but rather you can specify if you want uh an array wrapper or uh you know, just a single object. And then also, there's the passing clause and the on error and empty clause, right? So, how does that look like in an HQL query, for example? So, if you have like a pre-filtered

uh data set, then you do just want to, you know, get some of of the additional data that you didn't have before. This is how it looks. So, the the syntax of the JSON path is starting with a dollar sign, usually, to uh reflect the document root. Then you have the dot operator to uh dereference the JSON nodes, so to say. Right. And how does it look

like from a database uh support perspective? We implemented support in Hibernate ORM for all the databases that have reasonable support for JSON, which includes uh even H2, although it was uh a bit more involved. But then there is a bit of differentiation between simple JSON path uh or wild card wild card support. Um because not every database likes to return multiple uh elements out of a JSON

from a JSON path query. Um so you just have to be careful what kind of JSON paths uh you're going to use. And so we saved the day, right? Uh the startup went on. We we were able to deploy a new application version, and uh yeah, we had that one missing field in there, and all was good. Right. So, um then we wanted to go a bit

deeper, because obviously this was just a quick fix, and uh it might have might not have been the the best performing solution, after all. And so you you have to understand there is a couple of challenges uh with JSON extraction. So, there's this question of how do you extract data from, let's say, an array where uh you have product variants for every product, right? Uh like different

colors or different sizes. And maybe you want to know like the actual price of uh or, you know, some tags of a product variant based on the product variant ID or whatever it is, right? So, you you have this needs to uh uh access array elements in some way or fashion. Then you might also want uh arrays to be transformed into rows so that you could do

aggregation or things like that. And then there's also this question of does it actually perform if you have multiple of those JSON value or JSON query uh function expressions in your HQL or ultimately then also in your SQL. Um and like I was trying to say in the beginning the JSON path uh standard is a standard but not every database uh supports it fully. So, what about

the database portability? Um and JSON path is interesting because it has path filters. So, it has this uh notion of a pipe. Uh have like uh the path and then you have this operator, the question mark, which you can uh Yeah, imagine like the the pipe operator in Linux. Uh so, you pass downstream values to the next expression. So, you can uh drill down. And then you

have also functions to operate on values. Looks like this roughly. So, you can imagine the first part here is uh with the star, it's uh taking the array apart into its individual elements. And then with the at sign, that's a special um qualifier to address the current element of this downstream filter. So, you can filter and say, "Okay, the thing with ID one or two, you want

the name out of that, right?" Um but there's a couple of disadvantages like it's not uh supported on all databases. And we also don't go that far to emulate that. So, you have to be careful there. This is not um fully supported. Um and the aggregation support is also lacking. So, you don't you don't have the possibility to take an array and you know, get the sum

out of some um integer or you know, get the maximum value. You still have to like transform it somehow into a relational representation uh to be able to do that. yeah, the parsing or processing performance is a a major um thought that to address as well. So, there is this JSON table function which tries to address all those needs. JSON table is uh here to turn it

into a relational representation. So, you have to imagine you're going to tell um the database, "This is my document and I want this structure. So, please take it apart into these uh constituents." Um this is the column list, so you define that. And this is only going to parse or process the JSON document once. How does that look like? It's quite complicated, actually. And don't read it.

It's you know, just there for uh afterwards if you want to study it a bit more uh from an overview point of view. But, this is something called a set returning function, or some databases also call it table valued function, or just table function. Um this is something that we added in Hibernate ORM 7. the thing is that this function is not like a regular function, but

it goes into the from clause. And being in the from clause, it means that it will produce rows or um you know, results, so to say, because in in HQL we don't talk in terms of uh rows per se. Um and if you want to use it in a join, then usually you also have to use a lateral keyword. And lateral means that you're essentially allowed to

access data that on the left side of where it was uh written. So, say you have a a document um document entity with a JSON then you need to uh like put the lateral keyword to be able to refer to the JSON column or JSON field in that table function, right? Um and how does that look like in HQL? this is just a simple example to show

how uh you can do aggregation based on yeah. The JSON table itself is not not that uh complicated. You know, we're just going to take the document as it is, and then we're going to rely on some of the defaults here. We have a the tags and terms. Um and for the tags, we're using the JSON type, which roughly means like we're just taking the JSON query

out of Um and the name signifies also the the JSON path that we're going to use to extract uh the data out of the the And then there's this nested notion. So, a notion of uh arrays take apart uh and have the individual elements be accessible of the arrays. So, this is not going to just produce a uh a row or a result per per object in

the JSON itself, but also an additional result per product variant. So, this is like a join uh against the the array, so to say. Um the support for databases is usually very good because we try to we're we're very narrow in what we support here. Uh we also try to follow the SQL specification as good as we can. Um there is a couple of notes, though. Um

sometimes the JSON table implementation has to be transformed into individual JSON value or JSON query invocations. Um and some databases like don't support unwrapping a documents, so you have to emulate it a bit by generating some rows querying the individual array elements. But, I mean, the production useful usually support this out of the box and I I'd say for H2, the implementation is good enough for your

automated testing if you want to just quickly test things. Right, so there's a couple of advantages of this. There's the database portability, right? So, you don't have to worry about the JSON path anymore. Is it supported the thing that you're going to use or not? You can aggregate your data because it's relational. It's only processed once. Theoretically, unless we have to emulate it with JSON value and

JSON query. But, maybe database vendors will catch up and then we can change the translation strategy. And supposedly it's easier to read because if you have deep paths and you're you know, repeating yourself over and over, then it's going to be a mess, probably. But, there's also a couple of challenges. So, you can use the blob, but you have to be careful. Um it's data modeling at

query time. That means that there is no there is repetitiveness, but there's also no data conformance guarantees. So, you don't know if the thing that you're trying to extract is really going to be an integer or is it going to be you know, a string or whatever. So, what happens there, right? Um so, back to relational or what is the solution? Actually, we can go a bit

beyond this simple query. Hibernate has something called uh structured data as as document or like I call it aggregate embeddables since ORM 6.2, Which um allows you to model the contents of uh JSON, XML, or you know, struct this is modeled anywhere already. But it's using the same mechanism. And you can have constraints for those nested fields. So uh what Hibernate can do is it can uh

create check constraints for those paths, so that your data on insert is going to be validated. And and you know it works. Um the canonical Java model for querying is also very useful, so you don't have to repeat those paths in all those different queries that you're using. And it can be combined with something called embeddable inheritance, which is also new, I think as of ORM 6.6.

where you can do the the inheritance mappings that you know from uh JPA entities, like single table inheritance with a discriminator. You can also do with embeddables now. And this is very nice for for a JSON model, because usually in in the JSON model, you you have that kind of uh inheritance. How does that look like? Um So as you can see here, where instead of having

a string field for a JSON, we're still marking it as being of type JSON, but now we're using an embeddable, the product embeddable. And the product embeddable is a regular embeddable, just like you write all your other embeddables. Um And here you can see that we have a JSON array within here, which points to another embeddable. So this is the product variant. Um and one of the

constraints that I wanted to show off here is like we're going to say the terms have to be given in a in one of those JSON documents. Um how does that look like in DDL? So this is just for PostgreSQL, an example. one thing that you can see here is that we're taking apart the JSON documents with this operator. It's a post-greSQL operator for accessing adjacent field,

casting it to a varchar and the ensuring it's not null. But, of course, we still allow the the JSON document itself to be null. So, this is the semantics that you can specify. And something also to note is that we're array types here and here. So, if you're on the entity level or on the relational side and you're using an array, then we're also going to use

the um relational array types if possible. The HQL query for this looks much cleaner now. As you can see, we can access the JSON parts here and here. So, it's like a regular embeddable. Um and the nice thing here is that you can join those arrays that you have specified in your embeddable. And this is going to turn into a JSON table expression behind the scenes automatically.

Um the database support is is interesting. JSON is supported on all the databases usually. That's lingua franca, as people might say, but uh structs uh are not supported on all databases. And we could in theory also support SQL Server, um but that needs CLR and it's a bit complicated. Um I have yet to find a user who actually wants this. Um but, are any Maybe a Do

a raise of hands. Who uses SQL Server here? No. No one. Okay. So, maybe there's no need after all. yeah, but uh you can use that. And then there's this question about filtering, right? Because so far we only extracted data for uh showing it. But, you might also want to filter based on the infer- information that you have uh in the JSON document. there's a couple of

questions like can we also filter arrays? Like can we understand does an array contain something of this shape? Uh and what about the performance? The performance is very important. We're going to look into that as well. So basic filtering, um if you take a look at uh this example that we just had with the embeddable, uh the basic filtering is very simple, right? So you just access

you use your relational operators. Um and then the array filtering requires a so-called unnest, but you don't see the unnest or the JSON table invocation itself. You can just take the array and put it in the from clause and it's going to do that behind the scenes for you. So you're just going to see is the is the tag contained in that array or not? And the

performance is okay, I would You can create an index on this expression, specifically this this casted access expression. Uh since we're using a like predicate on PostgreSQL, um I guess most of you use PostgreSQL, so I thought this is most relevant. Um you're going to have to use like the this trigram uh op operations uh so that it can actually use an index. Um and then you

can see bitmap index scan on on this uh thing here, which is nice, but you have to be careful because the recheck is maybe very expensive. Especially if the JSON document is on on the toast table. Um so if if it has to rematerialize it from multiple uh pages on the on the disk, it's going to be very expensive. So you might want to use a generated

column for that. So you can create a I'm going to show you that actually. Um the generated column here is uh for the generated terms. Um there is a Hibernate way to to make uh to make Hibernate understand this, but this is a separate field, right? Um but this is what you would need if you wanted uh to have best performance. Unless you know your JSON documents

are very small, never going to be outside of the page uh where the row is stored. and then you can also override this with a dialect override uh of the annotations for every uh database dialect. So, you have a a bit of freedom there. The array filter performance, on the other hand, um is a bit more involved. um this is the the SQL that that is generated

for this uh exists subquery on PostgreSQL at least. And what you can see here is that it's going to do a table function scan on top of a sequential scan. No bueno. Yeah. what are you going to do? You have to apply a little trick. And the trick is you need a special function uh to turn this JSON array into like a PostgreSQL array. And with this

PostgreSQL array then you can actually create a gin index um a general inverted uh index varchar array. Uh you also have to let Hibernate know about this function. And if you then use the JSON to string thing here, to string array, turns it into a string array, which Hibernate supports through its built-in array functions. And with the array contains function that we offer, you're going to get

this special um it's this one here, this at and then the greater sign, which is a containment operator on PostgreSQL. And for that operator, this index can actually do a a quick look up. But it will again have to do a recheck because general inverted indexes are usually not fully exact. They're approximate. They can have false positives, is my understanding. Um so yeah, you might want to

do a generated stored column for that as Especially if your JSON document is big. here comes the conclusion. Hibernate has a lot of mighty tools for JSON as of the latest few versions. Um but you have to be careful because good performance requires some extra work. There's lots of things that lots of traps that you can fall into. I only have covered PostgreSQL here because PostgreSQL is

the database that I know best. But I know Oracle is a lot better in trying to match the JSON value functions so that it only processes um the JSON document once. monitor performance. Be careful with the index rechecks. Uh the relational model can still be faster, but like I was trying to say, this is about time to market. Uh which is very important in a startup. If

you have any questions, you can follow up on X or via email if you want. I also have examples on this uh project here on GitHub, which I will make public right in a few seconds. Um and then I also thought I'd give you a couple of infos on what happened to the startup. Of course, the startup dissolved like many other startups. We were bootstrapping back then.

Um and so it was not because of using JSON, trust me. Um but we didn't find the right investor in time. We ran out of money. Uh and couldn't sustain it with the little bits and pieces that we got from affiliate partnerships. And also we had a couple of disputes in the company, but if you want to know more, uh let me know afterwards. And uh with

that, I will switch to some questions if you have some. I have this thing here open. Whoop. Are there no questions? Oh, yeah. Uh thank you. Sure, please go ahead. >> Okay, so I have a database table of PostgreSQL. >> Mhm. >> And there I have ID and >> And in this JSON, I have a list of objects, and within these objects, there is a property. I

want to be able to sort by the first instance of the property in the JSON. Can I do this efficiently? >> You can, yes. Um so set returning functions have uh something called an ordinality keyword. So you can actually have the index of the thing of the array that you're unwrapping. So what you could do is when you use that function, uh have the have an alias

for or actually you you use the index Uh in JPQL, you have uh index and key functions to access uh from joined collections uh the index and keys respectively, and we just reuse that. But you have to mark the the join itself with with ordinality as the keyword. And if you do that, then you can access this property and just have a where filter to understand like

I want the first element of the array. You're welcome. Any other questions? Yeah? >> Um so so I enjoy JSON as much as the next guy, and you sort of gave us an example. It it kind of seems like you stumbled into this. Like you were you having this presentation with in a few hours and you took JSON data presentation. >> I I kind of still struggle

to see the usefulness of this. >> The use >> I mean where would you I mean storing JSON in a SQL database, why >> Why not just why not use a no SQL database for that part of the you >> Yeah, it's debatable. I mean sometimes um maybe I should rephrase the the question. The question was that um why not use a no SQL database um because

why would you store JSON in in the SQL database? You know, sometimes your business uh just works around the SQL database, and you don't want the problems of having to manage two different transactions or uh you know, two-phase commit or whatever. Uh have to just be very fast, quick time to market. But there are also some benefits uh like this dense storage of JSON um is is

nice because you can avoid some of the joins, right? And if you keep your JSON very small, then the database only has to do like single IO lookup instead of going to like tens or hundreds of random pages just to collect all the results. So there is a fine line between uh is it going to be a performance burden or is it actually going to improve your

performance? But depending on the database that you have, um you might also just use arrays or structs, right? Which is probably um the the better way to model those uh structured document things. Yes, please. >> Oracle has this signal object document API which allows storing and materializing inline documents without further transformations. You think this is an extension of this JSON theme in bypassing JSON ID somehow and

letting the database work with all these details and would that be something that you could see in Hibernate? Are you asking about the JSON uh relational duality which they introduced in uh Oracle 23c AI? can JSON relational duality be useful to Hibernate ORM is the question, I guess. Uh we have talked with Oracle engineers about exactly this topic. Um but we didn't think in the first iteration

that this was a good match because um we don't have like names or anything for those JSON documents yet. Maybe if we introduce like a separate annotation to name those objects, um then we could create those JSON um duality views, I think they're called, um and then you would also have a way with SQL itself to make use of it. But yeah, not in the first iteration.

But good point. Thank you. >> Can you please bring up the slide with the links just for a second? >> With the links? Yeah, sure. But like I said, the the examples are not yet online. I have to make them public. Sorry, yeah. Okay. Any more questions? Okay. Well, then I'll leave you to lunch and you can come to me afterwards and

From event

Spring I/O

13 Apr 2026 – 15 Apr 2026

All event videos
Back to Watch