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 javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Converter;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.MapsId;
|
||||
|
@ -2632,6 +2633,42 @@ public class Configuration implements Serializable {
|
|||
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
|
||||
* 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) {
|
||||
Type type = null;
|
||||
if ( operand instanceof SqlNode ) {
|
||||
type = ( ( SqlNode ) operand ).getDataType();
|
||||
type = ( (SqlNode) operand ).getDataType();
|
||||
}
|
||||
if ( type == null && operand instanceof ExpectedTypeAwareNode ) {
|
||||
type = ( ( ExpectedTypeAwareNode ) operand ).getExpectedType();
|
||||
type = ( (ExpectedTypeAwareNode) operand ).getExpectedType();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
|
|
@ -23,25 +23,37 @@
|
|||
*/
|
||||
package org.hibernate.hql.internal.ast.tree;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
import java.sql.Types;
|
||||
|
||||
import antlr.SemanticException;
|
||||
|
||||
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||
import org.hibernate.type.SingleColumnType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||
|
||||
/**
|
||||
* Represents a literal.
|
||||
*
|
||||
* @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 {
|
||||
ColumnHelper.generateSingleScalarColumn( this, i );
|
||||
}
|
||||
|
||||
public Type getDataType() {
|
||||
if ( expectedType != null ) {
|
||||
return expectedType;
|
||||
}
|
||||
|
||||
switch ( getType() ) {
|
||||
case NUM_INT:
|
||||
return StandardBasicTypes.INTEGER;
|
||||
|
@ -64,4 +76,53 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
|||
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,
|
||||
attributeConverterDefinition.getAttributeConverter(),
|
||||
sqlTypeDescriptorAdapter,
|
||||
entityAttributeJavaType,
|
||||
databaseColumnJavaType,
|
||||
entityAttributeJavaTypeDescriptor
|
||||
);
|
||||
}
|
||||
|
|
|
@ -40,15 +40,22 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
|||
private static final Logger log = Logger.getLogger( AttributeConverterTypeAdapter.class );
|
||||
|
||||
private final String name;
|
||||
|
||||
private final Class modelType;
|
||||
private final Class jdbcType;
|
||||
private final AttributeConverter<? extends T,?> attributeConverter;
|
||||
|
||||
public AttributeConverterTypeAdapter(
|
||||
String name,
|
||||
AttributeConverter<? extends T,?> attributeConverter,
|
||||
SqlTypeDescriptor sqlTypeDescriptorAdapter,
|
||||
Class modelType,
|
||||
Class jdbcType,
|
||||
JavaTypeDescriptor<T> entityAttributeJavaTypeDescriptor) {
|
||||
super( sqlTypeDescriptorAdapter, entityAttributeJavaTypeDescriptor );
|
||||
this.name = name;
|
||||
this.modelType = modelType;
|
||||
this.jdbcType = jdbcType;
|
||||
this.attributeConverter = attributeConverter;
|
||||
|
||||
log.debug( "Created AttributeConverterTypeAdapter -> " + name );
|
||||
|
@ -59,6 +66,14 @@ public class AttributeConverterTypeAdapter<T> extends AbstractSingleColumnStanda
|
|||
return name;
|
||||
}
|
||||
|
||||
public Class getModelType() {
|
||||
return modelType;
|
||||
}
|
||||
|
||||
public Class getJdbcType() {
|
||||
return jdbcType;
|
||||
}
|
||||
|
||||
public AttributeConverter<? extends T,?> getAttributeConverter() {
|
||||
return attributeConverter;
|
||||
}
|
||||
|
|
|
@ -1102,7 +1102,12 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
|
|||
List<Class> loadedAnnotatedClasses = (List<Class>) configurationValues.remove( AvailableSettings.LOADED_CLASSES );
|
||||
if ( loadedAnnotatedClasses != null ) {
|
||||
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