JPA

์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ Lazy Loading์ผ ๋•Œ, ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @Transactional์ด ์—†๋‹ค๋ฉด?

๋งน์ˆ˜ํ˜ธ๋นต 2024. 8. 22. 16:50
728x90

๐Ÿค” ๋ฌธ์ œ ์ƒํ™ฉ

JPA์—์„œ ์—”ํ‹ฐํ‹ฐ ๊ฐ„ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ Lazy Loading์œผ๋กœ ์„ค์ •ํ–ˆ์„ ๋•Œ, ์ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†๋‹ค๋ฉด LazyInitializationException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ˆ์‹œ ์ฝ”๋“œ๋กœ ๋ณด์ž๋ฉด

@Entity
public class User {
    @Id
    private Long id;

    @OneToMany(fetch = FetchType.LAZY)
    private List<Order> orders;
}

public class UserService {
    public void processUserOrders(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();
        user.getOrders().forEach(order -> {
            // ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ๋กœ์ง
        });
    }
}

์ด ๊ฒฝ์šฐ, processUserOrders ๋ฉ”์„œ๋“œ์—์„œ user.getOrders()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ LazyInitializationException์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ•Š๏ธ ๋‚˜์˜ ์ƒ๊ฐ

์‹ค์ œ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์—์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ด๋ฏธ ๋‹ซํ˜€์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. -> ๊ทธ๋ž˜์„œ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋‹ค!
์ด ๋ฌธ์ œ๋Š” ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„์™€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์•„ ๋ฐœ์ƒํ•œ๋‹ค.
@Transactional ์–ด๋…ธํ…Œ์ด์…˜์ด ์—†์œผ๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ํ›„ ์ฆ‰์‹œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋‹ซํžˆ๊ฒŒ ๋˜์–ด ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๊ฒŒ ๋œ๋‹ค!!

๐Ÿ’ซ ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

1.@Transactional ์ถ”๊ฐ€ : ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ํ•ด๋‹น ๋ฉ”์„œ๋“œ์— @Transactional์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ”์„œ๋“œ ์‹คํ–‰ ๋™์•ˆ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ์œ ์ง€๋˜์–ด ์ง€์—ฐ ๋กœ๋”ฉ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

@Transactional
public void processUserOrders(Long userId) {
    // ๊ธฐ์กด ์ฝ”๋“œ
}

2.Fetch Join ์‚ฌ์šฉ: JPQL์ด๋‚˜ Criteria API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•„์š”ํ•œ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋”ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :userId")
    Optional<User> findByIdWithOrders(@Param("userId") Long userId);
}

3.DTO ์‚ฌ์šฉ: ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ DTO๋กœ ์กฐํšŒํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค.(Projection) ์ด ๋ฐฉ๋ฒ•์€ ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
4.Open Session In View (OSIV) ํŒจํ„ด ์‚ฌ์šฉ: ์ด ๋ฐฉ๋ฒ•์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ทฐ ๋ Œ๋”๋ง๊นŒ์ง€ ์œ ์ง€ํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ์˜ค๋ž˜ ์œ ์ง€ํ•˜๋Š” ๋‹จ์ ์ด ์žˆ์–ด ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
5.ํ”„๋ก์‹œ ์ดˆ๊ธฐํ™”: ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์—ฐ๊ด€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค.

@Transactional(readOnly = true)
public void processUserOrders(Long userId) {
    User user = userRepository.findById(userId).orElseThrow();
    Hibernate.initialize(user.getOrders());
    // ์ดํ›„ ๋กœ์ง
}