[SPRING] 스프링 데이터 JPA에서 LazyInitializationException을 해결하는 방법?
SPRING스프링 데이터 JPA에서 LazyInitializationException을 해결하는 방법?
나는 일대 다 (one-to-many-relation) 클래스를 가져야한다. 느슨하게로드 된 컬렉션에 액세스하려고하면 LazyInitializationException이 발생합니다. 나는 잠시 동안 웹 검색을하고 이제 컬렉션을 보유하는 클래스를로드하는 데 사용 된 세션이 닫혀 있기 때문에 예외가 발생한다는 것을 알고 있습니다. 그러나 나는 해결책을 찾지 못했다. (또는 나는 적어도 그것을 이해하지 못했다.) 기본적으로 나는 그 수업을 가지고 :
사용자
@Entity
@Table(name = "user")
public class User {
@Id
@GeneratedValue
@Column(name = "id")
private long id;
@OneToMany(mappedBy = "creator")
private Set<Job> createdJobs = new HashSet<>();
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public Set<Job> getCreatedJobs() {
return createdJobs;
}
public void setCreatedJobs(final Set<Job> createdJobs) {
this.createdJobs = createdJobs;
}
}
UserRepository
public interface UserRepository extends JpaRepository<User, Long> {}
UserService
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository repository;
boolean usersAvailable = false;
public void addSomeUsers() {
for (int i = 1; i < 101; i++) {
final User user = new User();
repository.save(user);
}
usersAvailable = true;
}
public User getRandomUser() {
final Random rand = new Random();
if (!usersAvailable) {
addSomeUsers();
}
return repository.findOne(rand.nextInt(100) + 1L);
}
public List<User> getAllUsers() {
return repository.findAll();
}
}
일
@Entity
@Table(name = "job")
@Inheritance
@DiscriminatorColumn(name = "job_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Job {
@Id
@GeneratedValue
@Column(name = "id")
private long id;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User creator;
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public User getCreator() {
return creator;
}
public void setCreator(final User creator) {
this.creator = creator;
}
}
JobRepository
public interface JobRepository extends JpaRepository<Job, Long> {}
Jobservice
@Service
@Transactional
public class JobService {
@Autowired
private JobRepository repository;
public void addJob(final Job job) {
repository.save(job);
}
public List<Job> getJobs() {
return repository.findAll();
}
public void addJobsForUsers(final List<User> users) {
final Random rand = new Random();
for (final User user : users) {
for (int i = 0; i < 20; i++) {
switch (rand.nextInt(2)) {
case 0:
addJob(new HelloWorldJob(user));
break;
default:
addJob(new GoodbyeWorldJob(user));
break;
}
}
}
}
}
앱
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class App {
public static void main(final String[] args) {
final ConfigurableApplicationContext context = SpringApplication.run(App.class);
final UserService userService = context.getBean(UserService.class);
final JobService jobService = context.getBean(JobService.class);
userService.addSomeUsers(); // Generates some users and stores them in the db
jobService.addJobsForUsers(userService.getAllUsers()); // Generates some jobs for the users
final User random = userService.getRandomUser(); // Picks a random user
System.out.println(random.getCreatedJobs());
}
}
세션이 현재 스레드에 바인드되어야한다는 것을 자주 읽었지 만 Spring의 주석 기반 구성으로이를 수행하는 방법을 알지 못합니다. 누군가가 저에게 어떻게 할 수 있다고 지적 할 수 있습니까?
추신 게으른 로딩을 사용하고 싶기 때문에 열망하는 로딩 옵션이 없습니다.
해결법
-
==============================
1.기본적으로 트랜잭션 내부에있는 동안 게으른 데이터를 가져와야합니다. 서비스 클래스가 @Transactional 인 경우에는 모든 것이 정상적으로 처리되어야합니다. 서비스 클래스를 벗어나 게으른 콜렉션을 얻으려면 main () 메소드 인 System.out.println (random.getCreatedJobs ());에있는 해당 예외가 발생합니다.
기본적으로 트랜잭션 내부에있는 동안 게으른 데이터를 가져와야합니다. 서비스 클래스가 @Transactional 인 경우에는 모든 것이 정상적으로 처리되어야합니다. 서비스 클래스를 벗어나 게으른 콜렉션을 얻으려면 main () 메소드 인 System.out.println (random.getCreatedJobs ());에있는 해당 예외가 발생합니다.
이제, 귀하의 서비스 방법이 무엇을 반환해야하는지에 관해서 설명합니다. userService.getRandomUser ()가 조작 할 수 있도록 초기화 된 작업을 가진 사용자를 반환 할 것으로 예상되는 경우 가져 오는 것은 해당 메서드의 책임입니다. Hibernate로 그것을하는 가장 간단한 방법은 Hibernate.initialize (user.getCreatedJobs ())를 호출하는 것이다.
-
==============================
2.엔티티 그래프와 함께 JPA 2.1 사용을 고려하십시오.
엔티티 그래프와 함께 JPA 2.1 사용을 고려하십시오.
JPA 2.0에서는 종종 지연로드가 문제가되었습니다. 엔티티 FetchType.LAZY 또는 FetchType.EAGER에서 정의해야하며 관계가 트랜잭션 내에서 초기화되는지 확인해야합니다.
이 작업은 다음과 같이 수행 할 수 있습니다.
두 방법 모두 완벽하지 못합니다. JPA 2.1 엔티티 그래프가 더 나은 솔루션입니다.
-
==============================
3.2 가지 옵션이 있습니다.
2 가지 옵션이 있습니다.
옵션 1 : BetaRide에서 언급했듯이 EAGER 가져 오기 전략을 사용합니다.
옵션 2 : hibernate를 사용하여 데이터베이스에서 사용자를 얻은 후에 콜렉션 요소를로드하기 위해 아래 코드를 코드에 추가하십시오 :
Hibernate.initialize(user.getCreatedJobs())
이것은 hibernate에게 collection 요소를 초기화하도록 지시한다.
-
==============================
4.변화
변화
@OneToMany(mappedBy = "creator") private Set<Job> createdJobs = new HashSet<>();
에
@OneToMany(fetch = FetchType.EAGER, mappedBy = "creator") private Set<Job> createdJobs = new HashSet<>();
또는 서비스 내에서 Hibernate.initialize를 사용하면 동일한 효과를 얻을 수 있습니다.
-
==============================
5.JPA 2.1을 사용할 가능성이 없지만 컨트롤러에서 엔티티를 반환 할 가능성을 유지하려는 사람 (응답으로 쓰기가 가능한 String / JsonNode / byte [] / void가 아님)의 경우 :
JPA 2.1을 사용할 가능성이 없지만 컨트롤러에서 엔티티를 반환 할 가능성을 유지하려는 사람 (응답으로 쓰기가 가능한 String / JsonNode / byte [] / void가 아님)의 경우 :
트랜잭션에 DTO를 구축 할 가능성이 여전히 있으며, 이는 컨트롤러가 리턴합니다.
@RestController @RequestMapping(value = FooController.API, produces = MediaType.APPLICATION_JSON_VALUE) class FooController{ static final String API = "/api/foo"; private final FooService fooService; @Autowired FooController(FooService fooService) { this.fooService= fooService; } @RequestMapping(method = GET) @Transactional(readOnly = true) public FooResponseDto getFoo() { Foo foo = fooService.get(); return new FooResponseDto(foo); } }
-
==============================
6.컨텍스트 구성 클래스에 @EnableTransactionManagement 주석을 추가하여 Spring 트랜잭션 관리자를 활성화해야한다.
컨텍스트 구성 클래스에 @EnableTransactionManagement 주석을 추가하여 Spring 트랜잭션 관리자를 활성화해야한다.
두 서비스 모두 @Transactional annotation과 그것의 default value 속성을 가지고 있기 때문에 TxType.Required이므로 트랜잭션 관리자가 켜져 있으면 현재 트랜잭션은 서비스간에 공유됩니다. 따라서 세션을 사용할 수 있어야하고 LazyInitializationException을 얻지 못할 것입니다.
from https://stackoverflow.com/questions/26507446/how-to-resolve-lazyinitializationexception-in-spring-data-jpa by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 루트 컨텍스트와 디스패처 서블릿 컨텍스트가 Spring MVC 웹 애플리케이션과 정확히 얼마나 일치합니까? (0) | 2019.03.05 |
---|---|
[SPRING] HTTPS / HTTP 포트를 실행하기 위해 Spring Boot를 설정하는 방법 (0) | 2019.03.05 |
[SPRING] Spring MVC 요청없이 WEB-INF하에 파일 가져 오기 (0) | 2019.03.05 |
[SPRING] Spring 애플리케이션을위한 jboss-deployment-structure.xml이 필요한 이유는 무엇입니까? (0) | 2019.03.05 |
[SPRING] Spring은이 종속성에 대한 autowire 후보가 될 수있는 적어도 하나의 bean을 예상했다. (0) | 2019.03.05 |