HHH-12662 - JPQL queries fail when using the Java attribute type which has an associated AttributeConverter (only the DB column type works)
This commit is contained in:
parent
bff9e2e450
commit
30fec4be79
|
@ -3,6 +3,7 @@
|
|||
:modeldir: ../../../../../main/java/org/hibernate/userguide/model
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/mapping
|
||||
:resourcedir: ../../../../../test/resources/org/hibernate/userguide/
|
||||
:converter-sourcedir: ../../../../../../../hibernate-core/src/test/java/org/hibernate/test/converter
|
||||
:extrasdir: extras
|
||||
|
||||
Basic value types usually map a single database column, to a single, non-aggregated Java type.
|
||||
|
@ -536,6 +537,66 @@ JPA explicitly disallows the use of an AttributeConverter with an attribute mark
|
|||
So if using the AttributeConverter approach, be sure not to mark the attribute as `@Enumerated`.
|
||||
====
|
||||
|
||||
[[basic-attribute-converter-query-parameter]]
|
||||
====== Using the AttributeConverter entity property as a query parameter
|
||||
|
||||
Assuming you have the following entity:
|
||||
|
||||
[[basic-attribute-converter-query-parameter-entity-example]]
|
||||
.`Photo` entity with `AttributeConverter`
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{converter-sourcedir}/ConverterTest.java[tags=basic-attribute-converter-query-parameter-entity-example]
|
||||
----
|
||||
====
|
||||
|
||||
And the `Caption` class looks as follows:
|
||||
|
||||
[[basic-attribute-converter-query-parameter-object-example]]
|
||||
.`Caption` Java object
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{converter-sourcedir}/ConverterTest.java[tags=basic-attribute-converter-query-parameter-object-example]
|
||||
----
|
||||
====
|
||||
|
||||
And we have an `AttributeConverter` to handle the `Caption` Java object:
|
||||
|
||||
[[basic-attribute-converter-query-parameter-converter-example]]
|
||||
.`Caption` Java object AttributeConverter
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{converter-sourcedir}/ConverterTest.java[tags=basic-attribute-converter-query-parameter-converter-example]
|
||||
----
|
||||
====
|
||||
|
||||
Traditionally, you could only use the dbData `Caption` representation, which in our case is a `String`, when referencing the `caption` entity property.
|
||||
|
||||
[[basic-attribute-converter-query-parameter-converter-dbdata-example]]
|
||||
.Filtering by the `Caption` property using the DB data representation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{converter-sourcedir}/ConverterTest.java[tags=basic-attribute-converter-query-parameter-converter-dbdata-example]
|
||||
----
|
||||
====
|
||||
|
||||
In order to use the Java object `Caption` representation, you have to get the associated Hibernate `Type`.
|
||||
|
||||
[[basic-attribute-converter-query-parameter-converter-object-example]]
|
||||
.Filtering by the `Caption` property using the Java Object representation
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{converter-sourcedir}/ConverterTest.java[tags=basic-attribute-converter-query-parameter-converter-object-example]
|
||||
----
|
||||
====
|
||||
|
||||
By passing the associated Hibernate `Type`, you can use the `Caption` object when binding the query parameter value.
|
||||
|
||||
[[basic-hbm-attribute-converter]]
|
||||
====== Mapping an AttributeConverter using HBM mappings
|
||||
|
||||
|
|
|
@ -62,10 +62,10 @@ public class QueryParameterBindingImpl<T> implements QueryParameterBinding<T> {
|
|||
if ( isBindingValidationRequired ) {
|
||||
validate( value, clarifiedType );
|
||||
}
|
||||
bindValue( value );
|
||||
if ( clarifiedType != null ) {
|
||||
this.bindType = clarifiedType;
|
||||
}
|
||||
bindValue( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.converter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-12662")
|
||||
public class ConverterTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Photo.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterEntityManagerFactoryBuilt() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Photo photo = new Photo();
|
||||
photo.setId( 1 );
|
||||
photo.setName( "Dorobanțul" );
|
||||
photo.setCaption( new Caption( "Nicolae Grigorescu" ) );
|
||||
|
||||
entityManager.persist( photo );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJPQLUpperDbValueBindParameter() throws Exception {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::basic-attribute-converter-query-parameter-converter-dbdata-example[]
|
||||
Photo photo = entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Photo p " +
|
||||
"where upper(caption) = upper(:caption) ", Photo.class )
|
||||
.setParameter( "caption", "Nicolae Grigorescu" )
|
||||
.getSingleResult();
|
||||
//end::basic-attribute-converter-query-parameter-converter-dbdata-example[]
|
||||
|
||||
assertEquals( "Dorobanțul", photo.getName() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJPQLUpperAttributeValueBindParameterType() throws Exception {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::basic-attribute-converter-query-parameter-converter-object-example[]
|
||||
SessionFactory sessionFactory = entityManager.getEntityManagerFactory()
|
||||
.unwrap( SessionFactory.class );
|
||||
|
||||
MetamodelImplementor metamodelImplementor = (MetamodelImplementor) sessionFactory.getMetamodel();
|
||||
|
||||
Type captionType = metamodelImplementor
|
||||
.entityPersister( Photo.class.getName() )
|
||||
.getPropertyType( "caption" );
|
||||
|
||||
Photo photo = (Photo) entityManager.createQuery(
|
||||
"select p " +
|
||||
"from Photo p " +
|
||||
"where upper(caption) = upper(:caption) ", Photo.class )
|
||||
.unwrap( Query.class )
|
||||
.setParameter( "caption", new Caption("Nicolae Grigorescu"), captionType)
|
||||
.getSingleResult();
|
||||
//end::basic-attribute-converter-query-parameter-converter-object-example[]
|
||||
|
||||
assertEquals( "Dorobanțul", photo.getName() );
|
||||
} );
|
||||
}
|
||||
|
||||
//tag::basic-attribute-converter-query-parameter-object-example[]
|
||||
public static class Caption {
|
||||
|
||||
private String text;
|
||||
|
||||
public Caption(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
Caption caption = (Caption) o;
|
||||
return text != null ? text.equals( caption.text ) : caption.text == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return text != null ? text.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
//end::basic-attribute-converter-query-parameter-object-example[]
|
||||
|
||||
//tag::basic-attribute-converter-query-parameter-converter-example[]
|
||||
public static class CaptionConverter
|
||||
implements AttributeConverter<Caption, String> {
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(Caption attribute) {
|
||||
return attribute.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Caption convertToEntityAttribute(String dbData) {
|
||||
return new Caption( dbData );
|
||||
}
|
||||
}
|
||||
//end::basic-attribute-converter-query-parameter-converter-example[]
|
||||
|
||||
//tag::basic-attribute-converter-query-parameter-entity-example[]
|
||||
@Entity(name = "Photo")
|
||||
public static class Photo {
|
||||
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Convert(converter = CaptionConverter.class)
|
||||
private Caption caption;
|
||||
|
||||
//Getters and setters are omitted for brevity
|
||||
//end::basic-attribute-converter-query-parameter-entity-example[]
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Caption getCaption() {
|
||||
return caption;
|
||||
}
|
||||
|
||||
public void setCaption(Caption caption) {
|
||||
this.caption = caption;
|
||||
}
|
||||
//tag::basic-attribute-converter-query-parameter-entity-example[]
|
||||
}
|
||||
//end::basic-attribute-converter-query-parameter-entity-example[]
|
||||
}
|
Loading…
Reference in New Issue