HHH-8478 - AttributeConverters need to be applied to JPQL and Criteria queries
This commit is contained in:
parent
2243fa68ff
commit
498735aa37
|
@ -53,6 +53,7 @@ import java.util.jar.JarFile;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.MapsId;
|
import javax.persistence.MapsId;
|
||||||
|
@ -2632,6 +2633,42 @@ public class Configuration implements Serializable {
|
||||||
addAttributeConverter( attributeConverter, autoApply );
|
addAttributeConverter( attributeConverter, autoApply );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the AttributeConverter Class to this Configuration.
|
||||||
|
*
|
||||||
|
* @param attributeConverterClass The AttributeConverter class.
|
||||||
|
*/
|
||||||
|
public void addAttributeConverter(Class<? extends AttributeConverter> attributeConverterClass) {
|
||||||
|
final AttributeConverter attributeConverter;
|
||||||
|
try {
|
||||||
|
attributeConverter = attributeConverterClass.newInstance();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new AnnotationException(
|
||||||
|
"Unable to instantiate AttributeConverter [" + attributeConverterClass.getName() + "]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAttributeConverter( attributeConverter );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the AttributeConverter instance to this Configuration. This form is mainly intended for developers
|
||||||
|
* to programatically add their own AttributeConverter instance. HEM, instead, uses the
|
||||||
|
* {@link #addAttributeConverter(Class, boolean)} form
|
||||||
|
*
|
||||||
|
* @param attributeConverter The AttributeConverter instance.
|
||||||
|
*/
|
||||||
|
public void addAttributeConverter(AttributeConverter attributeConverter) {
|
||||||
|
boolean autoApply = false;
|
||||||
|
Converter converterAnnotation = attributeConverter.getClass().getAnnotation( Converter.class );
|
||||||
|
if ( converterAnnotation != null ) {
|
||||||
|
autoApply = converterAnnotation.autoApply();
|
||||||
|
}
|
||||||
|
|
||||||
|
addAttributeConverter( new AttributeConverterDefinition( attributeConverter, autoApply ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the AttributeConverter instance to this Configuration. This form is mainly intended for developers
|
* Adds the AttributeConverter instance to this Configuration. This form is mainly intended for developers
|
||||||
* to programatically add their own AttributeConverter instance. HEM, instead, uses the
|
* to programatically add their own AttributeConverter instance. HEM, instead, uses the
|
||||||
|
|
|
@ -228,10 +228,10 @@ public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryO
|
||||||
protected Type extractDataType(Node operand) {
|
protected Type extractDataType(Node operand) {
|
||||||
Type type = null;
|
Type type = null;
|
||||||
if ( operand instanceof SqlNode ) {
|
if ( operand instanceof SqlNode ) {
|
||||||
type = ( ( SqlNode ) operand ).getDataType();
|
type = ( (SqlNode) operand ).getDataType();
|
||||||
}
|
}
|
||||||
if ( type == null && operand instanceof ExpectedTypeAwareNode ) {
|
if ( type == null && operand instanceof ExpectedTypeAwareNode ) {
|
||||||
type = ( ( ExpectedTypeAwareNode ) operand ).getExpectedType();
|
type = ( (ExpectedTypeAwareNode) operand ).getExpectedType();
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,25 +23,37 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.hql.internal.ast.tree;
|
package org.hibernate.hql.internal.ast.tree;
|
||||||
|
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
import antlr.SemanticException;
|
import antlr.SemanticException;
|
||||||
|
|
||||||
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
||||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||||
|
import org.hibernate.type.SingleColumnType;
|
||||||
import org.hibernate.type.StandardBasicTypes;
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a literal.
|
* Represents a literal.
|
||||||
*
|
*
|
||||||
* @author josh
|
* @author josh
|
||||||
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class LiteralNode extends AbstractSelectExpression implements HqlSqlTokenTypes {
|
public class LiteralNode extends AbstractSelectExpression implements HqlSqlTokenTypes, ExpectedTypeAwareNode {
|
||||||
|
private Type expectedType;
|
||||||
|
|
||||||
public void setScalarColumnText(int i) throws SemanticException {
|
public void setScalarColumnText(int i) throws SemanticException {
|
||||||
ColumnHelper.generateSingleScalarColumn( this, i );
|
ColumnHelper.generateSingleScalarColumn( this, i );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type getDataType() {
|
public Type getDataType() {
|
||||||
|
if ( expectedType != null ) {
|
||||||
|
return expectedType;
|
||||||
|
}
|
||||||
|
|
||||||
switch ( getType() ) {
|
switch ( getType() ) {
|
||||||
case NUM_INT:
|
case NUM_INT:
|
||||||
return StandardBasicTypes.INTEGER;
|
return StandardBasicTypes.INTEGER;
|
||||||
|
@ -64,4 +76,53 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getLiteralValue() {
|
||||||
|
String text = getText();
|
||||||
|
if ( getType() == QUOTED_STRING ) {
|
||||||
|
text = text.substring( 1, text.length() -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
final Type inherentType = getDataType();
|
||||||
|
if ( inherentType == null ) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ( (SingleColumnType) inherentType ).fromStringValue( text );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExpectedType(Type expectedType) {
|
||||||
|
if ( this.expectedType != null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( AttributeConverterTypeAdapter.class.isInstance( expectedType ) ) {
|
||||||
|
final AttributeConverterTypeAdapter adapterType = (AttributeConverterTypeAdapter) expectedType;
|
||||||
|
if ( getDataType().getReturnedClass().equals( adapterType.getModelType() ) ) {
|
||||||
|
// apply the converter
|
||||||
|
final AttributeConverter converter = ( (AttributeConverterTypeAdapter) expectedType ).getAttributeConverter();
|
||||||
|
final Object converted = converter.convertToDatabaseColumn( getLiteralValue() );
|
||||||
|
if ( isCharacterData( adapterType.sqlType() ) ) {
|
||||||
|
setText( "'" + converted.toString() + "'" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setText( converted.toString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.expectedType = expectedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCharacterData(int typeCode) {
|
||||||
|
return Types.VARCHAR == typeCode
|
||||||
|
|| Types.CHAR == typeCode
|
||||||
|
|| Types.NVARCHAR == typeCode
|
||||||
|
|| Types.NCHAR == typeCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getExpectedType() {
|
||||||
|
return expectedType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -446,6 +446,8 @@ public class SimpleValue implements KeyValue {
|
||||||
name,
|
name,
|
||||||
attributeConverterDefinition.getAttributeConverter(),
|
attributeConverterDefinition.getAttributeConverter(),
|
||||||
sqlTypeDescriptorAdapter,
|
sqlTypeDescriptorAdapter,
|
||||||
|
entityAttributeJavaType,
|
||||||
|
databaseColumnJavaType,
|
||||||
entityAttributeJavaTypeDescriptor
|
entityAttributeJavaTypeDescriptor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,15 +40,22 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
||||||
private static final Logger log = Logger.getLogger( AttributeConverterTypeAdapter.class );
|
private static final Logger log = Logger.getLogger( AttributeConverterTypeAdapter.class );
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
|
private final Class modelType;
|
||||||
|
private final Class jdbcType;
|
||||||
private final AttributeConverter<? extends T,?> attributeConverter;
|
private final AttributeConverter<? extends T,?> attributeConverter;
|
||||||
|
|
||||||
public AttributeConverterTypeAdapter(
|
public AttributeConverterTypeAdapter(
|
||||||
String name,
|
String name,
|
||||||
AttributeConverter<? extends T,?> attributeConverter,
|
AttributeConverter<? extends T,?> attributeConverter,
|
||||||
SqlTypeDescriptor sqlTypeDescriptorAdapter,
|
SqlTypeDescriptor sqlTypeDescriptorAdapter,
|
||||||
|
Class modelType,
|
||||||
|
Class jdbcType,
|
||||||
JavaTypeDescriptor<T> entityAttributeJavaTypeDescriptor) {
|
JavaTypeDescriptor<T> entityAttributeJavaTypeDescriptor) {
|
||||||
super( sqlTypeDescriptorAdapter, entityAttributeJavaTypeDescriptor );
|
super( sqlTypeDescriptorAdapter, entityAttributeJavaTypeDescriptor );
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.modelType = modelType;
|
||||||
|
this.jdbcType = jdbcType;
|
||||||
this.attributeConverter = attributeConverter;
|
this.attributeConverter = attributeConverter;
|
||||||
|
|
||||||
log.debug( "Created AttributeConverterTypeAdapter -> " + name );
|
log.debug( "Created AttributeConverterTypeAdapter -> " + name );
|
||||||
|
@ -59,6 +66,14 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class getModelType() {
|
||||||
|
return modelType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class getJdbcType() {
|
||||||
|
return jdbcType;
|
||||||
|
}
|
||||||
|
|
||||||
public AttributeConverter<? extends T,?> getAttributeConverter() {
|
public AttributeConverter<? extends T,?> getAttributeConverter() {
|
||||||
return attributeConverter;
|
return attributeConverter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1102,7 +1102,12 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
||||||
List<Class> loadedAnnotatedClasses = (List<Class>) configurationValues.remove( AvailableSettings.LOADED_CLASSES );
|
List<Class> loadedAnnotatedClasses = (List<Class>) configurationValues.remove( AvailableSettings.LOADED_CLASSES );
|
||||||
if ( loadedAnnotatedClasses != null ) {
|
if ( loadedAnnotatedClasses != null ) {
|
||||||
for ( Class cls : loadedAnnotatedClasses ) {
|
for ( Class cls : loadedAnnotatedClasses ) {
|
||||||
cfg.addAnnotatedClass( cls );
|
if ( AttributeConverter.class.isAssignableFrom( cls ) ) {
|
||||||
|
cfg.addAttributeConverter( (Class<? extends AttributeConverter>) cls );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cfg.addAnnotatedClass( cls );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Inc.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.jpa.test.convert;
|
||||||
|
|
||||||
|
import javax.persistence.AttributeConverter;
|
||||||
|
import javax.persistence.Converter;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test AttributeConverter functioning in various Query scenarios.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class QueryTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
public static final float SALARY = 267.89f;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJpqlLiteral() {
|
||||||
|
EntityManager em = getOrCreateEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
Employee jDoe = em.createQuery( "from Employee e where e.salary = " + SALARY + "f", Employee.class ).getSingleResult();
|
||||||
|
assertNotNull( jDoe );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] { Employee.class, SalaryConverter.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUpTestData() {
|
||||||
|
EntityManager em = entityManagerFactory().createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.persist( new Employee( 1, new Name( "John", "Q.", "Doe" ), SALARY ) );
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanUpTestData() {
|
||||||
|
EntityManager em = entityManagerFactory().createEntityManager();
|
||||||
|
em.getTransaction().begin();
|
||||||
|
em.createQuery( "delete Employee" ).executeUpdate();
|
||||||
|
em.getTransaction().commit();
|
||||||
|
em.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "Employee" )
|
||||||
|
@Table( name = "EMP" )
|
||||||
|
public static class Employee {
|
||||||
|
@Id
|
||||||
|
public Integer id;
|
||||||
|
@Embedded
|
||||||
|
public Name name;
|
||||||
|
public Float salary;
|
||||||
|
|
||||||
|
public Employee() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Employee(Integer id, Name name, Float salary) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.salary = salary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Embeddable
|
||||||
|
public static class Name {
|
||||||
|
public String first;
|
||||||
|
public String middle;
|
||||||
|
public String last;
|
||||||
|
|
||||||
|
public Name() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Name(String first, String middle, String last) {
|
||||||
|
this.first = first;
|
||||||
|
this.middle = middle;
|
||||||
|
this.last = last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Converter( autoApply = true )
|
||||||
|
public static class SalaryConverter implements AttributeConverter<Float,Long> {
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("UnnecessaryBoxing")
|
||||||
|
public Long convertToDatabaseColumn(Float attribute) {
|
||||||
|
if ( attribute == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Long( (long)(attribute*100) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("UnnecessaryBoxing")
|
||||||
|
public Float convertToEntityAttribute(Long dbData) {
|
||||||
|
if ( dbData == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Float( ( dbData.floatValue() ) / 100 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue