HHH-9534 - Exception with custom EnumType and Map relation

This commit is contained in:
Janario Oliveira 2014-12-05 17:56:01 -02:00 committed by Steve Ebersole
parent 3192e1db92
commit 7ccbd46932
12 changed files with 465 additions and 48 deletions

View File

@ -271,19 +271,12 @@ public class MapBinder extends CollectionBinder {
//do not call setType as it extract the type from @Type //do not call setType as it extract the type from @Type
//the algorithm generally does not apply for map key anyway //the algorithm generally does not apply for map key anyway
elementBinder.setKey(true); elementBinder.setKey(true);
MapKeyType mapKeyTypeAnnotation = property.getAnnotation( MapKeyType.class );
if ( mapKeyTypeAnnotation != null
&& !BinderHelper.isEmptyAnnotationValue( mapKeyTypeAnnotation.value() .type() ) ) {
elementBinder.setExplicitType( mapKeyTypeAnnotation.value() );
}
else {
elementBinder.setType( elementBinder.setType(
property, property,
keyXClass, keyXClass,
this.collection.getOwnerEntityName(), this.collection.getOwnerEntityName(),
holder.keyElementAttributeConverterDefinition( keyXClass ) holder.keyElementAttributeConverterDefinition( keyXClass )
); );
}
elementBinder.setPersistentClassName( propertyHolder.getEntityName() ); elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
elementBinder.setAccessType( accessType ); elementBinder.setAccessType( accessType );
mapValue.setIndex( elementBinder.make() ); mapValue.setIndex( elementBinder.make() );

View File

@ -21,6 +21,7 @@ import javax.persistence.TemporalType;
import org.hibernate.AnnotationException; import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.Nationalized; import org.hibernate.annotations.Nationalized;
import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
@ -147,7 +148,18 @@ public class SimpleValueBinder {
isNationalized = property.isAnnotationPresent( Nationalized.class ) isNationalized = property.isAnnotationPresent( Nationalized.class )
|| buildingContext.getBuildingOptions().useNationalizedCharacterData(); || buildingContext.getBuildingOptions().useNationalizedCharacterData();
Type annType = property.getAnnotation( Type.class ); Type annType = null;
if ( (!key && property.isAnnotationPresent( Type.class ))
|| (key && property.isAnnotationPresent( MapKeyType.class )) ) {
if ( key ) {
MapKeyType ann = property.getAnnotation( MapKeyType.class );
annType = ann.value();
}
else {
annType = property.getAnnotation( Type.class );
}
}
if ( annType != null ) { if ( annType != null ) {
setExplicitType( annType ); setExplicitType( annType );
type = explicitType; type = explicitType;

View File

@ -19,46 +19,29 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import org.hibernate.annotations.Formula; import org.hibernate.annotations.*;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.test.annotations.enumerated.custom_types.LastNumberType;
import org.hibernate.annotations.TypeDefs; import org.hibernate.test.annotations.enumerated.enums.Common;
import org.hibernate.test.annotations.enumerated.enums.FirstLetter;
import org.hibernate.test.annotations.enumerated.enums.LastNumber;
import org.hibernate.test.annotations.enumerated.enums.Trimmed;
/** /**
* @author Janario Oliveira * @author Janario Oliveira
* @author Brett Meyer * @author Brett Meyer
*/ */
@Entity @Entity
@TypeDefs({ @TypeDef(typeClass = LastNumberType.class, defaultForType = EntityEnum.LastNumber.class) }) @TypeDefs({ @TypeDef(typeClass = LastNumberType.class, defaultForType = LastNumber.class) })
public class EntityEnum { public class EntityEnum {
enum Common {
A1, A2, B1, B2
}
enum FirstLetter {
A_LETTER, B_LETTER, C_LETTER
}
enum LastNumber {
NUMBER_1, NUMBER_2, NUMBER_3
}
enum Trimmed {
A, B, C
}
@Id @Id
@GeneratedValue @GeneratedValue
private long id; private long id;
private Common ordinal; private Common ordinal;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private Common string; private Common string;
@Type(type = "org.hibernate.test.annotations.enumerated.FirstLetterType") @Type(type = "org.hibernate.test.annotations.enumerated.custom_types.FirstLetterType")
private FirstLetter firstLetter; private FirstLetter firstLetter;
private LastNumber lastNumber; private LastNumber lastNumber;
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)

View File

@ -22,15 +22,16 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.type.EnumType; import org.hibernate.type.EnumType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.junit.Test;
import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.annotations.enumerated.EntityEnum.Common; import org.hibernate.test.annotations.enumerated.custom_types.FirstLetterType;
import org.hibernate.test.annotations.enumerated.EntityEnum.FirstLetter; import org.hibernate.test.annotations.enumerated.custom_types.LastNumberType;
import org.hibernate.test.annotations.enumerated.EntityEnum.LastNumber; import org.hibernate.test.annotations.enumerated.enums.Common;
import org.hibernate.test.annotations.enumerated.EntityEnum.Trimmed; import org.hibernate.test.annotations.enumerated.enums.FirstLetter;
import org.hibernate.test.annotations.enumerated.enums.LastNumber;
import org.hibernate.test.annotations.enumerated.enums.Trimmed;
import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -0,0 +1,51 @@
/*
* 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.annotations.enumerated.custom_mapkey;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MapKeyEnumerated;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
import org.hibernate.test.annotations.enumerated.custom_types.LastNumberType;
import org.hibernate.test.annotations.enumerated.enums.Common;
import org.hibernate.test.annotations.enumerated.enums.FirstLetter;
import org.hibernate.test.annotations.enumerated.enums.LastNumber;
/**
* @author Janario Oliveira
*/
@Entity
@TypeDefs({@TypeDef(typeClass = LastNumberType.class, defaultForType = LastNumber.class)})
public class EntityMapEnum {
@Id
@GeneratedValue
int id;
@ElementCollection
Map<Common, String> ordinalMap = new HashMap<Common, String>();
@ElementCollection
@MapKeyEnumerated(EnumType.STRING)
Map<Common, String> stringMap = new HashMap<Common, String>();
@ElementCollection
@MapKeyType(@Type(type = "org.hibernate.test.annotations.enumerated.custom_types.FirstLetterType"))
Map<FirstLetter, String> firstLetterMap = new HashMap<FirstLetter, String>();
@ElementCollection
Map<LastNumber, String> lastNumberMap = new HashMap<LastNumber, String>();
@MapKeyEnumerated(EnumType.STRING)
@ElementCollection
Map<LastNumber, String> explicitOverridingImplicitMap = new HashMap<LastNumber, String>();
}

View File

@ -0,0 +1,317 @@
/*
* 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.annotations.enumerated.custom_mapkey;
import java.io.Serializable;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.type.EnumType;
import org.hibernate.type.Type;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.test.annotations.enumerated.custom_types.FirstLetterType;
import org.hibernate.test.annotations.enumerated.custom_types.LastNumberType;
import org.hibernate.test.annotations.enumerated.enums.Common;
import org.hibernate.test.annotations.enumerated.enums.FirstLetter;
import org.hibernate.test.annotations.enumerated.enums.LastNumber;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
/**
* Test as in EnumeratedTypeTest but with MapKey
*
* @author Janario Oliveira
*/
public class MapKeyCustomEnumTypeTest extends BaseNonConfigCoreFunctionalTestCase {
private Type getMapKeyType(Property prop) {
Value value = prop.getValue();
assertEquals( Map.class, value.getClass() );
Map map = (Map) value;
return map.getIndex().getType();
}
private void assetTypeDefinition(
Property property,
Class expectedReturnedClass, Class expectedType) {
Type type = getMapKeyType( property );
assertEquals( expectedReturnedClass, type.getReturnedClass() );
assertEquals( expectedType.getName(), type.getName() );
}
@Test
public void testTypeDefinition() {
PersistentClass pc = metadata().getEntityBinding( EntityMapEnum.class.getName() );
// ordinal default of EnumType
assetTypeDefinition( pc.getProperty( "ordinalMap" ), Common.class, EnumType.class );
// string defined by Enumerated(STRING)
assetTypeDefinition( pc.getProperty( "stringMap" ), Common.class, EnumType.class );
// explicit defined by @Type
assetTypeDefinition( pc.getProperty( "firstLetterMap" ), FirstLetter.class, FirstLetterType.class );
// implicit defined by @TypeDef in somewhere
assetTypeDefinition( pc.getProperty( "lastNumberMap" ), LastNumber.class, LastNumberType.class );
// implicit defined by @TypeDef in anywhere, but overrided by Enumerated(STRING)
assetTypeDefinition( pc.getProperty( "explicitOverridingImplicitMap" ), LastNumber.class, EnumType.class );
}
private void assetEntityMapEnumEquals(EntityMapEnum expected, EntityMapEnum found) {
assertEquals( expected.id, found.id );
assertEquals( expected.ordinalMap, found.ordinalMap );
assertEquals( expected.stringMap, found.stringMap );
assertEquals( expected.firstLetterMap, found.firstLetterMap );
assertEquals( expected.lastNumberMap, found.lastNumberMap );
assertEquals( expected.explicitOverridingImplicitMap, found.explicitOverridingImplicitMap );
}
private EntityMapEnum assertFindEntityMapEnum(
EntityMapEnum expected,
String query,
String queryWithParam, Object param,
String nativeQueryCheck) {
assertNotEquals( 0, expected.id );
assertNotNull( param );
Session session = openNewSession();
EntityMapEnum found = (EntityMapEnum) session.createQuery( query ).uniqueResult();
//find
assetEntityMapEnumEquals( expected, found );
// find with parameter
found = (EntityMapEnum) session.createQuery( queryWithParam )
.setParameter( "param", param ).uniqueResult();
assetEntityMapEnumEquals( expected, found );
//native query check
SQLQuery sqlQuery = session.createSQLQuery( nativeQueryCheck );
sqlQuery.setParameter( "idEntityMapEnum", expected.id );
Object o = sqlQuery.uniqueResult();
assertNotNull( o );
return found;
}
@Test
public void testQuery() {
// ordinal
EntityMapEnum entityMapEnum = new EntityMapEnum();
entityMapEnum.ordinalMap.put( Common.A2, "Common.A2" );
Serializable id = save( entityMapEnum );
EntityMapEnum found = assertFindEntityMapEnum(
entityMapEnum, "from EntityMapEnum ee where key(ee.ordinalMap)=1",
"from EntityMapEnum ee where key(ee.ordinalMap)=:param", Common.A2,
"select 1 from EntityMapEnum_ordinalMap where EntityMapEnum_id=:idEntityMapEnum and ordinalMap_KEY=1"
);
assertFalse( found.ordinalMap.isEmpty() );
delete( id );
// **************
// string
entityMapEnum = new EntityMapEnum();
entityMapEnum.stringMap.put( Common.B1, "Common.B2" );
id = save( entityMapEnum );
found = assertFindEntityMapEnum(
entityMapEnum,
"from EntityMapEnum ee where key(ee.stringMap)='B1'",
"from EntityMapEnum ee where key(ee.stringMap)=:param",
Common.B1,
"select 1 from EntityMapEnum_stringMap where EntityMapEnum_id=:idEntityMapEnum and stringMap_KEY='B1'"
);
assertFalse( found.stringMap.isEmpty() );
delete( id );
// **************
// custom local type (FirstLetterType)
entityMapEnum = new EntityMapEnum();
entityMapEnum.firstLetterMap.put( FirstLetter.C_LETTER, "FirstLetter.C_LETTER" );
id = save( entityMapEnum );
found = assertFindEntityMapEnum(
entityMapEnum,
"from EntityMapEnum ee where key(ee.firstLetterMap)='C'",
"from EntityMapEnum ee where key(ee.firstLetterMap)=:param",
FirstLetter.C_LETTER,
"select 1 from EntityMapEnum_firstLetterMap where EntityMapEnum_id=:idEntityMapEnum and firstLetterMap_KEY='C'"
);
assertFalse( found.firstLetterMap.isEmpty() );
delete( id );
// **************
// custom global type(LastNumberType)
entityMapEnum = new EntityMapEnum();
entityMapEnum.lastNumberMap.put( LastNumber.NUMBER_1, "LastNumber.NUMBER_1 " );
id = save( entityMapEnum );
found = assertFindEntityMapEnum(
entityMapEnum,
"from EntityMapEnum ee where key(ee.lastNumberMap)='1'",
"from EntityMapEnum ee where key(ee.lastNumberMap)=:param",
LastNumber.NUMBER_1,
"select 1 from EntityMapEnum_lastNumberMap where EntityMapEnum_id=:idEntityMapEnum and lastNumberMap_KEY='1'"
);
assertFalse( found.lastNumberMap.isEmpty() );
delete( id );
// **************
// override global type
entityMapEnum = new EntityMapEnum();
entityMapEnum.explicitOverridingImplicitMap.put(
LastNumber.NUMBER_2, "LastNumber.NUMBER_2 "
);
id = save( entityMapEnum );
found = assertFindEntityMapEnum(
entityMapEnum,
"from EntityMapEnum ee where key(ee.explicitOverridingImplicitMap)='NUMBER_2'",
"from EntityMapEnum ee where key(ee.explicitOverridingImplicitMap)=:param",
LastNumber.NUMBER_2,
"select 1 from EntityMapEnum_explicitOverridingImplicitMap where EntityMapEnum_id=:idEntityMapEnum and explicitOverridingImplicitMap_KEY='NUMBER_2'"
);
assertFalse( found.explicitOverridingImplicitMap.isEmpty() );
delete( id );
}
private EntityMapEnum assertFindCriteria(
EntityMapEnum expected,
String mapPath, Object param) {
assertNotEquals( 0, expected.id );
Session session = openNewSession();
session.beginTransaction();
EntityMapEnum found = (EntityMapEnum) session.createCriteria( EntityMapEnum.class )
.createCriteria( mapPath, "m" )
.add( Restrictions.eq( "indices", param ) )
.uniqueResult();
//find
assetEntityMapEnumEquals( expected, found );
session.getTransaction().commit();
session.close();
return found;
}
@Test
public void testCriteria() {
// ordinal
EntityMapEnum entityMapEnum = new EntityMapEnum();
entityMapEnum.ordinalMap.put( Common.A1, "Common.A1" );
Serializable id = save( entityMapEnum );
EntityMapEnum found = assertFindCriteria(
entityMapEnum,
"ordinalMap", Common.A1
);
assertFalse( found.ordinalMap.isEmpty() );
delete( id );
// **************
// string
entityMapEnum = new EntityMapEnum();
entityMapEnum.stringMap.put( Common.B2, "Common.B2" );
id = save( entityMapEnum );
found = assertFindCriteria(
entityMapEnum,
"stringMap", Common.B2
);
assertFalse( found.stringMap.isEmpty() );
delete( id );
// **************
// custom local type (FirstLetterType)
entityMapEnum = new EntityMapEnum();
entityMapEnum.firstLetterMap.put( FirstLetter.A_LETTER, "FirstLetter.A_LETTER" );
id = save( entityMapEnum );
found = assertFindCriteria(
entityMapEnum,
"firstLetterMap", FirstLetter.A_LETTER
);
assertFalse( found.firstLetterMap.isEmpty() );
delete( id );
// **************
// custom global type(LastNumberType)
entityMapEnum = new EntityMapEnum();
entityMapEnum.lastNumberMap.put( LastNumber.NUMBER_3, "LastNumber.NUMBER_3" );
id = save( entityMapEnum );
found = assertFindCriteria(
entityMapEnum,
"lastNumberMap", LastNumber.NUMBER_3
);
assertFalse( found.lastNumberMap.isEmpty() );
delete( id );
// **************
// override global type
entityMapEnum = new EntityMapEnum();
entityMapEnum.explicitOverridingImplicitMap.put(
LastNumber.NUMBER_2, "LastNumber.NUMBER_2 "
);
id = save( entityMapEnum );
found = assertFindCriteria(
entityMapEnum,
"explicitOverridingImplicitMap", LastNumber.NUMBER_2
);
assertFalse( found.explicitOverridingImplicitMap.isEmpty() );
delete( id );
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {EntityMapEnum.class};
}
private Session openNewSession() {
if ( getSession() != null && getSession().isOpen() ) {
getSession().close();
}
return openSession();
}
private Serializable save(Object o) {
Session session = openNewSession();
session.getTransaction().begin();
Serializable id = session.save( o );
session.getTransaction().commit();
session.close();
return id;
}
private void delete(Serializable id) {
Session session = openNewSession();
session.getTransaction().begin();
Object o = session.get( EntityMapEnum.class, id );
session.delete( o );
session.getTransaction().commit();
session.close();
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.annotations.enumerated; package org.hibernate.test.annotations.enumerated.custom_types;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.test.annotations.enumerated; package org.hibernate.test.annotations.enumerated.custom_types;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;

View File

@ -0,0 +1,15 @@
/*
* 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.annotations.enumerated.enums;
/**
* @author Janario Oliveira
* @author Brett Meyer
*/
public enum Common {
A1, A2, B1, B2
}

View File

@ -0,0 +1,15 @@
/*
* 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.annotations.enumerated.enums;
/**
* @author Janario Oliveira
* @author Brett Meyer
*/
public enum FirstLetter {
A_LETTER, B_LETTER, C_LETTER
}

View File

@ -0,0 +1,15 @@
/*
* 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.annotations.enumerated.enums;
/**
* @author Janario Oliveira
* @author Brett Meyer
*/
public enum LastNumber {
NUMBER_1, NUMBER_2, NUMBER_3
}

View File

@ -0,0 +1,15 @@
/*
* 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.annotations.enumerated.enums;
/**
* @author Janario Oliveira
* @author Brett Meyer
*/
public enum Trimmed {
A, B, C
}