关于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的配置:



    
        
            
            
        

        
    




    
        
            
            
        
    

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


    
        
            
            
        

        
    

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

至于其他意思请百度。

你可能感兴趣的:(关于hibernate 3 配置one-to-one 单向关联出现的异常:No row with the given identifier exists)