jpa+hibernate5中lazy无效的解决办法(JpaRepository的save方法源码解析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011870280/article/details/85678155

  今天发现fetch = FetchType.LAZY这个设置无效,在查询和新增都会查一遍。

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "loan_req_no", referencedColumnName = "loanReqNo", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "null", value = ConstraintMode.NO_CONSTRAINT))
private LoanOrderEntity loanOrderEntity;

诸多办法尝试无果后,决定深入源码查看原因,以下是SimpleJpaRepository保存实体的源码:

@Transactional
public <S extends T> S save(S entity) {
        判断是否是新实体
	if (entityInformation.isNew(entity)) {
		em.persist(entity);
		return entity;
	} else {
		return em.merge(entity);
	}
}

 然后isNew判断如果实体不是基本类型,那么ID为null就是新实体

public boolean isNew(T entity) {
        获取实体ID
	ID id = getId(entity);
	Class<ID> idType = getIdType();
        判断ID不是基本类型
	if (!idType.isPrimitive()) {
		return id == null;
	}
	if (id instanceof Number) {
		return ((Number) id).longValue() == 0L;
	}
	throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
}

我这实体是修改,会调用EntityManager的merge方法修改实体,其中判断实体状态

switch ( entityState ) {
	case DETACHED:
		entityIsDetached( event, copyCache );
		break;
	case TRANSIENT:
		entityIsTransient( event, copyCache );
		break;
	case PERSISTENT:
		entityIsPersistent( event, copyCache );
		break;
	default: //DELETED
		throw new ObjectDeletedException(
				"deleted instance passed to merge",
				null,
				getLoggableName( event.getEntityName(), entity )
		);

我的实体是DETACHED(游离态),在entityIsDetached方法中的先用session.get方法查出实体,在这把实体所有关联的ManyToOne对象都查出来了,就算设置了fetch=FetchType=LAZY也毫无效果,如果依赖的对象多的话,这样是很不效率的

...
final Object result = source.get( entityName, clonedIdentifier );
source.getLoadQueryInfluencers().setInternalFetchProfile( previousFetchProfile );
if ( result == null ) {
	entityIsTransient( event, copyCache );
}else {
	((MergeContext) copyCache).put( entity, result, true ); //before cascade!
	final Object target = source.getPersistenceContext().unproxy( result );
	...
}

接下来是优化方案

1,增加RealLazy

@Retention(RetentionPolicy.RUNTIME)
public @interface RealLazy {
}

1,重写SimpleJpaRepository的save方法,判断实体类带@RealLazy注解的才不查询实体

public class CustomJpaRepository<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
    private final JpaEntityInformation<T, ?> entityInformationWrap;
    private final EntityManager emWrap;
    public CustomJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        entityInformationWrap=entityInformation;
        emWrap=entityManager;
    }

    @Override
    public <S extends T> S save(S entity) {
        if (entityInformationWrap.isNew(entity)) {
            emWrap.persist(entity);
            return entity;
        } else {
            if(Objects.nonNull(entity.getClass().getAnnotation(RealLazy.class))){
                ((HibernateEntityManager) emWrap).getSession().update(entity);
                return entity;
            }else{
                return emWrap.merge(entity);
            }
        }
    }
}

3,在启动类指定我们SimpleJpaRepository继承类

@EnableJpaRepositories(repositoryBaseClass = CustomJpaRepository.class)

tips:同理可以重写findAll方法,让findAll不要实时加载依赖类

猜你喜欢

转载自blog.csdn.net/u011870280/article/details/85678155
今日推荐