2017-12-09 33 views
0

나는 다음과 같은 것을 고민하고있다 : 는 PersistenceContext 및 EJB 트랜잭션 경계

내가이 엔터티에 대한 저장소로 상태가없는 빈을 가지고,이 빈은 엔티티 관리자는 선언했다.

다른 stateless bean에서이 bean을 호출 할 때 엔티티가 리턴 된 다음이 새로운 리턴 된 엔티티에서 릴레이션을 호출하면 "org.hibernate.LazyInitializationException : 예외 콜을 생성합니다. 롤 콜렉션을 지연 초기화하지 못했습니다 ". 이해할 수 있듯이 트랜잭션에 첨부 된 지속성 컨텍스트가 있거나 트랜잭션이 존재하지 않는 경우 새 컨텍스트를 작성하지만,이 경우 트랜잭션은 존재하며 저장소 빈을 호출하는 클라이언트의 상태 비 저장 Bean에서 시작됩니다. 여기

의 간단한 예 :

@Entity 
public class Config{ 

    Long id; 
    String description; 

    @ManyToOne(fetch=FetchType.LAZY) 
    @JoinColumn(name="equipment") 
    private Equipment equipment; 
} 

@Entity 
public class Equipment{ 
    Long id; 
    String name; 
    @OneToMany(mappedBy = "equipment") 
    Config config; 
} 

@Stateless 
public class EquipmentRepo{ 

    @PersistenceContext(type=PersistenceContextType.TRANSACTION) 
    EntityManager em; 

    public Equipment find(Long id) { 
     return em.find(Equipment.class, id);  
    } 

} 

@Stateless 
public class ServiceFacade { 

    @Inject 
    EquipmentRepo repo; 

    public List<Config> findEquipmentConfig(Long id) { 
     Equipment element = repo.find(id); 
     List<Config> configurations = element.getConfig(); 
     return configurations; 
    } 
}` 

답변

0

게으른 초기화는 액티브 한 트랜잭션 (transaction)를 필요로한다. 주의 깊게 확인해야하는 경우 예외의 원인은 누락 된 거래와 관련이 있습니다.

상태없는 메소드가 반환되면, 활성 트랜잭션은 종료되고, 트랜잭션은 컨테이너 관리이므로 hibernate는 엔티티 메소드를 호출하여 참조 된 지연로드 된 특성을로드 할 때 활성 트랜잭션을 예상한다.

이 문제는 일반적으로 엔티티 도메인이 트랜잭션/서비스 수준을 초과하여 사용되지 않도록하여 해결됩니다. 서비스가 반환되면 필수 필드가있는 매핑 된 엔터티의 dto를 반환해야합니다. 따라서이 경우 지연된 속성이 필요하면 동일한 트랜잭션 내에서 실행되고 사용자 자신의 dto에 매핑됩니다. 모든

+0

안녕하세요, maress, 답장을 보내 주셔서 감사합니다. 저는 이해 합니다만, 주입 (beanB)에 의해 또 다른 상태없는 빈을 호출 한 다음, beanB가 엔티티를 검색하는 상태 비 저장 빈 (beanA)이 있습니다. 트랜잭션은 사양에 따라 beanA에서 시작하고 beanB가 호출되면 현재 트랜잭션에 첨부하고 entityA를 beanA에 반환하므로 트랜잭션이 계속 실행됩니다. – cmd

0

첫째, 당신의 Equipment 클래스가 같아야합니다 있으리라 믿고있어 :

@Entity 
public class Equipment{ 
    Long id; 
    String name; 
    @OneToMany(mappedBy = "equipment") 
    List<Config> config; 
} 

config가 수집되어야한다.

@OneToMany 관계는 기본적으로 지연 초기화됩니다.

당신은 실행하면 :

는 개체 자체가 아니라 Config 프록시의 컬렉션을 가지고 하나의 Equipment 객체를 반환
Equipment element = repo.find(id); 

.

findEquipmentConfig 메소드 내에서 element.config을 반복하는 경우 JPA 구현은 참조 된대로 각 구성 엔티티를로드합니다. 이것은 엔티티 관리자와 트랜잭션이 여전히 활성화되어 있기 때문에 작동합니다.

그러나 당신은 findEquipmentConfig이라는 메서드를 반복적으로 사용하고 있습니다. 서블릿이나 트랜잭션 컨텍스트가 활성화되어 있지 않은 다른 종류의 웹 컨트롤러가 있다고 상상해보십시오. 따라서 org.hibernate.LazyInitializationException이 표시됩니다.

이제 더미로드 루프를 추가하여 모든 구성을 미리로드하는 것이 좋습니다.

하지만 악명 높은 N + 1 SELECT 문제와 응용 프로그램의 성능이 좋지 않기 때문에 좋지 않습니다.장비 엔티티가 1000 개의 설정 아이템을 가지고 있다면, 어플리케이션은 객체를로드하기 위해 1001 개의 SELECT 구문을 실행합니다 (추가 장비는 초기 장비 엔티티를로드하는 것입니다).

실제로는 JPA QL에서 사용할 수있는 쿼리 구문 join fetch의 동기 중 하나입니다. 이 장비의 설정을 모두 한 번에로드되어 있는지 확인합니다

public Equipment find(Long id) { 
    TypedQuery<Equipment> query = em.createQuery(
    "SELECT e FROM Equipment e LEFT JOIN FETCH e.config WHERE e.id = :id", Equipment.class); 
    return query.setParameter("id", id).getSingleResult(); 
} 

: 그것은 같은 것을 보이도록

당신은 당신의 find 방법을 변경할 수 있습니다.

+0

안녕하세요, 그렇지 않습니다 ... findEquipmentConfig를 호출하는 메소드가 ejb이므로 트랜잭션이 살아 있어야합니다. – cmd

+0

스택 추적을 추가하십시오. 당신이 우리에게 말하지 않는 것이 있습니다. 또한 어쨌든 쿼리를 변경하려고 시도 했습니까? 더 나은 성능을 얻을 수 있습니다. –