簡體   English   中英

公共方法中的 LazyInitializationException 標記為 @Transactional 並從另一個 bean 調用

[英]LazyInitializationException in public method marked as @Transactional and called from another bean

我正在處理一個相當奇怪的問題,我無法理解。

我有一個名為CompanyAuthorization的 class ,它是一個 Spring @Service並且依賴於實現CompanyAuthorizationStrategy接口的bean,通過構造函數注入,代碼如下:

@Service
public class CompanyAuthorization
{
    private final CompanyRepository companyRepository; // <-- JpaRepository
    private final Set<CompanyAuthorizationStrategy> authorizationStrategies;

    public CompanyAuthorization(CompanyRepository companyRepository,
            Set<CompanyAuthorizationStrategy> authorizationStrategies)
    {
        this.companyRepository = companyRepository;
        this.authorizationStrategies = authorizationStrategies;
    }

    // ...

    @Transactional(readOnly = true)
    public boolean authorizeUserToCompany(CompanyIdentity companyIdentity)
    {
        Company c = companyRepository.getOne(companyIdentity);
        User u = SecurityUtils.getCurrentUserOrFail();

        return authorizationStrategies.stream()
                .anyMatch(strategy -> strategy.isAuthorized(c, u));
    }
}

這些 CompanyAuthorizationStrategies 也由 Spring 初始化(使用@Service注釋,啟用組件掃描)。

這是有問題的示例:

@Service
public class OwnerCompanyAuthorizationStrategy implements CompanyAuthorizationStrategy
{
    @Transactional(readOnly = true)
    @Override
    public boolean isAuthorized(Company company, User user)
    {
        return company.getOwner().equals(user);
    }
}

在代碼中,我可能會使用類似的東西:

@PreAuthorize("isAuthenticated() && @companyAuthorization.authorizeUserToCompany(#companyIdentity)")
public void doSomething(CompanyIdentity companyIdentity) {
    // ...
}

問題是,調用doSomething會導致LazyInitializationException ,這是由OwnerCompanyAuthorizationStrategy的方法isAuthorized中的company.getOwner()調用引起的 - 這是@Transactional (.)。

堆棧跟蹤的相關部分:

org.hibernate.LazyInitializationException: could not initialize proxy [<MY_PROGRAM>.core.domain.entity.User#testuser] - no Session
        at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:170)
        at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:310)
        at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45)
        at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
        at <MY_PROGRAM>.core.domain.entity.User$HibernateProxy$FNb1iLnl.equals(Unknown Source)
        at <MY_PROGRAM>.retail.core.api.authorization.OwnerCompanyAuthorizationStrategy.isAuthorized(OwnerCompanyAuthorizationStrategy.java:16)
        at <MY_PROGRAM>.retail.core.api.authorization.OwnerCompanyAuthorizationStrategy$$FastClassBySpringCGLIB$$9600d666.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
        at <MY_PROGRAM>.retail.core.api.authorization.OwnerCompanyAuthorizationStrategy$$EnhancerBySpringCGLIB$$3c84e20c.isAuthorized(<generated>)
        at <MY_PROGRAM>.retail.core.api.authorization.CompanyAuthorization.lambda$authorizeUserToCompany$0(CompanyAuthorization.java:74)

我錯過了什么? 為什么沒有 Session 可用於isAuthorized方法,即@Transactional ,包裝在 Spring 的代理中並在外部調用? 我想不通。

請從以下位置更改存儲庫調用:

Company c = companyRepository.getOne(companyIdentity);

Company c = companyRepository.findOne(companyIdentity);

根據您的代碼,您正在立即使用公司實例,沒有理由使用延遲加載。 簡而言之, findOne會急切地加載實體,因此它將解決您的問題。 請在這里查看很好的解釋

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM