服务委托引发“ LazyInitializationException:无法初始化代理-没有会话”

时间:2018-08-11 14:43:27

标签: java hibernate java-ee

我知道有很多“无法初始化代理-没有会话”的问题,但是我没有找到解决问题的答案。

问题是,当我委托fetchLazy方法时,它会引发上述异常。这是我的服务类的简化版本:

服务

public abstract class Service<S extends Service<S,E>, E extends Entity> {
    @PersistenceContext private EntityManager entityManager;

    // person = personService.fetchLazy(person, Person::getCompany); OK
    public E fetchLazy(E entity, Function<E,?> proxyMapper) {
        E attachedEntity = entityManager.find(entity.getClass(), entity.getId());
        Object proxy = proxyMapper.apply(attachedEntity);
        if (!Hibernate.isInitialized(proxy)) { Hibernate.initialize(proxy); }
        return attachedEntity;
    }

    // person = personService.fetch(person).lazy(Person::getCompany); EXCEPTION
    public FetchBuilder fetch(E entity) { return new FetchBuilder((S) this, entity); }

    public class FetchBuilder {
        private final S service; private final E entity;
        LazyFetchBuilder(E e, S s) { this.entity = e; this.service = s; }
        public E lazy(E entity, Function<E,?> proxyMapper) {
             return service.fetchLazy(entity, proxyMapper); // DELEGATE
        }
    }
}

PersonService

@Stateless
public class PersonService extends Service<PersonService,Person> { ... }

PersonBean

@Named @ViewScoped
public class PersonBean implements Serializable {
    @EJB private PersonService personService;

    @PostConstruct public void init()  {
        person = personService.getById(id);
        person = personService.fetchLazy(person, Person::getCompany); // OK
        person = personService.fetch(person).lazy(Person::getCompany); // EXCEPTION
    }
}

1 个答案:

答案 0 :(得分:1)

我将假定此服务是Java EE或Spring事务服务。声明式事务是基于代理的。当您使用依赖项注入获取服务的实例并调用事务方法时,实际上您会调用包装服务的事务代理的方法:

client ----------> transactional proxy -----------> service
                    - start the transaction
                    - call the service
                    - commit
                    -return the value returned by the service

致电fetchLazy()时,一切正常:

  • 交易开始,
  • 然后使用实体管理器找到实体,并初始化其公司代理,
  • 然后交易被提交
  • 然后您将获得带有初始化公司的实体。

致电fetch()时,会发生以下情况

  • 交易开始,
  • 已构建FetchBuilder
  • 然后交易被提交
  • 然后您将获得FetchBuilder

由于您从不使用实体管理器,因此该交易实际上无用。

现在,当您在返回的构建器上调用fetch()时会发生什么?它在FetchBuilder的fetchLazy实例变量上调用serviceservice是实际服务实例的实例,而不是包装服务实例的代理的实例,因为您是用this从服务实例本身对其进行初始化的。因此,您将绕过代理,因此这些交易就不会将对find()的调用和公司的初始化包装起来。