i’m using postgresql with spring boot and I have a json column defined like this
ALTER TABLE core."foo"
ADD COLUMN if not exists config json;
the Foo
entity is mapped like this
import com.vladmihalcea.hibernate.type.json.JsonType;
import org.hibernate.annotations.Type;
...
@Type(JsonType.class)
private Config config;
the Foo
entity contains a lot of columns so I use a custom dto to perform some optimized select
@Query(
"""
SELECT new path.to.package.FooProjection(
f.id,
f.config
)
FROM Foo f
WHERE ...
""")
Optional<FooProjection> fetchMinimalFoo(Long fooId);
the FooProjection
is classic pojo with config
field mapped like this
@Getter
@Builder
@RequiredArgsConstructor
public class FooProjection {
private final Long id;
private final Config config;
the issue is when the query is executed I got this error
java.lang.IllegalStateException: Cannot instantiate class 'path.to.package.FooProjection'
(it has no constructor with signature [java.lang.Long, java.lang.Object], and not every argument has an alias)
It consider the json field as an object !!
2
Answers
After reading "Entities or DTOs – When should you use which projection?" and "Why, When and How to Use DTO Projections with JPA and Hibernate" from Thorben Janssen, it is clear that Hibernate uses constructors in DTO projections, and the constructor must match the exact types being returned by the query.
Since Hibernate sees the JSON field as a generic
Object
, or perhaps another default type likeString
, depending on your Hibernate dialect, you need to make sure yourFooProjection
constructor can handle this.Update the
FooProjection
to accept the JSON field as the type Hibernate is converting it to.If it is
String
(common case), you would have something like:If Hibernate is not naturally converting the JSON to a String, you might need to explicitly cast or convert it within the query. That is dependent on your specific database dialect. For PostgreSQL, you could modify the query to cast JSON to text:
That assumes that
config
can be represented entirely as a string.If
config
includes nested objects, and you are trying to retrieve a specific value, you would need to adjust the path in the#>>
operator accordingly.Make sure you have a method in your
Config
class or related utility to convert a JSON string back to yourConfig
object.The JSON to
Config
object conversion should happen at the right place in your code:The
FooProjection
DTO must use this method correctly: if theconfig
field inFooProjection
is of typeConfig
, you need to modify the constructor to parse the JSON string:Use
@JdbcTypeCode(SqlTypes.JSON)
, instead of@Type(JsonType.class)
Here is a full working example:
The entity:
The projection is same as yours:
The config POJO:
The query is same as yours, I only added
@Param("fooId")
.