简体   繁体   English

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

[英]Why does Hibernate throw a ClassCastException when calling session.save(object)?

When I do the following Hibernate call I get a ClassCastException (see Stacktrace) but I have problems to understand why.当我执行以下 Hibernate 调用时,我得到一个 ClassCastException(请参阅 Stacktrace),但我无法理解原因。 What's Hibernate trying to do here? Hibernate 想在这里做什么? Is it trying to cast one of my objects to a different class type?它是否试图将我的一个对象转换为不同的 class 类型? If yes, why and to what class?如果是,为什么和什么 class?

session.save(fooAccount);

Stacktrace:堆栈跟踪:

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)

Edit: Here are the mappings as well as the code that stores the object:编辑:这是映射以及存储 object 的代码:

Mapping file (shortened version. Irrelevant stuff omitted):映射文件(缩短版。省略无关内容):

<?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 (shortened) 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 (shortened)密码.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 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;
        }
    }
}

Note: If I remove these 3 lines from my mapping file everything runs smoothly:注意:如果我从我的映射文件中删除这 3 行,一切都会顺利进行:

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

So, for me there seems to be a problem with getting the password's hash value via passwordInstance.getHash() ?所以,对我来说,通过passwordInstance.getHash()获取密码的 hash 值似乎有问题? Not sure though.虽然不确定。

What is the idea behind using a subclass of a subclass of Password in the mapping?在映射中使用密码子类的子类背后的想法是什么?

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

What happens here is that you require a specific implementation to be serialised on the persist.这里发生的是你需要一个特定的实现在持久化上被序列化。 In you case your object implements the parent class (SecurePassword) so it can not be cast to MediumSecurePassword.在您的情况下,您的 object 实现了父级 class (SecurePassword),因此它无法转换为 MediumSecurePassword。

I will advise to use the parent class in the mapping, this way you will be able to use both SecurePassword and MediumSecurePassword in your implementation:我会建议在映射中使用父 class,这样您就可以在实现中同时使用 SecurePassword 和 MediumSecurePassword:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM