Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ORM] Blog post on Boolean type type mapping in ORM 6 #164

Closed
wants to merge 2 commits into from

Conversation

jrenaat
Copy link
Contributor

@jrenaat jrenaat commented Apr 29, 2022

Signed-off-by: Jan Schatteman jschatte@redhat.com

@jrenaat jrenaat requested a review from sebersole April 29, 2022 15:26
@dreab8 dreab8 force-pushed the staging branch 2 times, most recently from 50b66fd to f1da3ec Compare May 6, 2022 15:55
@jrenaat jrenaat force-pushed the staging branch 2 times, most recently from ece6fed to 5736f30 Compare May 16, 2022 15:14
@Sanne Sanne force-pushed the staging branch 2 times, most recently from 2df319f to 9b924ef Compare May 16, 2022 16:44
@jrenaat jrenaat force-pushed the staging branch 2 times, most recently from 14a2401 to 1f96743 Compare May 24, 2022 20:29
[source, JAVA, indent=0]
----
// this Boolean attribute will be converted to a CHAR in the database
@JdbcTypeCode(SqlTypes.CHAR)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you verified that this actually works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Believe it or not, but yes, I actually have ;)
What makes you doubt it wouldn't, btw?

Copy link
Member

@sebersole sebersole May 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tells Hibernate which JDBC type to use, not how to perform a conversion from boolean -> char.

What gets stored when you do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in H2: CHAR(1) CHECK ("B" IN('N', 'Y'))
So, Hibernate does magic even beyond it's makers' knowledge ... :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth mentioning in the blog post, that Y/N is the "default" representation as per BooleanJavaType

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YesNoConverter:: encodes boolean values as 'Y' or 'N'

@jrenaat jrenaat changed the title [ORM] Blog post on custom basic type mapping in H6 [ORM] Blog post on Boolean type type mapping in ORM 6 May 25, 2022

This default, 'implicit' mapping can be modified in several ways.

== JPA compliant, using AttributeConverter
Copy link
Member

@sebersole sebersole May 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for JPA compliant, here. It adds no value.

I'd also condense this whole conversation into something like:

== AttributeConverter

Using an `AttributeConverter` is a natural way to map a boolean accounting for different storage representations.  
See the link:{url-hibernate-ug}#basic-jpa-convert[User Guide] for details about using `AttributeConverter`.

Hibernate provides 3 built-in `AttributeConverter` implementations for the most common cases:

YesNoConverter:: encodes boolean values as 'Y' or 'N'
TrueFalseConverter:: encodes boolean values as 'T' or 'F'
NumericBooleanConverter:: encodes boolean values as an integer, 1 for true and 0 for false


[[convert]]
.Using AttributeConverter
====
[source, JAVA, indent=0]
----
@Entity
class TheEntity {
    ...

    // this will get mapped to CHAR or NCHAR
    @Basic
    @Convert(converter = org.hibernate.type.YesNoConverter.class)
    boolean convertedYesNo;

    // this will get mapped to CHAR or NCHAR
    @Basic
    @Convert(converter = org.hibernate.type.TrueFalseConverter.class)
    boolean convertedTrueFalse;

    // this will get mapped to TINYINT
    @Basic
    @Convert(converter = org.hibernate.type.NumericBooleanConverter.class)
    boolean convertedNumeric;

    // use our custom converter
    @Basic
    @Convert(converter = CustomBooleanConverter.class)
    boolean convertedCustom;
}

@Converter
public class CustomBooleanConverter implements AttributeConverter<Boolean, Character> {
    ...
}
----
====


== Auto-applied AttributeConverter

In the above <<convert,examples>>, the conversions will only be applied to the annotated attribute.

Usually, however, this storage representation will be the same across an entire application or organization.  In such cases, annotating each and every boolean attribute individually is tedious and error prone.

Jakarta Persistence defines a portable way to auto-apply a converter to all boolean types found in the domain model using `jakarta.persistence.Converter#autoApply`.  


[[convert-auto]]
.Auto-applied AttributeConverter
====
[source, JAVA, indent=0]
----
@Converter( autoApply = true )
public class CustomBooleanConverter implements AttributeConverter<Boolean, Character> {
    ...
}
----
====

// We should actually move this discussion about how Hibernate becomes aware of auto-applied 
// converters into the User Guide.  I just noticed we don't really have any content there about that.
// And then:

See the link:{user-guide-url}[User Guide] for details about auto-applied converters.



[[convert-reg]]
== ConverterRegistration

Starting with the imminent 6.1 release, Hibernate also supports registering converters using the new `@ConverterRegistration` annotation.  This annotation is a corollary to the `orm.xml` `<converter/>` element.

Consider Hibernate's built-in `YesNoConverter`, and let's say we want to auto-apply it.  The converter itself is not defined for auto-application (and that would not be appropriate given potential discovery problems).

One option is to explicitly tell Hibernate about it using `orm.xml`:


.<converter/>
====
[source, JAVA, indent=0]
----
<entity-mappings>
    ...
        <converter>org.hibernate.type.YesNoConverter</converter>
    ...
</entity-mappings>
----
====


Another is to use this new annotation, which can be specified at the class or package level.  It's scope is global regardless.

.@ConverterRegistration
====
[source, JAVA, indent=0]
----
@ConverterRegistration( converter=org.hibernate.type.YesNoConverter.class, autoApply=true )
----
====

`ConverterRegistration` will be covered in more detail in the 6.1 version of the User Guide.


// == Hibernate Extensions
// === `@JdbcTypeCode / @JdbcType`


== `@JdbcTypeCode / @JdbcType`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seriously though Steve, it's one thing to correct obvious errors and/or omissions and suggest improvements, and another to rewrite the whole thing. At one point or another you're gonna have to either accept the fact that if someone else wrote something it's not gonna be 100% like you would have done it, or just write it yourself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are new to writing public blogs. You asked me for help. I am suggesting that you write way too much content for a blog post and how you can make the content simpler. If that irks you, sure.. I'll do it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't irk me, I just feel maybe you're overthinking this a bit. But sure, we can keep chipping away at this until it's reduced to nothing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've given my own 'twist' to your suggestions.
For one, I don't see the need for an Auto-applied AttributeConverter section, after all it's about attributeconverters with just an attribute set to true, imo a section of its own suggests that those are some kind of special entities in their own right.
I did not know that (or forgot about) putting a converter in orm.xml auto-applies it, so obviously that has to go in there.

@jrenaat jrenaat force-pushed the staging branch 2 times, most recently from c29bc25 to 594acaf Compare May 26, 2022 22:13
Copy link
Contributor

@beikov beikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would love to see a some kind of "conclusion" that gives a "recommendation" maybe (given that we can provide that). I think that with converters, literals might not work as expected i.e. the following code:

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery cq = session.createQuery( TestEntity.class );
cq.where( cb.equal( root.get( "convertedBoolean" ), cb.literal( true ) ) );

but we'd have to check to make sure. It would be nice though, if it doesn't work as expected (rendering as literal to SQL), that we "warn" users about this and hence provide guidance.

@sebersole
Copy link
Member

literals might not work as expected

They should. At least from HQL. Fairly certain there are tests for that. But more tests always ok :D

@yrodiere
Copy link
Member

Hey @jrenaat @sebersole @beikov, should we close this or do you still plan to merge it someday?

@yrodiere
Copy link
Member

Hey @jrenaat @sebersole @beikov, should we close this or do you still plan to merge it someday?

Bump.

@yrodiere
Copy link
Member

yrodiere commented Nov 6, 2023

Ok I'm pretty sure nobody cares by now... Closing, feel free to reopen :)

@yrodiere yrodiere closed this Nov 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants