Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
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;
Expand Down Expand Up @@ -1211,6 +1212,11 @@ private static void bindSimpleValueType(Element node, SimpleValue simpleValue, M
}
}

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();
Expand All @@ -1220,6 +1226,19 @@ private static void bindSimpleValueType(Element node, SimpleValue simpleValue, M
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 );
Expand Down Expand Up @@ -3166,4 +3185,27 @@ private static void recognizeEntities(
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);
}

}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}

Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.hibernate.test.mapping.usertypes;

public enum TestEnum {
FOO,
BAR;
}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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);
}

}