2

지연된 엔티티를 얻을 때 IDE에서 다음과 같은 예외 메시지가 표시됩니다 (프록시 엔티티에 저장된 위치를 찾을 수 없어서 전체 스택을 제공 할 수 없습니다).)이 예외에 대한 추적 : 여기 지연 초기화 된 인스턴스를 얻으려고 LazyInitializationException을합니다.

Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate com.epam.spring.core.domain.UserAccount_$$_jvste6b_4.toString() 

내가 제대로 스택 추적입니다 내가 사용하려는 게으른 초기화 된 개체의 필드에 액세스하려고 후 : 나는 봄을 사용하고

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 

    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) 

    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286) 

    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) 

    at com.epam.spring.core.domain.UserAccount_$$_jvstfc9_4.getMoney(UserAccount_$$_jvstfc9_4.java) 

    at com.epam.spring.core.web.rest.controller.BookingController.refill(BookingController.java:128) 

을 데이터, 구성된 JpaTransactionManager, 데이터베이스는 MySql, ORM 공급자는 최대 절전 모드 4입니다. 주석 @EnableTransactionManagement가 켜져 있고, @Transactional은 내가 상상할 수있는 곳이라도 있지만 아무 것도 작동하지 않습니다. 여기

는 관계입니다 :

@Entity 
public class User extends DomainObject implements Serializable { 

    .. 

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    @JoinColumn(name = "user_fk") 
    private UserAccount userAccount; 

    .. 

@Entity 
public class UserAccount extends DomainObject { 

    .. 

    @OneToOne(mappedBy = "userAccount") 
    private User user; 

    .. 

.. 구성의 조각 :

@Bean 
    public DataSource dataSource() { 
     DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
     dataSource.setDriverClassName(env.getRequiredProperty(PROP_NAME_DATABASE_DRIVER)); 
     dataSource.setUrl(env.getRequiredProperty(PROP_NAME_DATABASE_URL)); 
     dataSource.setUsername(env.getRequiredProperty(PROP_NAME_DATABASE_USERNAME)); 
     dataSource.setPassword(env.getRequiredProperty(PROP_NAME_DATABASE_PASSWORD)); 
     return dataSource; 
    } 

    @Bean 
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() { 
     LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); 
     entityManagerFactoryBean.setDataSource(dataSource()); 
     entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class); 
     entityManagerFactoryBean.setPackagesToScan(env.getRequiredProperty(PROP_ENTITYMANAGER_PACKAGES_TO_SCAN)); 
     entityManagerFactoryBean.setJpaProperties(getHibernateProperties());    
     return entityManagerFactoryBean; 
    } 

    @Bean 
    public JpaTransactionManager transactionManager(@Autowired DataSource dataSource, 
                @Autowired EntityManagerFactory entityManagerFactory) { 
     JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(); 
     jpaTransactionManager.setEntityManagerFactory(entityManagerFactory); 
     jpaTransactionManager.setDataSource(dataSource); 

     return jpaTransactionManager; 
    } 

..이 내가 UserAccount 검색하는 방법입니다 :

@RequestMapping(...) 
    @Transactional() 
    public void refill(@RequestParam Long userId, @RequestParam Long amount) { 
     User user = userService.getById(userId); 
     UserAccount userAccount = user.getUserAccount(); 
     userAccount.setMoney(userAccount.getMoney() + amount); 
    } 

최대 절전 모드를 버전은 4.3.8입니다. 최종, 스프링 데이터 1.3.4.RELEASE 및 MySql 커넥터 5.1.29.

다른 것이 필요한지 물어보십시오. 미리 감사드립니다.

+0

스택 추적의이 부분은 아무것도 정말 보여줍니다. –

+0

@ v.ladynev 전체 스택을 제공해야합니까? –

+0

이'메소드가있는 부분은 org.hibernate.LazyInitializationException'을 던졌습니다. –

답변

3

먼저, 문제의 근원이 트랜잭션이 아니라는 것을 이해해야합니다. 우리에게는 트랜잭션과 지속적인 컨텍스트 (세션)가 있습니다. @Transactional annotation Spring은 트랜잭션을 생성하고 영속 컨텍스트를 연다. 메소드가 호출 된 후에는 지속적 컨텍스트가 닫힙니다.

당신이 user.getUserAccount()을받을

당신이 (당신이 UserUserAccount를로드하지 않는 경우) UserAccount을 래핑하는 프록시 클래스가 있습니다. 따라서 지속적인 컨텍스트가 닫히면 UserAccount의 메서드 호출 중 LazyInitializationException이 있습니다 (예 : toString()).

수준에서만 작동하는 @Transactional이 있습니다. 아마 이것은 맞습니다. 다른 서비스 방법 인 updateMoney(userId, amount)을 사용해야합니다.

컨트롤러 메서드에서 @Transactional을 사용하려면 컨트롤러를 Spring 컨텍스트에서 가져와야합니다. 그리고 Spring은 모든 @Transactional 메서드를 영구적 인 문맥을 열고 닫는 특별한 방법으로 포장해야한다는 것을 이해해야한다. 다른 방법은 Session Per Request Antipattern을 사용하는 것입니다. 특별한 HTTP 필터를 추가해야합니다.

+0

고마워, 이제는 초기화에 문제가 게으른로드되는 개체 개체를 참조하십시오. 그러나 어떻게 초기화를 고칠 수 있습니까? –

+0

@DmitrySenkovich 내 대답 업데이트 –

+0

하지만 컨트롤러 방식으로 거래를 사용할 수없는 이유는 무엇입니까? 그 차이점은 무엇입니까? 그러나 당신의 제안을 시도하겠습니다. –

1

@ vladimev 간단히 설명하자면, 당신은 영속 컨텍스트 외부에서 게으른 관계를 초기화하고 싶다는 것이 었습니다.

내가 이것에 대해 기사를 쓴, 당신은 도움이 될 수 : http://blog.arnoldgalovics.com/2017/02/27/lazyinitializationexception-demystified/

+0

고마워, 나는 그것을 읽을 것이다! 하지만 가장 흥미로운 부분은 Spring이 트랜잭션 동작을 추가하는 컨트롤러에 대한 프록시를 제공하지 않는다는 것입니다. –

+0

이를 수행하는 방법이 있습니다. 예를 들어 각 요청에 대해 트랜잭션 컨텍스트를 자동으로 추가하는 필터를 사용할 수 있습니다. 좋은 생각이 아닌 이유는 트랜잭션이 요청을 처리하는 관점이 아닌 비즈니스 논리 측면에서 작업 단위를 나타내야하기 때문입니다. – galovics

+0

예, 이제 알겠습니다. 감사합니다.) –