HHH-9074 - HQL Query with boolean and @Convert
(cherry picked from commit ac2f06800e
)
This commit is contained in:
parent
4ee6b88b2b
commit
d8ab9c0905
|
@ -11,6 +11,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.type.LiteralType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
|
||||
|
||||
/**
|
||||
* Represents a boolean literal within a query.
|
||||
|
@ -21,13 +22,18 @@ public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAware
|
|||
private Type expectedType;
|
||||
|
||||
public Type getDataType() {
|
||||
return expectedType == null ? StandardBasicTypes.BOOLEAN : expectedType;
|
||||
return getExpectedType() == null ? StandardBasicTypes.BOOLEAN : getExpectedType();
|
||||
}
|
||||
|
||||
public Boolean getValue() {
|
||||
return getType() == TRUE ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String s) {
|
||||
super.setText( s );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpectedType(Type expectedType) {
|
||||
this.expectedType = expectedType;
|
||||
|
@ -41,16 +47,20 @@ public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAware
|
|||
@Override
|
||||
@SuppressWarnings( {"unchecked"})
|
||||
public String getRenderText(SessionFactoryImplementor sessionFactory) {
|
||||
try {
|
||||
return typeAsLiteralType().objectToSQLString( getValue(), sessionFactory.getDialect() );
|
||||
}
|
||||
catch( Exception t ) {
|
||||
throw new QueryException( "Unable to render boolean literal value", t );
|
||||
}
|
||||
}
|
||||
final boolean literalValue = getValue();
|
||||
|
||||
private LiteralType typeAsLiteralType() {
|
||||
return (LiteralType) getDataType();
|
||||
if ( expectedType instanceof AttributeConverterTypeAdapter ) {
|
||||
return determineConvertedValue( (AttributeConverterTypeAdapter) expectedType, literalValue );
|
||||
}
|
||||
else if ( expectedType instanceof LiteralType ) {
|
||||
try {
|
||||
return ( (LiteralType) expectedType ).objectToSQLString( getValue(), sessionFactory.getDialect() );
|
||||
}
|
||||
catch( Exception t ) {
|
||||
throw new QueryException( "Unable to render boolean literal value using expected LiteralType", t );
|
||||
}
|
||||
}
|
||||
|
||||
return sessionFactory.getDialect().toBooleanValueString( literalValue );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
package org.hibernate.hql.internal.ast.tree;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.util.Locale;
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
||||
import org.hibernate.hql.internal.ast.util.ColumnHelper;
|
||||
import org.hibernate.type.LiteralType;
|
||||
import org.hibernate.type.SingleColumnType;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -32,8 +35,8 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
|||
}
|
||||
|
||||
public Type getDataType() {
|
||||
if ( expectedType != null ) {
|
||||
return expectedType;
|
||||
if ( getExpectedType() != null ) {
|
||||
return getExpectedType();
|
||||
}
|
||||
|
||||
switch ( getType() ) {
|
||||
|
@ -81,21 +84,36 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
|||
|
||||
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() );
|
||||
}
|
||||
}
|
||||
setText( determineConvertedValue( adapterType, getLiteralValue() ) );
|
||||
this.expectedType = expectedType;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected String determineConvertedValue(AttributeConverterTypeAdapter converterTypeAdapter, Object literalValue) {
|
||||
if ( getDataType().getReturnedClass().equals( converterTypeAdapter.getModelType() ) ) {
|
||||
// apply the converter
|
||||
final AttributeConverter converter = converterTypeAdapter.getAttributeConverter();
|
||||
final Object converted = converter.convertToDatabaseColumn( getLiteralValue() );
|
||||
if ( isCharacterData( converterTypeAdapter.sqlType() ) ) {
|
||||
return "'" + converted.toString() + "'";
|
||||
}
|
||||
else {
|
||||
return converted.toString();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new QueryException(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"AttributeConverter domain-model attribute type [%s] did not match query literal type [%s]",
|
||||
converterTypeAdapter.getModelType().getName(),
|
||||
getDataType().getReturnedClass().getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCharacterData(int typeCode) {
|
||||
return Types.VARCHAR == typeCode
|
||||
|| Types.CHAR == typeCode
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
|
|||
import org.hibernate.hql.internal.antlr.SqlTokenTypes;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.internal.ast.InvalidPathException;
|
||||
import org.hibernate.hql.internal.ast.tree.BooleanLiteralNode;
|
||||
import org.hibernate.hql.internal.ast.tree.DotNode;
|
||||
import org.hibernate.hql.internal.ast.tree.FromClause;
|
||||
import org.hibernate.hql.internal.ast.tree.IdentNode;
|
||||
|
@ -184,17 +185,10 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
|
|||
}
|
||||
|
||||
public void processBoolean(AST constant) {
|
||||
// TODO: something much better - look at the type of the other expression!
|
||||
// TODO: Have comparisonExpression and/or arithmeticExpression rules complete the resolution of boolean nodes.
|
||||
String replacement = (String) walker.getTokenReplacements().get( constant.getText() );
|
||||
if ( replacement != null ) {
|
||||
constant.setText( replacement );
|
||||
}
|
||||
else {
|
||||
boolean bool = "true".equals( constant.getText().toLowerCase(Locale.ROOT) );
|
||||
Dialect dialect = walker.getSessionFactoryHelper().getFactory().getDialect();
|
||||
constant.setText( dialect.toBooleanValueString( bool ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void processLiteral(AST constant) {
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.test.converter;
|
|||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Converter;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
|
@ -15,6 +16,7 @@ import javax.persistence.Entity;
|
|||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
|
@ -23,6 +25,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Test AttributeConverter functioning in various Query scenarios.
|
||||
|
@ -34,7 +37,7 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
public static final float SALARY = 267.89f;
|
||||
|
||||
@Test
|
||||
public void testJpqlLiteral() {
|
||||
public void testJpqlFloatLiteral() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
Employee jDoe = (Employee) session.createQuery( "from Employee e where e.salary = " + SALARY + "f" ).uniqueResult();
|
||||
|
@ -43,6 +46,16 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
session.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJpqlBooleanLiteral() {
|
||||
Session session = openSession();
|
||||
session.getTransaction().begin();
|
||||
assertNotNull( session.createQuery( "from Employee e where e.active = true" ).uniqueResult() );
|
||||
assertNull( session.createQuery( "from Employee e where e.active = false" ).uniqueResult() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { Employee.class, SalaryConverter.class };
|
||||
|
@ -74,6 +87,8 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
@Embedded
|
||||
public Name name;
|
||||
public Float salary;
|
||||
@Convert( converter = BooleanConverter.class )
|
||||
public boolean active = true;
|
||||
|
||||
public Employee() {
|
||||
}
|
||||
|
@ -125,4 +140,29 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
|||
return new Float( ( dbData.floatValue() ) / 100 );
|
||||
}
|
||||
}
|
||||
|
||||
public static class BooleanConverter implements AttributeConverter<Boolean,Integer> {
|
||||
|
||||
@Override
|
||||
public Integer convertToDatabaseColumn(Boolean attribute) {
|
||||
if ( attribute == null ) {
|
||||
return null;
|
||||
}
|
||||
return attribute ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean convertToEntityAttribute(Integer dbData) {
|
||||
if ( dbData == null ) {
|
||||
return null;
|
||||
}
|
||||
else if ( dbData == 0 ) {
|
||||
return false;
|
||||
}
|
||||
else if ( dbData == 1 ) {
|
||||
return true;
|
||||
}
|
||||
throw new HibernateException( "Unexpected boolean numeric; expecting null, 0 or 1, but found " + dbData );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue