为什么Hibernate在调用session.save(object)时会抛出ClassCastException?

时间:2012-04-06 12:55:14

标签: java hibernate classcastexception

当我执行以下Hibernate调用时,我得到一个ClassCastException(请参阅Stacktrace)但我有问题要理解为什么。什么是Hibernate试图在这做?它是否试图将我的一个对象转换为不同的类类型?如果是,为什么和哪个班级?

session.save(fooAccount);

堆栈跟踪:

com.foo.web.model.exception.FailedDatabaseOperationException: java.lang.ClassCastException: com.foo.web.model.authentication.SecurePassword
    at com.foo.web.controller.db.HibernateController.savefooAccount(HibernateController.java:883)
    at com.foo.web.model.account.fooAccount.save(fooAccount.java:459)
    at com.foo.web.controller.AccountController.createfooAccount(AccountController.java:258)
    at com.foo.web.view.start.RegisterController.register(RegisterController.java:233)
    at com.foo.web.view.start.RegisterController.onClick$btn_register(RegisterController.java:196)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.zkoss.zk.ui.event.GenericEventListener.onEvent(GenericEventListener.java:81)
    at org.zkoss.zk.ui.impl.EventProcessor.process0(EventProcessor.java:192)
    at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138)
    at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.process0(EventProcessingThreadImpl.java:517)
    at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.sendEvent(EventProcessingThreadImpl.java:121)
    at org.zkoss.zk.ui.event.Events.sendEvent(Events.java:319)
    at org.zkoss.zk.ui.event.Events.sendEvent(Events.java:329)
    at org.zkoss.zk.ui.AbstractComponent$ForwardListener.onEvent(AbstractComponent.java:3034)
    at org.zkoss.zk.ui.impl.EventProcessor.process0(EventProcessor.java:192)
    at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138)
    at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.process0(EventProcessingThreadImpl.java:517)
    at org.zkoss.zk.ui.impl.EventProcessingThreadImpl.run(EventProcessingThreadImpl.java:444)
Caused by: java.lang.ClassCastException: com.foo.web.model.authentication.SecurePassword
    at org.hibernate.type.ComponentType.toLoggableString(ComponentType.java:410)
    at org.hibernate.type.ComponentType.toLoggableString(ComponentType.java:414)
    at org.hibernate.pretty.Printer.toString(Printer.java:76)
    at org.hibernate.pretty.Printer.toString(Printer.java:113)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:120)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    at com.foo.web.controller.db.HibernateController.savefooAccount(HibernateController.java:879)

编辑:以下是映射以及存储对象的代码:

映射文件(缩短版本。省略了不相关的内容):

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated 26.04.2011 14:49:15 by Hibernate Tools 3.3.0.GA -->
<hibernate-mapping>
    <class name="com.foo.web.model.account.fooAccount" table="fooACCOUNT">

        <id name="id" type="long" access="field">
            <column name="foo_ACCOUNT_ID" />
            <generator class="native" />
        </id>

        <many-to-one name="defaultMailAccount" lazy="false" column="DEFAULT_MAIL_ACCOUNT_ID" />

        <bag name="mailAccounts" table="MAILACCOUNTS" lazy="false" inverse="true">
            <key column="foo_ACCOUNT_ID"></key>
            <one-to-many class="com.foo.web.model.mail.account.MailAccount" />
        </bag>

        <component name="user" class="com.foo.web.model.account.fooUser">
            <component name="activationCode">
                <property name="activationCode" column="ACTIVATION_CODE"></property>
            </component>

            <component name="password" class="com.foo.web.model.authentication.MediumSecurePassword">
                <property name="hash" column="PASSWORD" />
            </component>

            <component name="resetCode">
                <property name="resetCode" column="RESET_CODE" />
            </component>
            <one-to-one name="fooAccount" class="com.foo.web.model.account.fooAccount"></one-to-one>
            <component name="username">
                <property name="username" column="USERNAME" unique="true" />
            </component>

            <property name="userStatus">
                <column name="USERSTATUS" />
                <type name="org.hibernate.type.EnumType">
                    <param name="type">12</param>
                    <param name="enumClass">com.foo.web.model.account.UserStatus</param>
                </type>
            </property>
            <property name="userType">
                <column name="USERTYPE" />
                <type name="org.hibernate.type.EnumType">
                    <param name="type">12</param>
                    <param name="enumClass">com.foo.web.model.account.UserType</param>
                </type>
            </property>
        </component>

    </class>
</hibernate-mapping>

SecurePassword.java (缩写)

/**
 * Represents a password.
 */
public class SecurePassword extends Password {

    /**
     * Default constructor
     */
    public SecurePassword() {
        super();
    }

    /**
     * Constructor method. Will throw IllegalPasswordException if password is
     * not safe.
     */
    public SecurePassword(String password) throws IllegalPasswordException {
        setPassword(password);
    }

    /**
     * Checks if the given character is a number
     * 
     * @param c
     *            The character to check
     * @return Returns true if the given character is a number
     */
    public boolean isNumber(char c) {
        // ...
    }

    /**
     * Checks is a given password is valid.
     */
    public boolean passwordValid(Password password)
        throws IllegalPasswordException {
        return passwordValid(password.toString());
    }

    /**
     * Checks is a given password is valid.
     */
    public boolean passwordValid(String password)
        throws IllegalPasswordException {

        // ...
    }

    /**
     * Sets a new password.
     */
    @Override
    public void setPassword(String password) throws IllegalPasswordException {

        if (passwordValid(password)) {
            this.password = password;
        }
    }

}

Password.java (缩写)

/**
 * Represents a simple password without much restriction
 */
public class Password {
    Crypter crypter     = new Crypter();
    String  hash        = null;
    String  password    = null;

    public Password() {
        super();
    }

    public Password(String password) throws IllegalPasswordException {
        setPassword(password);
    }

    @Override
    public boolean equals(Object password) {

        if (this == password) {
            return true;
        }

        if (password instanceof Password) {
            Password p = (Password) password;
            return getHash().equals(p.getHash());
        }

        if (password instanceof String) {
            String password_str = getPassword();
            if (password_str != null) {
                return password_str.equals(password);
            }
        }

        return false;
    }

    /**
     * Returns the hashed version of this password
     * 
     * @return The hashed version of this password
     */
    public String getHash() {
        if (hash == null) {
            try {
                hash = crypter.hash(getPassword());
            } catch (FailedCryptOperationException e) {
                handleException(e, false, null, null);
            }
        }
        return hash;
    }

    public String getPassword() {
        return password;
    }

    /*
     * Handles the given exception
     */
    private void handleException(Exception e, boolean notifyUser,
        String customTitle, String customErrorMessage) {

        SystemController.handleException(e, notifyUser, customTitle,
            customErrorMessage);
    }

    public void setHash(String hash) {
        this.hash = hash;
    }

    @SuppressWarnings("unused")
    public void setPassword(String password) throws IllegalPasswordException {
        hash = null;
        this.password = password;
    }

    @Override
    public String toString() {

        return getPassword();
    }
}

MediumSecurePassword.java

public class MediumSecurePassword extends SecurePassword {

    public final int    MAX_LENGTH              = 64;
    public final int    MIN_LENGTH              = 6;
    StringUtil          stringUtil              = new StringUtil();


    public MediumSecurePassword() {
        super();
    }

    /**
     * Constructor method. Will throw IllegalPasswordException if password is
     * not safe. Medium Safe passwords are at least 6 characters long.
     */
    public MediumSecurePassword(String password) throws IllegalPasswordException {
        setPassword(password);
    }

    /**
     * Checks if the given character is a number
     */
    public boolean isNumber(char c) {
        // ...
    }

    /**
     * Checks is a given password is valid. Valid means that it's secure (at
     * least 8 characters, at least 1 number, at least 1 special character).
     */
    public boolean passwordValid(Password password)
        throws IllegalPasswordException {
        return passwordValid(password.toString());
    }

    /**
     * Checks is a given password is valid. Valid means that it's "medium secure" (min.
     * length of 6).
     */
    public boolean passwordValid(String password)
        throws IllegalPasswordException {

        // ...
    }

    /**
     * Sets a new password. Will throw IllegalPasswordException if password is
     * not safe. Safe passwords are at least 8 characters long, consist of
     * numbers, letters and special characters.
     */
    @Override
    public void setPassword(String password) throws IllegalPasswordException {

        if (passwordValid(password)) {
            this.password = password;
        }
    }
}

注意:如果我从映射文件中删除这3行,一切都顺利进行:

<component name="password" class="com.foo.web.model.authentication.MediumSecurePassword">
 <property name="hash" column="PASSWORD" />
</component>

那么,对我来说,通过passwordInstance.getHash()获取密码的哈希值似乎有问题?但不确定。

1 个答案:

答案 0 :(得分:3)

在映射中使用Password子类的子类背后的想法是什么?

        <component name="password" class="com.foo.web.model.authentication.MediumSecurePassword">
            <property name="hash" column="PASSWORD" />
        </component>

这里发生的是您需要在persist上序列化特定的实现。在您的情况下,您的对象实现父类(SecurePassword),因此无法将其强制转换为MediumSecurePassword。

我建议在映射中使用父类,这样您就可以在实现中同时使用SecurePassword和MediumSecurePassword:

        <component name="password" class="com.foo.web.model.authentication.Password">
            <property name="hash" column="PASSWORD" />
        </component>