关于hibernate 3 配置one-to-one 单向关联出现的异常:No row with the given identifier exists

最近需要增加业务,需要增加一对一关联,由于系统采用hibernate3,而且使用了xml配置方式,所以就举例一下xml配置方式:

假设我们有两个类,人和身份证。我们要达到的效果就是查询人的信息时自动读取身份证。

类:

//人
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">
            <!-- 手动指定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">
            <!-- 手动指定ID -->
            <generator class="assigned" />
        </id>
    </class>
</hibernate-mapping>

这样配置后,如果数据一致是没有问题的,但是由于我们是后加业务,所以肯定会造成数据不一致的情况,意思就是有人的信息,但是没有身份证的信息。这样一来,hibernate会报错:

No row with the given identifier exists

让我们阅读源码,了解出错原因(之前已通过查阅异常栈定位到出错位置):

// EntityType 有几个子类,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() );
    
        
        // 调用查询,传了传了一个isNullable()用于处理为空的情况
        // isNullable()是抽象的,又子类实现
        Object proxyOrEntity = session.internalLoad(
                getAssociatedEntityName(),
                id,
                eager,
                isNullable() && !isProxyUnwrapEnabled
        );
        ...
    }
}

// 由于我们使用OneToOne,所以我们看一下OneToOneType的isNullable实现:

public class OneToOneType extends EntityType {
    ...
    protected boolean isNullable() {
        // 可以看到是根据foreignKeyType来进行判断,稍后我们来看一下foreignKeyType是如何来的
        return foreignKeyType==ForeignKeyDirection.FOREIGN_KEY_TO_PARENT;
    }
    ...
}

// 调用SessionImpl进行查询
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);
        // 调用查询
        fireLoad(event, type);
        if ( !nullable ) {
            UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName );
        }
        return event.getResult();
    }
}
// fireLoad调用了DefaultLoadEventListener的load
public class DefaultLoadEventListener {
    ...
    protected Object load(
            final LoadEvent event,
            final EntityPersister persister,
            final EntityKey keyToLoad,
            final LoadEventListener.LoadType options) {
    
            ...
            
            // 读取关联对象
            Object entity = doLoad(event, persister, keyToLoad, options);
    
            boolean isOptionalInstance = event.getInstanceToLoad() != null;
    
            // 进行为空处理,如果为空直接抛异常
            if ( !options.isAllowNulls() || isOptionalInstance ) {
                if ( entity == null ) {
                    event.getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( event.getEntityClassName(), event.getEntityId() );
                }
            }
    
            ...
    
            return entity;
        }
    ...
}

在上面的代码里我们可以看到,在关联对象为空时,hibernate通过调用"options.isAllowNulls()和isOptionalInstance"来判断是否需要抛异常。接下来我们再研究一下options.isAllowNulls()是如何来的。

接上面的话,我们看一下OneToOneType的foreignKeyType是如何来的,观察一下构建hbm时做了什么:

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">
            <!-- 手动指定ID -->
            <generator class="assigned" />
        </id>

        <one-to-one name="idCard" cascade="save-update" lazy="false"/>
    </class>
</hibernate-mapping>

那么constrained的意思是什么呢,通过阅读以上代码,我们得出结论,当constrained为true时,表明关联表肯定存在对应的键与主表进行对应。

至于其他意思请百度。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,094评论 19 139
  • Hibernate: 一个持久化框架 一个ORM框架 加载:根据特定的OID,把一个对象从数据库加载到内存中OID...
    JHMichael阅读 6,117评论 0 27
  • 相信每一个结婚的女人,都会面临婆媳关系问题,为了防止婚姻被破坏,为了守护家庭的和平,给大家总结一下引发婆媳关系的矛...
    婚礼纪阅读 5,069评论 0 51
  • 写作是将生活过的生活复原化的过程,特别是那些叙事或回忆录之类的写作。能做到何种程度的还原,取决于写作者的观察力、才...
    蓝柿阅读 5,160评论 7 22
  • 《皓月当空》—穿石— 帝都冬季的夜晚很难与你相遇如玉盘般的你走进了我的眼帘是才明月几时有 此刻微风徐来竹影轻盈也许...
    东方诗空阅读 2,372评论 2 2

友情链接更多精彩内容