当我执行以下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()
获取密码的哈希值似乎有问题?但不确定。
答案 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>