LazyInitializationException в методе publi c, помеченном как @Transactional и вызываемом из другого bean-компонента - PullRequest
0 голосов
/ 27 мая 2020

У меня довольно странная проблема, я не могу понять.

У меня есть класс с именем CompanyAuthorization, который является Spring @Service и зависит от beans, реализующего CompanyAuthorizationStrategy интерфейс, которые вводятся через конструктор, код выглядит следующим образом:

@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, сканирование компонентов включено).

Вот пример проблемы: c one:

@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, вызванный вызовом company.getOwner() в методе OwnerCompanyAuthorizationStrategy isAuthorized - который IS @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)

Что мне не хватает? Почему недоступен сеанс для метода isAuthorized, который равен @Transactional, завернутый в прокси-сервер Spring и вызываемый извне? Я не могу понять этого.

1 Ответ

0 голосов
/ 27 мая 2020

Измените вызов репозитория с:

Company c = companyRepository.getOne(companyIdentity);

на

Company c = companyRepository.findOne(companyIdentity);

Согласно вашему коду, вы сразу используете экземпляр компании, и нет причин использовать отложенную загрузку. Короче говоря, findOne загружает объект с нетерпением, поэтому он решит вашу проблему. Пожалуйста, проверьте хорошее объяснение здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...