Hibernate: "InstantiationException: Cannot instantiate abstract class or interface"
I have the following class hierachy:
public class MailAccount{
IncomingMailServer incomingServer;
OutgoingMailServer outgoingServer;
}
public class MailServer{
HostAddress hostAddress;
Port port;
}
public class IncomingMailServer extends MailServer{
// ...
}
public class OutgoingMailServer extends MailServer{
// ...
}
public class ImapServer extends IncomingMailServer{
// ...
}
public class Pop3Server extends IncomingMailServer{
// ...
}
public class SmtpServer extends OutgoingMailServer{
// ...
}
My (simplified) mapping file looks like this:
<?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">
<hibernate-mapping package="com.mail.account">
<class name="MailAccount" table="MAILACCOUNTS" dynamic-update="true">
<id name="id" column="MAIL_ACCOUNT_ID">
<generator class="native" />
</id>
<component name="incomingServer">
<component name="hostAddress">
<property name="address" column="IS_HOST_ADDRESS"></property>
</component>
<component name="port">
<property name="portNumber" column="IS_PORT_NUMBER"></property>
</component>
</component>
<component开发者_开发百科 name="outgoingServer">
<component name="hostAddress">
<property name="address" column="OS_HOST_ADDRESS"></property>
</component>
<component name="port">
<property name="portNumber" column="OS_PORT_NUMBER"></property>
</component>
</component>
</class>
</hibernate-mapping>
The problem: Hibernate throws this exception when I call session.save(mailAccountInstance);
:
org.hibernate.InstantiationException: Cannot instantiate abstract class or interface: IncomingMailServer
So, I added the following lines to the incomingServer component:
<discriminator column="SERVER_TYPE" type="string"/>
<subclass name="ImapServer" extends="IncomingMailServer" discriminator-value="IMAP_SERVER" />
<subclass name="Pop3Server" extends="IncomingMailServer" discriminator-value="POP3_SERVER" />
And to the outgoing server:
<discriminator column="SERVER_TYPE" type="string"/>
<subclass name="SmtpServer" extends="OutgoingMailServer" discriminator-value="SMTP_SERVER" />
But now, Hibernate gives me this error message:
org.xml.sax.SAXParseException: The content of element type "component" must match "(meta*,tuplizer*,parent?,(property|many-to-one|one-to-one|component|dynamic-component|any|map|set|list|bag|array|primitive-array)*)".
Obviously, Hibernate does not like these tags in components.
How could I work around this?
Ps: I already tried moving IncomingServer and OutgoingServer each to their own tables and map them via a one-to-one. That works but leads to inconsistencies in the database because I noticed that MailAccount and IncomingServer/OutgoingServer must always have the same primary key id. If not, everything gets out of sync and the autoincrement value for the primary keys don't match any more (between Mailaccount and Servers).
This mapping worked for me:
Hibernate Mapping
<class name="MailServer" abstract="true" table="SERVER">
<id name="id" column="id">
<generator class="native" />
</id>
<discriminator column="SERVER_TYPE" type="string"/>
<subclass name="IncomingMailServer" abstract="true">
<property name="address" column="IS_HOST_ADDRESS"></property>
<property name="portNumber" column="IS_PORT_NUMBER"></property>
</subclass>
<subclass name="OutgoingMailServer" abstract="true">
<property name="address" column="OS_HOST_ADDRESS"></property>
<property name="portNumber" column="OS_PORT_NUMBER"></property>
</subclass>
</class>
<subclass name="IMAPServer" extends="com.mail.account.IncomingMailServer"
discriminator-value="IMAP_SERVER"/>
<subclass name="POP3Server" extends="com.mail.account.IncomingMailServer"
discriminator-value="POP3_SERVER"/>
<subclass name="SMTPServer" extends="com.mail.account.OutgoingMailServer"
discriminator-value="SMTP_SERVER" />
Class Hierarchy
Abstract classes:
MailServer
is an abstract classIncomingServer
is an abstract class : this helped NOT specifingDISCRIMINATOR VALUE
OutgoingServer
is an abstract class : this helped NOT specifingDISCRIMINATOR VALUE
Concrete classes:
IMAPServer
extends IncomingServer withDISCRIMINATOR VALUE : IMAP
POP3Server
extends IncomingServer withDISCRIMINATOR VALUE : POP3
SMTPServer
extends IncomingServer withDISCRIMINATOR VALUE : SMTP
All the concrete servers are mapped to the same table "SERVER" however if need be they can be mapped to individual tables using join table.
Code Snippet
I was able to save the instances using the following simple code snippet:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction txn =session.beginTransaction();
IMAPServer imap=new IMAPServer();
imap.setAddress("SMTP_01");
imap.setPortNumber("SMTP_PORT_0001");
session.save(imap);
SMTPServer smtp=new SMTPServer();
smtp.setAddress("STMP_01");
smtp.setPortNumber("STMP_PORT_0001");
session.save(smtp);
MailAccount account=new MailAccount();
account.setIncomingServer(imap);
account.setOutgoingServer(smtp);
session.save(account);
txn.commit();
Let me know if this solves the problem. :)
EDIT: added the mapping from MailAccount to MailServer.
<class name="MailAccount" table="MAILACCOUNT" dynamic-update="true">
<id name="id" column="id">
<generator class="native" />
</id>
<many-to-one name="incomingServer" column="incoming" unique="true"/>
<many-to-one name="outgoingServer" column="outgoing" unique="true"/>
</class>
My approach would be this:
- Create a Entity (with it's own mapping) MailServer with discriminators and all. Mark it abstract just to be sure.
- Create the hierarchy of MailServer (one hbm for each) with it's own discriminator.
- Map MailAccount with 2 one-to-one relationships to MailServer. One incoming, one outgoing.
And, please, show us more code. It's hard to help without know :)
精彩评论