Permalink
Browse files

HHH-12132: HANA boolean type mapping doesn't work for existing schema…

… definitions

- introduce parameter hibernate.dialect.hana.use_legacy_boolean_type to enable
  switching between new and legacy behavior.
  • Loading branch information...
1 parent a0f430a commit 7b59cb2f8d57c145050982e0330e4add4266ddbd @breglerj breglerj committed with Naros Nov 28, 2017
@@ -54,6 +54,7 @@
import org.hibernate.dialect.pagination.LimitHelper;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.ConfigurationService.Converter;
+import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.BinaryStream;
import org.hibernate.engine.jdbc.BlobImplementer;
import org.hibernate.engine.jdbc.CharacterStream;
@@ -87,7 +88,9 @@
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
+import org.hibernate.type.descriptor.sql.BitTypeDescriptor;
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor;
+import org.hibernate.type.descriptor.sql.BooleanTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.NClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor;
@@ -662,15 +665,19 @@ public int getMaxLobPrefetchSize() {
}
private static final String MAX_LOB_PREFETCH_SIZE_PARAMETER_NAME = new String( "hibernate.dialect.hana.max_lob_prefetch_size" );
+ private static final String USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME = new String( "hibernate.dialect.hana.use_legacy_boolean_type" );
private static final int MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE = 1024;
+ private static final Boolean USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE = Boolean.FALSE;
private HANANClobTypeDescriptor nClobTypeDescriptor = new HANANClobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
private HANABlobTypeDescriptor blobTypeDescriptor = new HANABlobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
private HANAClobTypeDescriptor clobTypeDescriptor = new HANAClobTypeDescriptor( MAX_LOB_PREFETCH_SIZE_DEFAULT_VALUE );
+ private boolean useLegacyBooleanType = USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE.booleanValue();
+
/*
* Tables named "TYPE" need to be quoted
*/
@@ -1069,8 +1076,6 @@ public String getSequenceNextValString(final String sequenceName) {
@Override
protected SqlTypeDescriptor getSqlTypeDescriptorOverride(final int sqlCode) {
switch ( sqlCode ) {
- // case Types.BOOLEAN:
- // return BitTypeDescriptor.INSTANCE;
case Types.CLOB:
return this.clobTypeDescriptor;
case Types.NCLOB:
@@ -1080,6 +1085,8 @@ protected SqlTypeDescriptor getSqlTypeDescriptorOverride(final int sqlCode) {
case Types.TINYINT:
// tinyint is unsigned on HANA
return SmallIntTypeDescriptor.INSTANCE;
+ case Types.BOOLEAN:
+ return this.useLegacyBooleanType ? BitTypeDescriptor.INSTANCE : BooleanTypeDescriptor.INSTANCE;
default:
return super.getSqlTypeDescriptorOverride( sqlCode );
}
@@ -1499,6 +1506,9 @@ public Integer convert(Object value) {
if ( this.clobTypeDescriptor.getMaxLobPrefetchSize() != maxLobPrefetchSize ) {
this.clobTypeDescriptor = new HANAClobTypeDescriptor( maxLobPrefetchSize );
}
+
+ this.useLegacyBooleanType = configurationService.getSetting( USE_LEGACY_BOOLEAN_TYPE_PARAMETER_NAME, StandardConverters.BOOLEAN,
+ USE_LEGACY_BOOLEAN_TYPE_DEFAULT_VALUE ).booleanValue();
}
public SqlTypeDescriptor getBlobTypeDescriptor() {
@@ -1507,6 +1517,9 @@ public SqlTypeDescriptor getBlobTypeDescriptor() {
@Override
public String toBooleanValueString(boolean bool) {
+ if ( this.useLegacyBooleanType ) {
+ return bool ? "1" : "0";
+ }
return bool ? "true" : "false";
}
@@ -0,0 +1,248 @@
+/*
+ * 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.dialect.functional;
+
+import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.PreparedStatement;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.PersistenceException;
+
+import org.hibernate.Session;
+import org.hibernate.dialect.AbstractHANADialect;
+import org.hibernate.query.Query;
+import org.hibernate.testing.RequiresDialect;
+import org.hibernate.testing.TestForIssue;
+import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
+import org.junit.Test;
+
+/**
+ * Tests the correctness of the parameter hibernate.dialect.hana.use_legacy_boolean_type which controls the mapping of
+ * boolean types to be either TINYINT (parameter is set to true) or BOOLEAN (default behavior or parameter is set to
+ * false)
+ *
+ * @author Jonathan Bregler
+ */
+@RequiresDialect(value = { AbstractHANADialect.class })
+public class HANABooleanTest extends BaseCoreFunctionalTestCase {
+
+ private static final String ENTITY_NAME = "BooleanEntity";
+ private static final String LEGACY_ENTITY_NAME = "LegacyBooleanEntity";
+
+ @Override
+ protected void prepareTest() throws Exception {
+ doInHibernate( this::sessionFactory, session -> {
+ session.doWork( connection -> {
+ try ( PreparedStatement ps = connection
+ .prepareStatement( "CREATE COLUMN TABLE " + ENTITY_NAME + " (key INTEGER, bool BOOLEAN, PRIMARY KEY (key))" ) ) {
+ ps.execute();
+ }
+
+ try ( PreparedStatement ps = connection
+ .prepareStatement( "CREATE COLUMN TABLE " + LEGACY_ENTITY_NAME + " (key INTEGER, bool TINYINT, PRIMARY KEY (key))" ) ) {
+ ps.execute();
+ }
+ } );
+ } );
+ }
+
+ @Override
+ protected void cleanupTest() throws Exception {
+ doInHibernate( this::sessionFactory, session -> {
+ session.doWork( connection -> {
+ try ( PreparedStatement ps = connection.prepareStatement( "DROP TABLE " + ENTITY_NAME ) ) {
+ ps.execute();
+ }
+ catch (Exception e) {
+ // Ignore
+ }
+
+ try ( PreparedStatement ps = connection.prepareStatement( "DROP TABLE " + LEGACY_ENTITY_NAME ) ) {
+ ps.execute();
+ }
+ catch (Exception e) {
+ // Ignore
+ }
+ } );
+ } );
+ }
+
+ @Test
+ @TestForIssue(jiraKey = "HHH-12132")
+ public void testBooleanType() throws Exception {
+ rebuildSessionFactory( configuration -> {
+ configuration.setProperty( "hibernate.dialect.hana.use_legacy_boolean_type", Boolean.FALSE.toString() );
+ } );
+
+ Session s = openSession();
+ s.beginTransaction();
+
+ BooleanEntity entity = new BooleanEntity();
+ entity.key = Integer.valueOf( 1 );
+ entity.bool = Boolean.TRUE;
+
+ s.persist( entity );
+
+ s.flush();
+
+ s.getTransaction().commit();
+
+ s.clear();
+
+ Query<BooleanEntity> legacyQuery = s.createQuery( "select b from " + ENTITY_NAME + " b where bool = true", BooleanEntity.class );
+
+ BooleanEntity retrievedEntity = legacyQuery.getSingleResult();
+
+ assertEquals( Integer.valueOf( 1 ), retrievedEntity.key );
+ assertTrue( retrievedEntity.bool );
+ }
+
+ @Test
+ @TestForIssue(jiraKey = "HHH-12132")
+ public void testBooleanTypeDefaultBehavior() throws Exception {
+ rebuildSessionFactory();
+
+ Session s = openSession();
+ s.beginTransaction();
+
+ BooleanEntity entity = new BooleanEntity();
+ entity.key = Integer.valueOf( 1 );
+ entity.bool = Boolean.TRUE;
+
+ s.persist( entity );
+
+ s.flush();
+
+ s.getTransaction().commit();
+
+ s.clear();
+
+ Query<BooleanEntity> legacyQuery = s.createQuery( "select b from " + ENTITY_NAME + " b where bool = true", BooleanEntity.class );
+
+ BooleanEntity retrievedEntity = legacyQuery.getSingleResult();
+
+ assertEquals( Integer.valueOf( 1 ), retrievedEntity.key );
+ assertTrue( retrievedEntity.bool );
+ }
+
+ @Test(expected = PersistenceException.class)
+ @TestForIssue(jiraKey = "HHH-12132")
+ public void testLegacyBooleanType() throws Exception {
+ rebuildSessionFactory( configuration -> {
+ configuration.setProperty( "hibernate.dialect.hana.use_legacy_boolean_type", Boolean.FALSE.toString() );
+ } );
+
+ Session s = openSession();
+ s.beginTransaction();
+
+ LegacyBooleanEntity legacyEntity = new LegacyBooleanEntity();
+ legacyEntity.key = Integer.valueOf( 2 );
+ legacyEntity.bool = Boolean.FALSE;
+
+ s.persist( legacyEntity );
+ s.flush();
+
+ s.getTransaction().commit();
+
+ s.clear();
+
+ Query<LegacyBooleanEntity> query = s.createQuery( "select b from " + LEGACY_ENTITY_NAME + " b where bool = true", LegacyBooleanEntity.class );
+
+ query.getSingleResult();
+ }
+
+ @Test
+ @TestForIssue(jiraKey = "HHH-12132")
+ public void testLegacyBooleanTypeLegacyBehavior() throws Exception {
+ rebuildSessionFactory( configuration -> {
+ configuration.setProperty( "hibernate.dialect.hana.use_legacy_boolean_type", Boolean.TRUE.toString() );
+ } );
+
+ Session s = openSession();
+ s.beginTransaction();
+
+ LegacyBooleanEntity legacyEntity = new LegacyBooleanEntity();
+ legacyEntity.key = Integer.valueOf( 1 );
+ legacyEntity.bool = Boolean.TRUE;
+
+ s.persist( legacyEntity );
+
+ s.flush();
+
+ s.getTransaction().commit();
+
+ s.clear();
+
+ Query<LegacyBooleanEntity> legacyQuery = s.createQuery( "select b from " + LEGACY_ENTITY_NAME + " b where bool = true", LegacyBooleanEntity.class );
+
+ LegacyBooleanEntity retrievedEntity = legacyQuery.getSingleResult();
+
+ assertEquals( Integer.valueOf( 1 ), retrievedEntity.key );
+ assertTrue( retrievedEntity.bool );
+ }
+
+ @Test(expected = PersistenceException.class)
+ @TestForIssue(jiraKey = "HHH-12132")
+ public void testBooleanTypeLegacyBehavior() throws Exception {
+ rebuildSessionFactory( configuration -> {
+ configuration.setProperty( "hibernate.dialect.hana.use_legacy_boolean_type", Boolean.TRUE.toString() );
+ } );
+
+ Session s = openSession();
+ s.beginTransaction();
+
+ BooleanEntity entity = new BooleanEntity();
+ entity.key = Integer.valueOf( 2 );
+ entity.bool = Boolean.FALSE;
+
+ s.persist( entity );
+ s.flush();
+
+ s.getTransaction().commit();
+
+ s.clear();
+
+ Query<BooleanEntity> query = s.createQuery( "select b from " + ENTITY_NAME + " b where bool = true", BooleanEntity.class );
+
+ query.getSingleResult();
+ }
+
+ @Override
+ protected boolean createSchema() {
+ return false;
+ }
+
+ @Override
+ protected java.lang.Class<?>[] getAnnotatedClasses() {
+ return new java.lang.Class[]{
+ BooleanEntity.class, LegacyBooleanEntity.class
+ };
+ }
+
+ @Entity(name = LEGACY_ENTITY_NAME)
+ public static class LegacyBooleanEntity {
+
+ @Id
+ public Integer key;
+
+ public Boolean bool;
+ }
+
+ @Entity(name = ENTITY_NAME)
+ public static class BooleanEntity {
+
+ @Id
+ public Integer key;
+
+ public Boolean bool;
+ }
+
+}

0 comments on commit 7b59cb2

Please sign in to comment.