HHH-9074 - HQL Query with boolean and @Convert
This commit is contained in:
parent
6d590d76b6
commit
ac2f06800e
|
@ -11,6 +11,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.type.LiteralType;
|
import org.hibernate.type.LiteralType;
|
||||||
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 boolean literal within a query.
|
* Represents a boolean literal within a query.
|
||||||
|
@ -21,13 +22,18 @@ public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAware
|
||||||
private Type expectedType;
|
private Type expectedType;
|
||||||
|
|
||||||
public Type getDataType() {
|
public Type getDataType() {
|
||||||
return expectedType == null ? StandardBasicTypes.BOOLEAN : expectedType;
|
return getExpectedType() == null ? StandardBasicTypes.BOOLEAN : getExpectedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getValue() {
|
public Boolean getValue() {
|
||||||
return getType() == TRUE ;
|
return getType() == TRUE ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setText(String s) {
|
||||||
|
super.setText( s );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setExpectedType(Type expectedType) {
|
public void setExpectedType(Type expectedType) {
|
||||||
this.expectedType = expectedType;
|
this.expectedType = expectedType;
|
||||||
|
@ -41,16 +47,20 @@ public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAware
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings( {"unchecked"})
|
@SuppressWarnings( {"unchecked"})
|
||||||
public String getRenderText(SessionFactoryImplementor sessionFactory) {
|
public String getRenderText(SessionFactoryImplementor sessionFactory) {
|
||||||
try {
|
final boolean literalValue = getValue();
|
||||||
return typeAsLiteralType().objectToSQLString( getValue(), sessionFactory.getDialect() );
|
|
||||||
}
|
|
||||||
catch( Exception t ) {
|
|
||||||
throw new QueryException( "Unable to render boolean literal value", t );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private LiteralType typeAsLiteralType() {
|
if ( expectedType instanceof AttributeConverterTypeAdapter ) {
|
||||||
return (LiteralType) getDataType();
|
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;
|
package org.hibernate.hql.internal.ast.tree;
|
||||||
|
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
|
import java.util.Locale;
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
import org.hibernate.QueryException;
|
||||||
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.LiteralType;
|
||||||
import org.hibernate.type.SingleColumnType;
|
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;
|
||||||
|
@ -32,8 +35,8 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type getDataType() {
|
public Type getDataType() {
|
||||||
if ( expectedType != null ) {
|
if ( getExpectedType() != null ) {
|
||||||
return expectedType;
|
return getExpectedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( getType() ) {
|
switch ( getType() ) {
|
||||||
|
@ -81,21 +84,36 @@ public class LiteralNode extends AbstractSelectExpression implements HqlSqlToken
|
||||||
|
|
||||||
if ( AttributeConverterTypeAdapter.class.isInstance( expectedType ) ) {
|
if ( AttributeConverterTypeAdapter.class.isInstance( expectedType ) ) {
|
||||||
final AttributeConverterTypeAdapter adapterType = (AttributeConverterTypeAdapter) expectedType;
|
final AttributeConverterTypeAdapter adapterType = (AttributeConverterTypeAdapter) expectedType;
|
||||||
if ( getDataType().getReturnedClass().equals( adapterType.getModelType() ) ) {
|
setText( determineConvertedValue( adapterType, getLiteralValue() ) );
|
||||||
// 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;
|
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) {
|
private boolean isCharacterData(int typeCode) {
|
||||||
return Types.VARCHAR == typeCode
|
return Types.VARCHAR == typeCode
|
||||||
|| Types.CHAR == 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.antlr.SqlTokenTypes;
|
||||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||||
import org.hibernate.hql.internal.ast.InvalidPathException;
|
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.DotNode;
|
||||||
import org.hibernate.hql.internal.ast.tree.FromClause;
|
import org.hibernate.hql.internal.ast.tree.FromClause;
|
||||||
import org.hibernate.hql.internal.ast.tree.IdentNode;
|
import org.hibernate.hql.internal.ast.tree.IdentNode;
|
||||||
|
@ -184,17 +185,10 @@ public class LiteralProcessor implements HqlSqlTokenTypes {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processBoolean(AST constant) {
|
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() );
|
String replacement = (String) walker.getTokenReplacements().get( constant.getText() );
|
||||||
if ( replacement != null ) {
|
if ( replacement != null ) {
|
||||||
constant.setText( replacement );
|
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) {
|
private void processLiteral(AST constant) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.test.converter;
|
||||||
|
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Convert;
|
||||||
import javax.persistence.Converter;
|
import javax.persistence.Converter;
|
||||||
import javax.persistence.Embeddable;
|
import javax.persistence.Embeddable;
|
||||||
import javax.persistence.Embedded;
|
import javax.persistence.Embedded;
|
||||||
|
@ -15,6 +16,7 @@ import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
|
|
||||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
@ -23,6 +25,7 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNotNull;
|
import static junit.framework.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test AttributeConverter functioning in various Query scenarios.
|
* Test AttributeConverter functioning in various Query scenarios.
|
||||||
|
@ -34,7 +37,7 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
public static final float SALARY = 267.89f;
|
public static final float SALARY = 267.89f;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJpqlLiteral() {
|
public void testJpqlFloatLiteral() {
|
||||||
Session session = openSession();
|
Session session = openSession();
|
||||||
session.getTransaction().begin();
|
session.getTransaction().begin();
|
||||||
Employee jDoe = (Employee) session.createQuery( "from Employee e where e.salary = " + SALARY + "f" ).uniqueResult();
|
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();
|
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
|
@Override
|
||||||
protected Class<?>[] getAnnotatedClasses() {
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
return new Class[] { Employee.class, SalaryConverter.class };
|
return new Class[] { Employee.class, SalaryConverter.class };
|
||||||
|
@ -74,6 +87,8 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
@Embedded
|
@Embedded
|
||||||
public Name name;
|
public Name name;
|
||||||
public Float salary;
|
public Float salary;
|
||||||
|
@Convert( converter = BooleanConverter.class )
|
||||||
|
public boolean active = true;
|
||||||
|
|
||||||
public Employee() {
|
public Employee() {
|
||||||
}
|
}
|
||||||
|
@ -125,4 +140,29 @@ public class QueryTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
return new Float( ( dbData.floatValue() ) / 100 );
|
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