![](/img/trans.png)
[英]Transactional method called by method in same class called from another class
[英]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.