HHH-7300 - TypeDefs won't be found depending of files read-order
This commit is contained in:
parent
9ffc45dc34
commit
a608bc3b87
|
@ -95,6 +95,7 @@ import org.hibernate.mapping.TypeDef;
|
|||
import org.hibernate.mapping.UnionSubclass;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
import org.hibernate.mapping.Value;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.DiscriminatorType;
|
||||
import org.hibernate.type.ForeignKeyDirection;
|
||||
import org.hibernate.type.Type;
|
||||
|
@ -1212,6 +1213,11 @@ public final class HbmBinder {
|
|||
}
|
||||
}
|
||||
|
||||
resolveAndBindTypeDef(simpleValue, mappings, typeName, parameters);
|
||||
}
|
||||
|
||||
private static void resolveAndBindTypeDef(SimpleValue simpleValue,
|
||||
Mappings mappings, String typeName, Properties parameters) {
|
||||
TypeDef typeDef = mappings.getTypeDef( typeName );
|
||||
if ( typeDef != null ) {
|
||||
typeName = typeDef.getTypeClass();
|
||||
|
@ -1221,6 +1227,19 @@ public final class HbmBinder {
|
|||
allParameters.putAll( typeDef.getParameters() );
|
||||
allParameters.putAll( parameters );
|
||||
parameters = allParameters;
|
||||
}else if (typeName!=null && !mappings.isInSecondPass()){
|
||||
BasicType basicType=mappings.getTypeResolver().basic(typeName);
|
||||
if (basicType==null) {
|
||||
/*
|
||||
* If the referenced typeName isn't a basic-type, it's probably a typedef defined
|
||||
* in a mapping file not read yet.
|
||||
* It should be solved by deferring the resolution and binding of this type until
|
||||
* all mapping files are read - the second passes.
|
||||
* Fixes issue HHH-7300
|
||||
*/
|
||||
SecondPass resolveUserTypeMappingSecondPass=new ResolveUserTypeMappingSecondPass(simpleValue,typeName,mappings,parameters);
|
||||
mappings.addSecondPass(resolveUserTypeMappingSecondPass);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !parameters.isEmpty() ) simpleValue.setTypeParameters( parameters );
|
||||
|
@ -3183,4 +3202,27 @@ public final class HbmBinder {
|
|||
private static interface EntityElementHandler {
|
||||
public void handleEntity(String entityName, String className, Mappings mappings);
|
||||
}
|
||||
|
||||
private static class ResolveUserTypeMappingSecondPass implements SecondPass{
|
||||
|
||||
private SimpleValue simpleValue;
|
||||
private String typeName;
|
||||
private Mappings mappings;
|
||||
private Properties parameters;
|
||||
|
||||
public ResolveUserTypeMappingSecondPass(SimpleValue simpleValue,
|
||||
String typeName, Mappings mappings, Properties parameters) {
|
||||
this.simpleValue=simpleValue;
|
||||
this.typeName=typeName;
|
||||
this.parameters=parameters;
|
||||
this.mappings=mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSecondPass(java.util.Map persistentClasses)
|
||||
throws MappingException {
|
||||
resolveAndBindTypeDef(simpleValue, mappings, typeName, parameters);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
package org.hibernate.test.mapping.usertypes;
|
||||
import java.io.Serializable;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.usertype.ParameterizedType;
|
||||
import org.hibernate.usertype.UserType;
|
||||
|
||||
public class EnumUserType implements UserType, ParameterizedType {
|
||||
|
||||
private Class clazz = null;
|
||||
|
||||
public static EnumUserType createInstance(Class clazz){
|
||||
if (!clazz.isEnum())
|
||||
throw new IllegalArgumentException("Parameter has to be an enum-class");
|
||||
EnumUserType that=new EnumUserType();
|
||||
Properties p=new Properties();
|
||||
p.setProperty("enumClassName", clazz.getName());
|
||||
that.setParameterValues(p);
|
||||
return that;
|
||||
}
|
||||
|
||||
public void setParameterValues(Properties params) {
|
||||
String enumClassName = params.getProperty("enumClassName");
|
||||
if (enumClassName == null) {
|
||||
throw new MappingException("enumClassName parameter not specified");
|
||||
}
|
||||
|
||||
try {
|
||||
this.clazz = Class.forName(enumClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new MappingException("enumClass " + enumClassName + " not found", e);
|
||||
}
|
||||
if (!this.clazz.isEnum()){
|
||||
throw new MappingException("enumClass "+enumClassName+" doesn't refer to an Enum");
|
||||
}
|
||||
}
|
||||
|
||||
private static final int[] SQL_TYPES = {Types.CHAR};
|
||||
public int[] sqlTypes() {
|
||||
return SQL_TYPES;
|
||||
}
|
||||
|
||||
public Class returnedClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
|
||||
throws HibernateException, SQLException {
|
||||
String name = resultSet.getString(names[0]);
|
||||
Object result = null;
|
||||
if (!resultSet.wasNull()) {
|
||||
result = Enum.valueOf(clazz, name.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object nullSafeGet(ResultSet resultSet, String[] names,
|
||||
SessionImplementor session, Object owner) throws HibernateException,
|
||||
SQLException {
|
||||
return nullSafeGet(resultSet, names, owner);
|
||||
}
|
||||
|
||||
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index)
|
||||
throws HibernateException, SQLException {
|
||||
if (null == value) {
|
||||
preparedStatement.setNull(index, Types.VARCHAR);
|
||||
} else {
|
||||
preparedStatement.setString(index, ((Enum)value).name());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index,
|
||||
SessionImplementor session) throws HibernateException, SQLException {
|
||||
nullSafeSet(preparedStatement, value, index);
|
||||
}
|
||||
|
||||
public Object deepCopy(Object value) throws HibernateException{
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean isMutable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object assemble(Serializable cached, Object owner) throws HibernateException {
|
||||
return cached;
|
||||
}
|
||||
|
||||
public Serializable disassemble(Object value) throws HibernateException {
|
||||
return (Serializable)value;
|
||||
}
|
||||
|
||||
public Object replace(Object original, Object target, Object owner) throws HibernateException {
|
||||
return original;
|
||||
}
|
||||
public int hashCode(Object x) throws HibernateException {
|
||||
return x.hashCode();
|
||||
}
|
||||
public boolean equals(Object x, Object y) throws HibernateException {
|
||||
if (x == y)
|
||||
return true;
|
||||
if (null == x || null == y)
|
||||
return false;
|
||||
return x.equals(y);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping>
|
||||
<class name="org.hibernate.test.mapping.usertypes.TestEntity"
|
||||
table="TestTable">
|
||||
<id name="id" column="ID" type="integer">
|
||||
<generator class="uuid"/>
|
||||
</id>
|
||||
|
||||
<property name="testEnum" column="ENUM" type="testenumtype"/>
|
||||
</class>
|
||||
</hibernate-mapping>
|
|
@ -0,0 +1,19 @@
|
|||
package org.hibernate.test.mapping.usertypes;
|
||||
|
||||
public class TestEntity {
|
||||
private int id;
|
||||
private TestEnum testEnum;
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public void setTestEnum(TestEnum testEnum) {
|
||||
this.testEnum = testEnum;
|
||||
}
|
||||
public TestEnum getTestEnum() {
|
||||
return testEnum;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package org.hibernate.test.mapping.usertypes;
|
||||
|
||||
public enum TestEnum {
|
||||
FOO,
|
||||
BAR;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
|
||||
|
||||
<hibernate-mapping>
|
||||
<typedef name="testenumtype" class="org.hibernate.test.mapping.usertypes.EnumUserType">
|
||||
<param name="enumClassName">org.hibernate.test.mapping.usertypes.TestEnum</param>
|
||||
</typedef>
|
||||
</hibernate-mapping>
|
|
@ -0,0 +1,58 @@
|
|||
package org.hibernate.test.mapping.usertypes;
|
||||
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.testing.ServiceRegistryBuilder;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for read-order independent resolution of user-defined types
|
||||
* Testcase for issue HHH-7300
|
||||
* @author Stefan Schulze
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-7300")
|
||||
public class UserTypeMappingTest extends BaseUnitTestCase{
|
||||
|
||||
private Configuration cfg;
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
@Before
|
||||
public void setup(){
|
||||
cfg=new Configuration();
|
||||
Properties p = new Properties();
|
||||
p.put( Environment.DIALECT, "org.hibernate.dialect.HSQLDialect" );
|
||||
p.put( "hibernate.connection.driver_class", "org.h2.Driver" );
|
||||
p.put( "hibernate.connection.url", "jdbc:h2:mem:" );
|
||||
p.put( "hibernate.connection.username", "sa" );
|
||||
p.put( "hibernate.connection.password", "" );
|
||||
cfg.setProperties(p);
|
||||
serviceRegistry = ServiceRegistryBuilder.buildServiceRegistry( cfg.getProperties() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstTypeThenEntity(){
|
||||
cfg.addResource("org/hibernate/test/mapping/usertypes/TestEnumType.hbm.xml")
|
||||
.addResource("org/hibernate/test/mapping/usertypes/TestEntity.hbm.xml");
|
||||
SessionFactory sessions=cfg.buildSessionFactory(serviceRegistry);
|
||||
Assert.assertNotNull(sessions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstEntityThenType(){
|
||||
cfg.addResource("org/hibernate/test/mapping/usertypes/TestEntity.hbm.xml")
|
||||
.addResource("org/hibernate/test/mapping/usertypes/TestEnumType.hbm.xml");
|
||||
|
||||
SessionFactory sessions=cfg.buildSessionFactory(serviceRegistry);
|
||||
Assert.assertNotNull(sessions);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue