Hibernate-reactive: Support for custom UserTypes

Created on 9 Jul 2020  Â·  30Comments  Â·  Source: hibernate/hibernate-reactive

Hello to all. Does Hibernate Reactive support custom UserTypes? I want to use Postgres JSONB Type and for that I was using the following library https://github.com/vladmihalcea/hibernate-types which builds it's custom types based on Hibernate'sAbstractSingleColumnStandardBasicType. Unfortunately just moving from Hibernate to Hibernate Reactive the columns that use these types are always null in the entities. As an alternative I found this article which builds it's JSONB Postgres support on the Hibernate's UserType. Is there any known limitation around defining custom types and using Hibernate Reactive at the moment ?

design

All 30 comments

Hi, I didn’t test UserType since that interface is rather tied to JDBC. (Though in principle, perhaps it could be made to mostly work.)

Instead, my idea was that people could use JPA converters. I did test that and AFAIK it works. Have you tried that?

I tried with a quick test using UserType and it doesn't seem to work because the PgClient throw an exception when running the query.

I tried with a quick test using UserType and it doesn't seem to work because the PgClient throw an exception when running the query.

But that might just be because our fake JDBC layer is incomplete. (Some unimplemented methods.)

To clarify, I've tried the example using vlad library:

    @TypeDefs({
            @TypeDef(name = "json", typeClass = JsonStringType.class),
            @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
    })
    @Entity(name="GuineaPig")
    @Table(name="Pig")
    public static class GuineaPig {
        @Id
        private Integer id;
        private String name;

        @Type(type = "jsonb")
        @Column(columnDefinition = "jsonb")
        private  Location location;

This is the error:

Caused by: io.vertx.core.impl.NoStackTraceThrowable: Parameter at position[0] with class = [com.fasterxml.jackson.databind.node.ObjectNode] and value = [{"country":"UK","city":"Gotham"}] can not be coerced to the expected class = [java.lang.Object] for encoding.

It seems that the reactive pg client uses a different type to convert to JSON.

@gavinking Should we support some kind of mapping for the pg-client types?

JSON (io.reactiverse.pgclient.data.Json)
JSONB (io.reactiverse.pgclient.data.Json)
POINT (io.reactiverse.pgclient.data.Point)
LINE (io.reactiverse.pgclient.data.Line)
LSEG (io.reactiverse.pgclient.data.LineSegment)
BOX (io.reactiverse.pgclient.data.Box)
PATH (io.reactiverse.pgclient.data.Path)
POLYGON (io.reactiverse.pgclient.data.Polygon)
CIRCLE (io.reactiverse.pgclient.data.Circle)

I think the client is expecting an object of type io.reactiverse.pgclient.data.Json. It seems we handle UserType correctly though

Right so that’s what I meant in my comment above. Our JDBC adaptors aren’t even close to a complete implementation of JDBC, and I see it as quite likely that they never will be. In particular, for more “exotic” JDBC types, it might be quite difficult to map them to/from the Vert.x API.

Should we support some kind of mapping for the pg-client types?

Well, sure, if it’s reasonably easy to do.

But if not, we should consider that perhaps UserType is just not the right abstraction here, and look for a different approach.

I mean, to the extent that existing UserTypes can be made to work without doing anything horrible, then sure, let’s make ‘em work. It will make migration easier. But I think it’s possible that we’ll quickly run into limitations as to what’s really possible there.

I will have a look

I will have a look

As a first step we should add a test for a totally plain-vanilla UserType that doesn’t use any fancy database types, just to check that the basic concept works (I don’t see why it shouldn’t).

@gavinking No I did not test Converters yet because I am using Hibernate together with Hibernate Types library. But the suggestion to use converters would mean that I will in the end put a plain Postgres Text/Varchar to the DB and not a Postgres JSONB type.

@gavinking No I did not test Converters yet because I am using Hibernate together with Hibernate Types library. But the suggestion to use converters would mean that I will in the end put a plain Postgres Text/Varchar to the DB and not a Postgres JSONB type.

To clarify: are you concerned about what type you have in Java or in the database? There’s two different things here.

  • One is to use JSONB as the column type, but in principle I would have thought that the drivers can transparently convert strings to that type. (Not sure, I never tried.)
  • The second is to use io.reactiverse.pgclient.data.Json as the Java type in your model. That’s a whole different problem and would indeed require something more like a UserType.

@gavinking
I want to use the Postgres JSONB type. I think it is not supported directly by Hibernate. That's why I relied on this https://vladmihalcea.com/how-to-map-json-objects-using-generic-hibernate-types/ . Or for example this https://thorben-janssen.com/persist-postgresqls-jsonb-data-type-hibernate/ where the guide uses UserType .

Right I understand that but have you tried doing something really simple like just mapping a Java String to a column of type JSONB? Would that be good enough if it worked?

@gavinking No I did not try that yet. Yes it would be good enough. I would just need to convert the JSON objects to JSON string prior to trying to insert my Entities in the DB.

OK then it may be that the Vert.x client already supports that type conversion, we would need to try it, but even if it doesn't, I guess I think it probably should.

@DavideD what's the status here? Do you want me to take a closer look at this one?

I got sidetracked. Feel free to tackle this if you have time.

In #301 I've added a test demonstrating that UserTypes work in HR, at least to the extent to which our PreparedStatement and ResultSet adaptors are faithful implementation of JDBC (which is not a particularly great extent).

@gavinking Should we support some kind of mapping for the pg-client types?

JSON (io.reactiverse.pgclient.data.Json)
JSONB (io.reactiverse.pgclient.data.Json)
POINT (io.reactiverse.pgclient.data.Point)
LINE (io.reactiverse.pgclient.data.Line)
LSEG (io.reactiverse.pgclient.data.LineSegment)
BOX (io.reactiverse.pgclient.data.Box)
PATH (io.reactiverse.pgclient.data.Path)
POLYGON (io.reactiverse.pgclient.data.Polygon)
CIRCLE (io.reactiverse.pgclient.data.Circle)

So the question now becomes: can we make getObject() and setObject() in our JDBC adaptors support these types without creating a hard dependency to the PostgreSQL client?

Would it be possible to have something like:

@Type(type="passthrough")
Circle circle

That doesn't do any conversion but just pass the value as is?

So the question now becomes: can we make getObject() and setObject() in our JDBC adaptors support these types without creating a hard dependency to the PostgreSQL client?

Well, I've tried this, and apparently we don't actually need to do anything special in order to handle io.vertx.core.json.JsonObject on Postgres or MySQL. You can just use getObject()/setObject() from your UserType.

This works:

public class Json implements UserType {

    @Override
    public int[] sqlTypes() {
        return new int[] {Types.OTHER};
    }

    @Override
    public Class returnedClass() {
        return JsonObject.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return Objects.equals(x,y);
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return Objects.hashCode(x);
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
        return rs.getObject(names[0]);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
        if (value==null) {
            st.setNull(index, Types.OTHER);
        }
        else {
            st.setObject(index, value);
        }
    }

    ...
}

Would it be possible to have something like:

@Type(type="passthrough")
Circle circle

That doesn't do any conversion but just pass the value as is?

Yes, that would work too.

See 54434c94b3ff57261fd905abaeb25961b2ed285c.

I would say we can close this issue.

What about the other types, like io.reactiverse.pgclient.data.Circle for example?

@akoufa now to give you concrete responses to your original questions:

Does Hibernate Reactive support custom UserTypes?

The answer is yes!

However, you need to be aware that even though we expose something that looks a lot like JDBC to your UserType, under the covers there is no JDBC connection, but a whole different non-blocking database client. So some things simply aren't going to work.

I was using the following library https://github.com/vladmihalcea/hibernate-types

So I think you'll need to be pretty realistic about that. The above library is written to work with JDBC, so while some of it's UserTypes might work, others won't.

As an alternative I found this article which builds it's JSONB Postgres support on the Hibernate's UserType.

Take a look at how I've implemented this in the test suite. The basic idea is that you need to pass a Vert.x JsonObject to the Vert.x PostgreSQL or MySQL client by just calling setObject().

What about the other types, like io.reactiverse.pgclient.data.Circle for example?

Should be the same deal, though I did not test.

By the way, io.reactiverse.pgclient.data is dead. It's for a previous version of the client. That's not what you should be looking at.

Here is the current list of types:

https://vertx.io/docs/vertx-pg-client/java/#_postgresql_type_mapping

I understand that but have you tried doing something really simple like just mapping a Java String to a column of type JSONB?

...

OK then it may be that the Vert.x client already supports that type conversion, we would need to try it, but even if it doesn't, I guess I think it probably should.

According to the docs I linked above, this does work. I even tested writing an integer directly to a JSONB-types column and it worked.

So in principle you don't even need to mess about with JsonObject if you don't want to. (It's probably cleaner though.)

Sure, could you add a test for some of the other specific types? Like line or circle. So that we can show what the mapping looks like

Hi!

Have you guys saw some library like https://github.com/vladmihalcea/hibernate-types to use with hibernate-reactive ?

If that not exists yet, i would like to build something for my project (using quarkus). I use a lot of JSON columns in postgres.

But i dont know if i get it wrong, but hibernate-reactive only allow custom type by implementing from UserType interface? (http://hibernate.org/reactive/documentation/1.0/reference/html_single/#_custom_types)

@AlexandreGuidin, I think @gavinking already answered your questions in one of the previous comments: https://github.com/hibernate/hibernate-reactive/issues/279#issuecomment-666289449

But if I'm missing something feel free to follow up with some more questions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Sanne picture Sanne  Â·  12Comments

markusdlugi picture markusdlugi  Â·  30Comments

DavideD picture DavideD  Â·  17Comments

yaakov-berkovitch picture yaakov-berkovitch  Â·  16Comments

tsegismont picture tsegismont  Â·  9Comments