最近需要增加業(yè)務(wù)扰付,需要增加一對(duì)一關(guān)聯(lián),由于系統(tǒng)采用hibernate3价脾,而且使用了xml配置方式,所以就舉例一下xml配置方式:
假設(shè)我們有兩個(gè)類笛匙,人和身份證侨把。我們要達(dá)到的效果就是查詢?nèi)说男畔r(shí)自動(dòng)讀取身份證。
類:
//人
public class Person(){
Long id;
IdCard idCard;
}
// 身份證
public class IdCard(){
Long id;
String cardNo;
}
接下來是xml的配置:
<!-- person.hbm.xml -->
<hibernate-mapping package="beans" auto-import="true">
<class name="Person" table="person">
<id name="id" column="id" type="java.lang.Long">
<!-- 手動(dòng)指定ID -->
<generator class="assigned" />
</id>
<one-to-one name="idCard" cascade="save-update" constrained="true" lazy="false"/>
</class>
</hibernate-mapping>
<!-- id_card.hbm.xml -->
<hibernate-mapping package="beans" auto-import="true">
<class name="IdCard" table="id_card">
<id name="id" column="id" type="java.lang.Long">
<!-- 手動(dòng)指定ID -->
<generator class="assigned" />
</id>
</class>
</hibernate-mapping>
這樣配置后妹孙,如果數(shù)據(jù)一致是沒有問題的秋柄,但是由于我們是后加業(yè)務(wù),所以肯定會(huì)造成數(shù)據(jù)不一致的情況蠢正,意思就是有人的信息骇笔,但是沒有身份證的信息。這樣一來嚣崭,hibernate會(huì)報(bào)錯(cuò):
No row with the given identifier exists
讓我們閱讀源碼笨触,了解出錯(cuò)原因(之前已通過查閱異常棧定位到出錯(cuò)位置):
// EntityType 有幾個(gè)子類,ManyToOneType,OneToOneType
public class EntityType{
protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
...
boolean isProxyUnwrapEnabled = unwrapProxy &&
session.getFactory()
.getEntityPersister( getAssociatedEntityName() )
.isInstrumented( session.getEntityMode() );
// 調(diào)用查詢雹舀,傳了傳了一個(gè)isNullable()用于處理為空的情況
// isNullable()是抽象的芦劣,又子類實(shí)現(xiàn)
Object proxyOrEntity = session.internalLoad(
getAssociatedEntityName(),
id,
eager,
isNullable() && !isProxyUnwrapEnabled
);
...
}
}
// 由于我們使用OneToOne,所以我們看一下OneToOneType的isNullable實(shí)現(xiàn):
public class OneToOneType extends EntityType {
...
protected boolean isNullable() {
// 可以看到是根據(jù)foreignKeyType來進(jìn)行判斷葱跋,稍后我們來看一下foreignKeyType是如何來的
return foreignKeyType==ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
}
...
}
// 調(diào)用SessionImpl進(jìn)行查詢
public class SessionImpl{
public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
// todo : remove
LoadEventListener.LoadType type = nullable
? LoadEventListener.INTERNAL_LOAD_NULLABLE
: eager
? LoadEventListener.INTERNAL_LOAD_EAGER
: LoadEventListener.INTERNAL_LOAD_LAZY;
LoadEvent event = new LoadEvent(id, entityName, true, this);
// 調(diào)用查詢
fireLoad(event, type);
if ( !nullable ) {
UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName );
}
return event.getResult();
}
}
// fireLoad調(diào)用了DefaultLoadEventListener的load
public class DefaultLoadEventListener {
...
protected Object load(
final LoadEvent event,
final EntityPersister persister,
final EntityKey keyToLoad,
final LoadEventListener.LoadType options) {
...
// 讀取關(guān)聯(lián)對(duì)象
Object entity = doLoad(event, persister, keyToLoad, options);
boolean isOptionalInstance = event.getInstanceToLoad() != null;
// 進(jìn)行為空處理持寄,如果為空直接拋異常
if ( !options.isAllowNulls() || isOptionalInstance ) {
if ( entity == null ) {
event.getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
}
}
...
return entity;
}
...
}
在上面的代碼里我們可以看到源梭,在關(guān)聯(lián)對(duì)象為空時(shí)娱俺,hibernate通過調(diào)用"options.isAllowNulls()和isOptionalInstance"來判斷是否需要拋異常。接下來我們?cè)傺芯恳幌耾ptions.isAllowNulls()是如何來的废麻。
接上面的話荠卷,我們看一下OneToOneType的foreignKeyType是如何來的,觀察一下構(gòu)建hbm時(shí)做了什么:
public final class HbmBinder {
// 綁定one-to-one
public static void bindOneToOne(Element node, OneToOne oneToOne, String path, boolean isNullable,
Mappings mappings) throws MappingException {
bindColumns( node, oneToOne, isNullable, false, null, mappings );
Attribute constrNode = node.attribute( "constrained" );
boolean constrained = constrNode != null && constrNode.getValue().equals( "true" );
oneToOne.setConstrained( constrained );
// 如果constrained為true則ForeignKeyType就為FOREIGN_KEY_FROM_PARENT
oneToOne.setForeignKeyType( constrained ?
ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT :
ForeignKeyDirection.FOREIGN_KEY_TO_PARENT );
...
}
}
所以最終的解決辦法:去掉constrained="true":
<hibernate-mapping package="beans" auto-import="true">
<class name="Person" table="person">
<id name="id" column="id" type="java.lang.Long">
<!-- 手動(dòng)指定ID -->
<generator class="assigned" />
</id>
<one-to-one name="idCard" cascade="save-update" lazy="false"/>
</class>
</hibernate-mapping>
那么constrained的意思是什么呢烛愧,通過閱讀以上代碼油宜,我們得出結(jié)論掂碱,當(dāng)constrained為true時(shí),表明關(guān)聯(lián)表肯定存在對(duì)應(yīng)的鍵與主表進(jìn)行對(duì)應(yīng)慎冤。
至于其他意思請(qǐng)百度疼燥。