java.lang.verifyError 在休眠特定用户类型上

Posted

技术标签:

【中文标题】java.lang.verifyError 在休眠特定用户类型上【英文标题】:java.lang.verifyError on hibernate specific usertype 【发布时间】:2011-01-20 06:39:06 【问题描述】:

我们一直在使用 GenericEnumUserType 进行可扩展枚举,但我们的类无法在 Hibernate 3.6+ 容器上的 JBoss 6 中加载。

抛出以下错误

#abc state=Create: java.lang.NoSuchMethodError: org.hibernate.type.Type
Factory.basic(Ljava/lang/String;)Lorg/hibernate/type/Type;

关于以下代码

type = (NullableType)TypeFactory.basic(identifierType.getName());

【问题讨论】:

【参考方案1】:

不幸的是,如果您需要基于 Enum 的序数或名称以外的其他内容进行序列化,则 @​​Enumerated 不起作用。我设法找到了一个解决方案(从here 稍作修改)。

import java.io.Serializable;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.TypeResolver;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

public class GenericEnumUserType implements UserType, ParameterizedType 

    private Class<? extends Enum> enumClass;
    private Class<?> identifierType;
    private Method identifierMethod;
    private Method valueOfMethod;
    private static final String defaultIdentifierMethodName = "name";
    private static final String defaultValueOfMethodName = "valueOf";
    private AbstractSingleColumnStandardBasicType type;
    private int[] sqlTypes;

    @Override
    public void setParameterValues( Properties parameters )
    
        String enumClassName = parameters.getProperty("enumClass");
        try
        
            enumClass = Class.forName( enumClassName ).asSubclass( Enum.class );
        
        catch (ClassNotFoundException exception) 
            throw new HibernateException("Enum class not found", exception);
        

        String identifierMethodName = parameters.getProperty( "identifierMethod", defaultIdentifierMethodName );

        try
        
            identifierMethod = enumClass.getMethod( identifierMethodName,
                                                    new Class[0]);
            identifierType = identifierMethod.getReturnType();
        
        catch (Exception exception)
        
            throw new HibernateException("Failed to optain identifier method",
                    exception);
        

        TypeResolver tr = new TypeResolver();
        type = (AbstractSingleColumnStandardBasicType)tr.basic( identifierType.getName() );
        if (type == null)
        
            throw new HibernateException( "Unsupported identifier type " + identifierType.getName() );
        
        sqlTypes = new int[]  type.sqlType() ;

        String valueOfMethodName = parameters.getProperty( "valueOfMethod",
                                                           defaultValueOfMethodName);

        try
        
            valueOfMethod = enumClass.getMethod( valueOfMethodName,
                                                 new Class[]  identifierType  );
        
        catch ( Exception exception )
        
            throw new HibernateException( "Failed to optain valueOf method",
                                          exception);
        
    

    @Override
    public Class returnedClass()
    
        return enumClass;
    

    @Override
    public Object nullSafeGet( ResultSet rs, String[] names, Object owner )
        throws HibernateException, SQLException
    
        Object identifier = type.get( rs, names[0] );
        try
        
            return valueOfMethod.invoke( enumClass, new Object[]  identifier  );
        
        catch ( Exception exception )
        
            throw new HibernateException( "Exception while invoking valueOfMethod of enumeration class: ",
                                          exception);
        
    

    public void nullSafeSet( PreparedStatement st, Object value, int index )
        throws HibernateException, SQLException
    
        try
        
            Object identifier = value != null ? identifierMethod.invoke( value,
                                                                         new Object[0] ) : null;
            st.setObject( index, identifier );
        
        catch ( Exception exception )
        
            throw new HibernateException( "Exception while invoking identifierMethod of enumeration class: ",
                                          exception );

        
    

    @Override
    public int[] sqlTypes()
    
        return sqlTypes;
    

    @Override
    public Object assemble( Serializable cached, Object owner )
        throws HibernateException
    
        return cached;
    

    @Override
    public Object deepCopy( Object value )
        throws HibernateException
    
        return value;
    

    @Override
    public Serializable disassemble( Object value )
        throws HibernateException
    
        return (Serializable) value;
    

    @Override
    public boolean equals( Object x, Object y )
        throws HibernateException
    
        return x == y;
    

    @Override
    public int hashCode( Object x )
        throws HibernateException
    
        return x.hashCode();
    

    public boolean isMutable()
    
        return false;
    

    public Object replace( Object original, Object target, Object owner )
        throws HibernateException
    
        return original;
    

【讨论】:

这一直有效,直到我迁移到最新版本的休眠。 new TypeResolver() 自 5.3 起已弃用,我还没有找到任何替代方案。【参考方案2】:

Hibernate 3.6 中不再有 TypeFactory.basic(String) 了。比较 javadocs:

http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/type/TypeFactory.html http://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/type/TypeFactory.html

我认为现在是时候从自定义 UserType 转移到标准 @Enumerated 了 :-)

【讨论】:

不幸的是,如果您基于 Enum 序号或名称进行序列化,则只能使用 @Enumerated。在某些情况下这是不合适的。 我可以确认 mtpettyp 在说什么。一个常见的情况是当您用枚举替换“幻数”时,您仍然必须保留原始数据。【参考方案3】:

我查看了TypeResolver 的代码并注意到basic 方法的内部实现,该方法使用尚未弃用的类。所以,我换了

TypeResolver tr = new TypeResolver();
type = (AbstractSingleColumnStandardBasicType)tr.basic( identifierType.getName() );

type = (AbstractSingleColumnStandardBasicType<? extends Object>) new TypeConfiguration()
       .getBasicTypeRegistry().getRegisteredType(identifierType.getName());

而且...到目前为止,一切都很好。

【讨论】:

以上是关于java.lang.verifyError 在休眠特定用户类型上的主要内容,如果未能解决你的问题,请参考以下文章

API < 21 的 java.lang.VerifyError

java.lang.VerifyError:在分支目标 73 处期望堆栈图帧

java.lang.VerifyError:在分支目标 73 处期望堆栈图帧

java.lang.VerifyError:期望堆栈图帧

Proguard - 找不到常见的超类/java.lang.VerifyError

Evernote Android SDK 库在 R8 下抛出 java.lang.VerifyError