HHH-7300 - TypeDefs won't be found depending of files read-order

This commit is contained in:
Strong Liu 2012-09-05 17:06:40 +08:00
parent 9ffc45dc34
commit a608bc3b87
7 changed files with 262 additions and 0 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
package org.hibernate.test.mapping.usertypes;
public enum TestEnum {
FOO,
BAR;
}

View File

@ -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>

View File

@ -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);
}
}