Hibernate:由于一对一关系,主键生成不一致

4
我这里有两个一对一的关系,分别是类“MailAccount”和类“IncomingServer”以及类“OutgoingServer”之间的关系。
这是一个在Tomcat和Ubuntu服务器版上运行的Java应用程序。
映射如下所示:
MailAccount.hbm.xml
<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>

        <one-to-one name="incomingServer" cascade="all-delete-orphan">
        </one-to-one>
        <one-to-one name="outgoingServer" cascade="all-delete-orphan">
        </one-to-one>

    </class>
</hibernate-mapping>

IncomingMailServer.hbm.xml

<hibernate-mapping>
    <class name="com.IncomingMailServer" table="MAILSERVER_INCOMING" abstract="true">

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

        <discriminator column="SERVER_TYPE" type="string"/>

        <many-to-one name="mailAccount" column="MAIL_ACCOUNT_ID" not-null="true" unique="true" />

        <subclass name="com.ImapServer" extends="com.IncomingMailServer" discriminator-value="IMAP_SERVER" />           
        <subclass name="com.Pop3Server" extends="com.IncomingMailServer" discriminator-value="POP3_SERVER" />

    </class>
</hibernate-mapping>

OutgoingMailServer.hbm.xml

<hibernate-mapping>
    <class name="com.OutgoingMailServer" table="MAILSERVER_OUTGOING" abstract="true">

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

        <discriminator column="SERVER_TYPE" type="string"/>

        <many-to-one name="mailAccount" column="MAIL_ACCOUNT_ID" not-null="true" unique="true" />

        <subclass name="com.SmtpServer" extends="com.OutgoingMailServer" discriminator-value="SMTP_SERVER" />

    </class>
</hibernate-mapping>

类层次结构如下:

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{
 // ...
}

现在,这里出现了一个问题
虽然我的应用程序大部分时间运行良好,但似乎有一种情况可以导致电子邮件服务器被删除,但相应的账户没有被删除,而在这种情况下就会发生以下调用:
session.delete(mailAccountInstance);

在Hibernate中的一对一关系中,邮件账户和其服务器之间的主键必须相等,否则关系将完全失去同步:
例如:
想象一下,表中填充了以下数据:
表 "MailAccount" (当前自增值为2)
MAIL_ACCOUNT_ID NAME
0               Account1
1               Account2

表格"IncomingMailServer"(当前自增值为2)

MAIL_SERVER_ID  MAIL_ACCOUNT_ID
0               0
1               1

现在假设ID为1的账户被删除并添加了新的账户。接下来有时会发生以下情况:
表格"MailAccount"(当前自动增量值:3)
MAIL_ACCOUNT_ID NAME
0               Account1
1               Account2
2               Account3

“IncomingMailServer”表(当前自动增量值为2)

MAIL_SERVER_ID  MAIL_ACCOUNT_ID
0               0
1               2

这会完全破坏我的数据库的一致性。我该如何避免这种情况?

2
如果没有解决方案,你所说的一对一关系中主键必须相等的部分是错误的。在MAILACCOUNTS中,每个关系都必须有一个匹配的表或外键列。 - Nicktar
从我观察到的情况来看,Hibernate匹配了Incoming/Outgoing-Server和MailAccount的主键,如果这种匹配失去同步,所有后续的匹配都会出错!@JB Nizet - Timo Ernst
@valmar 必须有某种匹配,可以是匹配表或包含其他表主键的外键列。我从未听说过在 hibernate 中同步主键。要找到匹配项,您可以让 hibernate 记录其 SQL 语句(以及绑定变量)。 - Nicktar
你不需要关心这些自增的值是什么。唯一重要的是,如果邮件服务器属于某个账户,那么它的MAIL_ACCOUNT_ID列必须具有MailAccount表的MAIL_ACCOUNT_ID列的值。自增列永远不会减少,它只会增加,如果两个序列不同步也不是问题。 - JB Nizet
@JB Nizet 是的,我已经考虑用多对一替换一对一,并以此设置外键。 但是,根本原因(帐户存在,但服务器不存在)仍然存在,我希望解决这个问题或至少找到一种调试方法。 - Timo Ernst
显示剩余7条评论
1个回答

4
如果您想要一个共享的主键,只能使用本地id生成器一次。您首先创建邮件账户,该账户将生成自己的ID,但当您创建传入或传出邮件服务器时,这些服务器需要从mailAccount属性中取其ID。
因此,您需要使用“外部”生成器:
<class name="OutgoingMailServer">
    <id name="id" column="MAIL_SERVER_ID"> 
       <generator class="foreign"> 
           <param name="property">mailAccount</param> 
       </generator>
    </id>
    <one-to-one name="mailAccount" not-null="true" constrained="true"/>
<class>

您不需要一个MAIL_ACCOUNT_ID列,因为它始终与MAIL_SERVER_ID相同。

非常基础,请参考关于基于主键的双向一对一关联的参考资料。


很奇怪。当我这样做并调用session.save(mailAccountInstance)时,我在这里获得了一个NullPointerException:org.hibernate.tuple.entity.AbstractEntityTuplizer.getPropertyValue(AbstractEntityTuplizer.java:521)。我已经检查过被引用的邮件服务器或邮件帐户是否为空,但它们不是空的。以下是完整的跟踪记录:http://dl.dropbox.com/u/17844821/zeug/stacktrace.txt - Timo Ernst
@ greyfairer 差不多是对的,但 column="MAIL_SERVER_ID" 属性必须在 id 标签中,而不是在 one-to-one 标签中。 - Fortunato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接