Back-end

[JPA] OSIV(Open Session In View)λž€?

μ„œμ±„λ¦¬ 2023. 9. 21. 17:54

πŸ€” OSIV에 λŒ€ν•΄ μ•Œμ•„λ³΄κΈ° μ „.. Persistence Context λ₯Ό λͺ¨λ₯Έλ‹€λ©΄?

 

[JPA] μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ™μž‘μ›λ¦¬μ™€ 이점

πŸ‘ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ™μž‘μ›λ¦¬ 🌱 member μ—”ν‹°ν‹°λ₯Ό μΆ”κ°€ν•˜λŠ” κ³Όμ • 1. μ—”ν‹°ν‹°κ°€ μ˜μ†ν™”(persist)λ˜μ–΄ 1μ°¨ μΊμ‹œμ— μ €μž₯λœλ‹€. 2. 쓰기지연 SQL μ €μž₯μ†Œμ— INSERT문이 μƒμ„±λ˜μ–΄ 1μ°¨ μΊμ‹œμ— λ“±λ‘λœ 데이터λ₯Ό

chaewsscode.tistory.com


🀧 OSIV μš”μ•½

더보기

νŠΉμ§•

  • OSIVλŠ” ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό μƒμ„±ν•΄μ„œ μš”μ²­μ΄ 끝날 λ•ŒκΉŒμ§€ 같은 μ˜μ†μ„± μ»¨ν…μŠ€λ₯Ό μœ μ§€ν•œλ‹€.
    λ”°λΌμ„œ ν•œ 번 쑰회된 μ—”ν‹°ν‹°λŠ” μš”μ²­μ΄ 끝날 λ•ŒκΉŒμ§€ μ˜μ† μƒνƒœλ₯Ό μœ μ§€ν•œλ‹€.
  • μ—”ν‹°ν‹° μˆ˜μ •μ€ νŠΈλžœμž­μ…˜μ΄ μžˆλŠ” 계측(μ„œλΉ„μŠ€, λ ˆν¬μ§€ν† λ¦¬ 계측)μ—μ„œλ§Œ λ™μž‘ν•œλ‹€. νŠΈλžœμž­μ…˜μ΄ μ—†λŠ” ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측은 지연 λ‘œλ”©μ„ 포함해 쑰회만 ν•  수 μžˆλ‹€.

단점

  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ DB 컀λ„₯μ…˜μ€ 1:1둜 λ¬Όκ³ μžˆλŠ” 관계이기 λ•Œλ¬Έμ— ν”„λ ˆμ  ν…Œμ΄μ…˜ λ‘œμ§κΉŒμ§€ DB 컀λ„₯μ…˜ μžμ›μ„ λ‚­λΉ„ν•˜κ²Œ λœλ‹€.
  • OSIVλ₯Ό μ μš©ν•˜λ©΄ 같은 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ΄ κ³΅μœ ν•˜κ²Œλ  μˆ˜λ„ μžˆλ‹€.
  • ν”„λ ˆμ  ν…Œμ΄μ…˜μ—μ„œ μ—”ν‹°ν‹°λ₯Ό μˆ˜μ •ν•˜κ³  λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μˆ˜ν–‰ν•˜λ©΄ μ—”ν‹°ν‹°κ°€ μˆ˜μ •λ  수 μžˆλ‹€.
  • ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ λ Œλ”λ§ κ³Όμ •μ—μ„œ 지연 λ‘œλ”©μ— μ˜ν•΄ SQL이 μ‹€ν–‰λœλ‹€.
    λ”°λΌμ„œ μ„±λŠ₯ νŠœλ‹μ‹œμ— 확인해야 ν•  뢀뢄이 넓어진닀.

μŠ€ν”„λ§μ€ 기본으둜 νŠΈλžœμž­μ…˜ λ²”μœ„μ˜ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ „λž΅μ„ μ‚¬μš©ν•œλ‹€.

 

  • λΉ„μ˜μ†
    • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ μ „ν˜€ 관계가 μ—†λŠ” μƒˆλ‘œμš΄ μƒνƒœ
    • 객체λ₯Ό μƒμ„±λ§Œ ν•œ μƒνƒœ
  • μ˜μ†
    • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— κ΄€λ¦¬λ˜λŠ” μƒνƒœ
    • 객체 생성 ν›„ em.persist(member)둜 μ €μž₯ν•œ μƒνƒœ
  • μ€€μ˜μ†
    • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯λ˜μ—ˆλ‹€κ°€ λΆ„λ¦¬λœ μƒνƒœ
    • em.detach(member)둜 μ˜μ†μ΄ λΆ„λ¦¬λœ μƒνƒœ
  • μ‚­μ œ
    • μ‚­μ œλœ μƒνƒœ
    • em.remove(member)둜 영ꡬ μ €μž₯ν•œ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ§€μš°λŠ” μƒνƒœ
더보기

πŸ€” λΉ„μ˜μ†κ³Ό μ€€μ˜μ† μƒνƒœμ˜ 차이점이 뭘까?

 

λ‘˜μ˜ κ°€μž₯ 큰 μ°¨μ΄λŠ” ν•œλ²ˆ μ˜μ† μƒνƒœκ°€ 된 적이 μžˆλŠ”κ°€ μ—†λŠ”κ°€μ˜ 차이

μ˜μ† μƒνƒœκ°€ 되렀면 μ‹λ³„μžκ°€ λ°˜λ“œμ‹œ μ‘΄μž¬ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ— μ˜μ† μƒνƒœκ°€ λ˜μ—ˆλ‹€κ°€ λ‹€μ‹œ μ€€μ˜μ† μƒνƒœκ°€ 되면 μ‹λ³„μžκ°€ 항상 μ‘΄μž¬ν•œλ‹€.

ex) μ—”ν‹°ν‹° 쑰회 ν›„ νŠΈλžœμž­μ…˜μ΄ λλ‚˜ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ‚¬λΌμ§€λŠ” 경우 μ‘°νšŒν•œ μ—”ν‹°ν‹°κ°€ μ€€μ˜μ† μƒνƒœκ°€ 됨

 

νŠΈλžœμž­μ…˜μ€ 보톡 μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ μ‹œμž‘λ˜κΈ° λ•Œλ¬Έμ— μ„œλΉ„μŠ€ 계측이 λλ‚˜λŠ” μ‹œμ μ— νŠΈλž™μž­μ…˜μ΄ μ’…λ£Œλ˜μ–΄ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ„ μ’…λ£Œλœλ‹€.

νŠΈλžœμž­μ…˜ 컀밋 μ‹œ JPAλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό λ¨Όμ € flush ν•΄ λ³€κ²½ λ‚΄μš©μ„ DB에 λ°˜μ˜ν•œ ν›„ DB νŠΈλžœμž­μ…˜μ„ μ»€λ°‹ν•œλ‹€.

(μ˜ˆμ™Έκ°€ λ°œμƒν•΄ νŠΈλžœμž­μ…˜μ΄ λ‘€λ°± ν›„ μ’…λ£Œλ  경우 flush 호좜 X)

λ”°λΌμ„œ νŠΈλžœμž­μ…˜μ΄ μ—†λŠ” ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측(Controller)μ—μ„œ μ‘°νšŒν•œ μ—”ν‹°ν‹°λŠ” μ€€μ˜μ† μƒνƒœκ°€ λœλ‹€.

→ μ€€μ˜μ† μƒνƒœκ°€ 될 경우 λ³€κ²½ 감지와 지연 λ‘œλ”©μ΄ λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.
더보기

πŸ˜‰ λ³€κ²½ 감지가 μ•ˆλ˜λ©΄ μ–΄λ–€ 일이 λ²Œμ–΄μ§€λ‚˜μš”?

보톡 λ³€κ²½ 감지 κΈ°λŠ₯은 μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μˆ˜ν–‰ν•˜λ©΄μ„œ λ°œμƒν•˜κ³ 

λ‹¨μˆœνžˆ 데이터λ₯Ό λ³΄μ—¬μ£ΌλŠ” μš©λ„μΈ ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ 데이터λ₯Ό μˆ˜μ •ν•  일은 거의 μ—†κΈ° λ•Œλ¬Έμ— 큰 λ¬Έμ œκ°€ λ˜μ§€λŠ” μ•ŠλŠ”λ‹€.

 

πŸ₯Ή μ§€μ—° λ‘œλ”©μ€..?

μ€€μ˜μ† μƒνƒœμ—μ„œ 지연 λ‘œλ”©μ΄ λ™μž‘ν•˜μ§€ μ•ŠλŠ” 것은 λ¬Έμ œκ°€ λœλ‹€..

예λ₯Όλ“€μ–΄ ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ μ—°κ΄€λœ 엔티티도 ν•¨κ»˜ μ‚¬μš©ν•΄μ•Ό λ˜λŠ”λ° ν•΄λ‹Ή μ—”ν‹°ν‹°λ₯Ό 지연 λ‘œλ”©μœΌλ‘œ μ„€μ •ν–ˆμ„ 경우 LazyInitializationException μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.

 

 

@Entity
public class ReviewImage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String imageUrl;

    @ManyToOne(fetch = FetchType.LAZY)	// 지연 λ‘œλ”©
    private Review review;
    
}

μœ„μ™€ 같이 리뷰 이미지 μ—”ν‹°ν‹°μ—μ„œ 리뷰 μ—”ν‹°ν‹°λ₯Ό 지연 λ‘œλ”©μœΌλ‘œ μ„€μ •ν•  경우 아직 μ΄ˆκΈ°ν™”ν•˜μ§€ μ•Šμ€ ν”„λ‘μ‹œ 객체λ₯Ό μ‘°νšŒν•˜κΈ° λ•Œλ¬Έμ— μ»¨νŠΈλ‘€λŸ¬λ‚˜ 뷰의 지연 λ‘œλ”© μ‹œμ μ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.

class ReviewController {

	public String view(Long reviewId) {
   	    Review review = reviewService.findOne(reviewId);
   	    ReviewImage reviewImage = review.getReview();
   	    reviewImage.getImageUrl();	// 지연 λ‘œλ”© μ‹œ μ˜ˆμ™Έ λ°œμƒ
    }
}

 


μ€€μ˜μ† μƒνƒœμ—μ„œ 지연 λ‘œλ”© 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 방법

πŸ‘ λ·°κ°€ ν•„μš”ν•œ μ—”ν‹°ν‹° 미리 λ‘œλ”©ν•΄λ‘κΈ°

더보기

1️⃣ κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ μ¦‰μ‹œ λ‘œλ”©μœΌλ‘œ μˆ˜μ •

@Entity
public class ReviewImage {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String imageUrl;

    @ManyToOne(fetch = FetchType.EAGER)	// μ¦‰μ‹œ λ‘œλ”© μ „λž΅
    private Review review;
    
}

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ „μ²΄μ—μ„œ μ—”ν‹°ν‹°λ₯Ό λ‘œλ”©ν•  λ•Œλ§ˆλ‹€ ν•΄λ‹Ή μ—”ν‹°ν‹°μ˜ fetch νƒ€μž…μœΌλ‘œ μ§€μ •ν•œ μ „λž΅μ„ μ‚¬μš©ν•˜λ―€λ‘œ κΈ€λ‘œλ²Œ 페치 μ „λž΅μ΄λΌ ν•œλ‹€.

κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ FetchType.EAGER둜 μ„€μ •ν•˜κ³  ReviewImage μ—”ν‹°ν‹° 쑰회 μ‹œ μ—°κ΄€λœ review 엔티티도 항상 ν•¨κ»˜ λ‘œλ”©ν•œλ‹€. λ”°λΌμ„œ μ€€μ˜μ† μƒνƒœκ°€ λ˜μ–΄λ„ reviewλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

 

ν•˜μ§€λ§Œ μ΄λ ‡κ²Œ μ¦‰μ‹œ λ‘œλ”©μœΌλ‘œ κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ μ‚¬μš© μ‹œ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” μ—”ν‹°ν‹°λ₯Ό 항상 λ‘œλ”©ν•΄μ˜€λŠ” λ¬Έμ œμ™€ N+1 λ¬Έμ œκ°€ λ°œμƒν•œλ‹€.

🀧 N+1 λ¬Έμ œλž€?

select p.*, m.*
from Post p
left outer join Member m on p.MEMBER_ID=m.MEMBER_ID
where p.id=1

em.find둜 post μ—”ν‹°ν‹° 쑰회 μ‹œ μ¦‰μ‹œ λ‘œλ”©μœΌλ‘œ μ„€μ •ν•œ member μ—”ν‹°ν‹°λ₯Ό JOIN 쿼리둜 ν•¨κ»˜ μ‘°νšŒν•œλ‹€.

이λ₯Ό JPQL둜 쑰회 μ‹œ

List<Post> posts = em.createQuery("select p from Post p", Post.class)
	.getResultList();	// μ—°κ΄€λœ λͺ¨λ“  μ—”ν‹°ν‹° 쑰회

 μ‹€ν–‰λ˜λŠ” SQL은 μ•„λž˜μ™€ κ°™λ‹€.

select * from Post	# JPQL둜 μ‹€ν–‰λœ SQL
select * from Member where id=?	# EAGER둜 μ‹€ν–‰λœ SQL
select * from Member where id=?	# EAGER둜 μ‹€ν–‰λœ SQL
select * from Member where id=?	# EAGER둜 μ‹€ν–‰λœ SQL
select * from Member where id=?	# EAGER둜 μ‹€ν–‰λœ SQL

 JPAκ°€ JPQL을 뢄석해 SQL을 생성할 λ•ŒλŠ” κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ μ°Έκ³ ν•˜μ§€ μ•Šκ³  JPQL 자체만 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— μ¦‰μ‹œ λ‘œλ”©μ΄λ“  지연 λ‘œλ”©μ΄λ“  상관없이 SQL을 λ§Œλ“ λ‹€.
λ§Œμ•½ μ‘°νšŒν•œ post μ—”ν‹°ν‹°κ°€ 20개이면 memberλ₯Ό μ‘°νšŒν•˜λŠ” SQL도 20번 μ‹€ν–‰λ˜λŠ” 것이닀. 이λ₯Ό N+1 문제라 ν•œλ‹€.

 

 

 

κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ μ¦‰μ‹œ λ‘œλ”©μœΌλ‘œ μ„€μ •ν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 전체에 영ν–₯을 μ£Όλ―€λ‘œ λΉ„νš¨μœ¨μ μ΄λ‹€.

 

2️⃣ JPQL 페치 쑰인

페치 쑰인 μ‚¬μš© μ‹œ JPQL을 ν˜ΈμΆœν•˜λŠ” μ‹œμ μ— ν•¨κ»˜ λ‘œλ”©ν•  μ—”ν‹°ν‹°λ₯Ό 선택할 수 μžˆλ‹€.

페치 쑰인을 μ‚¬μš©ν•˜λ©΄ SQL JOIN을 μ‚¬μš©ν•΄ 페치 쑰인 λŒ€μƒκΉŒμ§€ ν•¨κ»˜ μ‘°νšŒν•˜κΈ° λ•Œλ¬Έμ— N+1 λ¬Έμ œκ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

# JPQL
SELECT p FROM Post p JOIN FETCH p.member

# SQL
SELECT p.*, m.*
FROM Post p
JOIN Member m on p.MEMBER_ID=m.MEMBER_ID

 

ν•˜μ§€λ§Œ 페치 쑰인을 λ¬΄λΆ„λ³„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ 화면에 맞좘 λ ˆν¬μ§€ν† λ¦¬ λ©”μ„œλ“œκ°€ 증가할 수 있기 λ•Œλ¬Έμ— μ μ ˆν•œ μ„ μ—μ„œ νƒ€ν˜‘μ μ„ μ°ΎλŠ” 것이 합리적이닀.

 

3️⃣ κ°•μ œ μ΄ˆκΈ°ν™”

κ°•μ œ μ΄ˆκΈ°ν™”λŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ‚΄μ•„μžˆμ„ λ•Œ ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측이 ν•„μš”ν•œ μ—”ν‹°ν‹°λ₯Ό κ°•μ œλ‘œ μ΄ˆκΈ°ν™”ν•΄μ„œ λ°˜ν™˜ν•˜λŠ” 방법이닀.

class ReviewService {

    @Transactional
    public Review findReview(id) {
    	Review review = reviewRepository.findReview(id);
        review.getReviewImage().getUrl();	// ν”„λ‘μ‹œ 객체 κ°•μ œ μ΄ˆκΈ°ν™”
        return review;
    }
}

κΈ€λ‘œλ²Œ 페치 μ „λž΅μ„ 지연 λ‘œλ”©μœΌλ‘œ μ„€μ • μ‹œ μ—°κ΄€λœ μ—”ν‹°ν‹°λŠ” μ‹€μ œ μ—”ν‹°ν‹°κ°€ μ•„λ‹Œ ν”„λ‘μ‹œ 객체둜 쑰회되며, ν”„λ‘μ‹œ κ°μ²΄λŠ” μ‹€μ œ μ‚¬μš©ν•˜λŠ” μ‹œμ μ— μ΄ˆκΈ°ν™”λœλ‹€.

λ”°λΌμ„œ μœ„μ²˜λŸΌ ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ ν•„μš”ν•œ ν”„λ‘μ‹œ 객체λ₯Ό μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ‚΄μ•„ μžˆμ„ λ•Œ κ°•μ œλ‘œ μ΄ˆκΈ°ν™”ν•΄ λ°˜ν™˜ν•˜λ©΄ 이미 μ΄ˆκΈ°ν™”ν–ˆκΈ° λ•Œλ¬Έμ— μ€€μ˜μ† μƒνƒœμ—μ„œ μ΄ˆκΈ°ν™”ν•  수 μžˆλ‹€.

 

ν•˜μ§€λ§Œ ν”„λ‘μ‹œλ₯Ό μ΄ˆκΈ°ν™”ν•˜λŠ” 역할을 μ„œλΉ„μŠ€ 계측이 λ‹΄λ‹Ήν•˜λ©΄ λ·°κ°€ ν•„μš”ν•œ 엔티티에 따라 μ„œλΉ„μŠ€ κ³„μΈ΅μ˜ λ‘œμ§μ„ λ³€κ²½ν•΄μ•Ό 되기 λ•Œλ¬Έμ— ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측이 μ„œλΉ„μŠ€ 계측을 μΉ¨λ²”ν•˜λŠ” 상황이 λ°œμƒν•œλ‹€.

λ”°λΌμ„œ μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측을 μœ„ν•œ ν”„λ‘μ‹œ μ΄ˆκΈ°ν™” 역할을 λΆ„λ¦¬ν•˜λŠ” FACADE 계측을 μΆ”κ°€ν•΄ μœ„ 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€.

 

λ·°λ₯Ό μœ„ν•œ ν”„λ‘μ‹œ μ΄ˆκΈ°ν™”λ₯Ό FACADE κ³„μΈ΅μ—μ„œ λ‹΄λ‹Ήν•΄ μ„œλΉ„μŠ€ 계측과 ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측 사이 논리적 μ˜μ‘΄μ„±μ„ 뢄리할 수 μžˆμ§€λ§Œ μ‹€μš©μ μΈ κ΄€μ μ—μ„œ λ³Ό λ•Œ 쀑간에 계측이 ν•˜λ‚˜ 더 끼어 κ²°κ΅­ 더 λ§Žμ€ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό λ˜λŠ” 단점이 μžˆλ‹€.

 

✌️ OSIVλ₯Ό μ‚¬μš©ν•΄ μ—”ν‹°ν‹°λ₯Ό 항상 μ˜μ† μƒνƒœλ‘œ μœ μ§€ν•˜κΈ°

μ•„κΉŒ νŠΈλžœμž­μ…˜μ΄ μ—†λŠ” ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측(Controller)μ—μ„œ μ‘°νšŒν•œ μ—”ν‹°ν‹°λŠ” μ€€μ˜μ† μƒνƒœκ°€ λœλ‹€λŠ” 말을 더 깊게 νŒŒκ³ λ“€μ–΄λ³΄μž

 

OSIVλŠ” μš”μ²­μ΄ μ²˜λ¦¬λ˜λŠ” λ™μ•ˆ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό 열어두고 νŠΈλžœμž­μ…˜ λ²”μœ„λ₯Ό λ„˜μ–΄μ„  λ ˆμ΄μ–΄κΉŒμ§€ μ—°κ΄€λœ μ—”ν‹°ν‹°λ₯Ό λ‘œλ”©ν•  수 μžˆλ„λ‘ ν•œλ‹€.

OSIVκ°€ trueAPI일 경우 ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ 응닡될 λ•ŒκΉŒμ§€, View일 경우 Viewκ°€ λ Œλ”λ§λ λ•ŒκΉŒμ§€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ‚΄μ•„μžˆλ‹€.

🌱 λΉ„μ¦ˆλ‹ˆμŠ€ κ³„μΈ΅μ—μ„œ νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•˜λŠ” OSIV의 λ™μž‘ 원리 🌱

1. ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ λ“€μ–΄μ˜€λ©΄ μ„œλΈ”λ¦Ώ ν•„ν„°λ‚˜, μŠ€ν”„λ§ μΈν„°μ…‰ν„°μ—μ„œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό 생성    단, νŠΈλžœμž­μ…˜μ€ μ‹œμž‘ν•˜μ§€ μ•ŠμŒ
2. μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ @Transactional둜 νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•  λ•Œ 1λ²ˆμ—μ„œ 미리 생성해둔 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό 찾아와 νŠΈλžœμž­μ…˜ μ‹œμž‘
3. μ„œλΉ„μŠ€ 계측이 λλ‚˜λ©΄ νŠΈλž˜μž­μ…˜ commit, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ flush
    μ΄λ•Œ νŠΈλžœμž­μ…˜μ€ λλ‚΄μ§€λ§Œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” μ’…λ£Œν•˜μ§€ μ•ŠμŒ
4. μ»¨νŠΈλ‘€λŸ¬μ™€ λ·°κΉŒμ§€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μœ μ§€λ˜λ―€λ‘œ μ‘°νšŒν•œ μ—”ν‹°ν‹°λŠ” μ˜μ† μƒνƒœ μœ μ§€μ€‘
5. μ„œλΈ”λ¦Ώ ν•„ν„°λ‚˜, μŠ€ν”„λ§ μΈν„°μ…‰ν„°λ‘œ μš”μ²­μ΄ λŒμ•„μ˜€λ©΄ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ’…λ£Œ.
     μ΄λ•Œ flushλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•Šκ³  λ°”λ‘œ μ’…λ£Œ
❄️ νŠΈλžœμž­μ…˜κ³Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ
- μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” νŠΈλžœμž­μ…˜ λ²”μœ„ μ•ˆμ—μ„œ μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•˜κ³  μˆ˜μ •ν•  수 μžˆλ‹€.
- μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” νŠΈλžœμž­μ…˜ λ²”μœ„ λ°–μ—μ„œ μ—”ν‹°ν‹°λ₯Ό 쑰회만 ν•  수 μžˆλ‹€. (νŠΈλžœμž­μ…˜ 없이 읽기: Nontransactional reads)

❄️ μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” OSIV νŠΉμ§•
- μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅κΉŒμ§€ μœ μ§€ν•œλ‹€.
- ν”„λ ˆμ  ν…Œμ΄μ…˜ 계측은 νŠΈλžœμž­μ…˜μ΄ μ—†κΈ° λ•Œλ¬Έμ— μ—”ν‹°ν‹°λ₯Ό μˆ˜μ •ν•  수 μ—†λ‹€.
- ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ νŠΈλžœμž­μ…˜ 없이 읽기λ₯Ό μ‚¬μš©ν•΄ 지연 λ‘œλ”©μ„ ν•  수 μžˆλ‹€.(ν”„λ‘μ‹œ 객체 μ΄ˆκΈ°ν™” κ°€λŠ₯)
     - μ—”ν‹°ν‹°κ°€ μ€€μ˜μ† μƒνƒœμ—¬μ„œ λ°œμƒν•˜λŠ” 문제 ν•΄κ²°
- OSIVκ°€ 켜져있으면 μš”μ²­ λ‹Ή μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” ν•œ 번 μƒμ„±λ˜κ³ , λ·° λ Œλ”λ§μ΄ 끝날 λ•ŒκΉŒμ§€ μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” μ’…λ£Œλ˜μ§€ μ•ŠμŒ
   λ˜ν•œ νŠΈλžœμž­μ…˜μ΄ λ‹€λ₯΄λ”라도 1μ°¨ μΊμ‹œ 곡유

 

λ§Œμ•½ νŠΈλžœμž­μ…˜ λ²”μœ„ 밖인 μ»¨νŠΈλ‘€λŸ¬μ™€ λ·°μ—μ„œ μ—”ν‹°ν‹°λ₯Ό μˆ˜μ •ν•΄λ„ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½ 감지에 μ˜ν•œ 데이터 μˆ˜μ •μ΄ λ‹€μŒ 두가지 이유둜 λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.

  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½ λ‚΄μš©μ„ DB에 λ°˜μ˜ν•˜λ €λ©΄ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό flush ν•΄μ•Ό ν•œλ‹€. μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” OSIVλŠ” μš”μ²­μ΄ λλ‚˜λ©΄ flushλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•Šκ³  em.close()둜 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ§Œ μ’…λ£Œμ‹œμΌœ 버린닀.
  • ν”„λ ˆμ  ν…Œμ΄μ…˜ κ³„μΈ΅μ—μ„œ em.flush()λ₯Ό ν˜ΈμΆœν•˜μ—¬ κ°•μ œλ‘œ flush 해도 νŠΈλžœμž­μ…˜ λ²”μœ„ λ°–μ΄λ―€λ‘œ 데이터λ₯Ό μˆ˜μ •ν•  수 μ—†λ‹€λŠ” μ˜ˆμ™Έκ°€ μΌμ–΄λ‚œλ‹€.   → javax.persistence.TransactionRequiredException

 

πŸ€” νŠΈλžœμž­μ…˜μ΄ λλ‚˜λ„ connection이 λ°˜λ‚©λ˜μ§€ μ•ŠλŠ”λ‹€κ³ ?

JPA의 open-in-view 섀정이 true일 경우 api의 μš”μ²­λΆ€ν„° μ‘λ‹΅κΉŒμ§€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μœ μ§€λœλ‹€.

μŠ€ν”„λ§ λΆ€νŠΈμ—μ„œλŠ” open-in-view μ†μ„±μ˜ default 값이 true이기 λ•Œλ¬Έμ— νŠΈλžœμž­μ…˜μ΄ λλ‚˜λ„ DB connection이 λ°˜λ‚©λ˜μ§€ μ•ŠλŠ” 것이닀.

 

🀩 OSIV μ‚¬μš©μ‹œ 주의점

 

섀정을 아무것도 κ±΄λ“œλ¦¬μ§€ μ•Šκ³  κΈ°λ³Έκ°’μœΌλ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ΅¬λ™ν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‹œμž‘ μ‹œ μœ„μ—μ„œ λ³΄μ΄λŠ” WARN λ‘œκ·Έκ°€ λœ¬λ‹€.

μœ„μ—μ„œ μ„€λͺ…ν–ˆλ“―이 ν”„λ‘μ‹œλ₯Ό μ΄ˆκΈ°ν™”ν•˜λŠ” μž‘μ—…μ„ Service κ³„μΈ΅μ—μ„œ 끝내지 μ•Šκ³  λ Œλ”λ§ μ‹œ μžλ™μœΌλ‘œ ν•΄κ²°λ˜λŠ” OSIV μ „λž΅μ— κ²½κ³ λ₯Ό μ£ΌλŠ” μ΄μœ κ°€ 뭘까?

 

OSIV μ „λž΅μ€ λ„ˆλ¬΄ μ˜€λžœμ‹œκ°„ DB connection μžμ›μ„ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ—, μ‹€μ‹œκ°„ νŠΈλž˜ν”½μ΄ μ€‘μš”ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλŠ” 컀λ„₯μ…˜μ΄ λͺ¨μžλž„ 수 있고 이것은 μž₯μ• λ‘œ μ΄μ–΄μ§€κ²Œ λœλ‹€.

예λ₯Ό λ“€μ–΄ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ™ΈλΆ€ API 호좜 μ‹œ μ™ΈλΆ€ API λŒ€κΈ° μ‹œκ°„λ§ŒνΌ 컀λ„₯μ…˜ μžμ›μ„ λ°˜ν™˜ν•˜μ§€ λͺ»ν•˜κ³ , μœ μ§€ν•΄μ•Ό ν•œλ‹€.

→ OISV의 치λͺ…적인 단점인, 컀λ„₯μ…˜μ„ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ’…λ£Œλ  λ•ŒκΉŒμ§€ 1:1둜 계속 λ¬Όκ³  μžˆλŠ” 것이닀.

πŸ€” open-in-view 섀정을 μˆ˜μ •ν•˜λ©΄ μ–΄λ–»κ²Œ λ˜λŠ”κ±°μ•Ό?

open-in-viewλ₯Ό false둜 μ„€μ •ν•  경우 νŠΈλžœμž­μ…˜μ΄ λλ‚˜κ³  DB connection이 λ°˜λ‚©λ˜μ§€ μ•Šλ˜ λ¬Έμ œλŠ” ν•΄κ²°λœλ‹€.

ν•˜μ§€λ§Œ lazy loading을 μ‚¬μš©ν•  경우 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€. μ§€κΈˆκΉŒμ§€ μž‘μ„±ν•œ λ§Žμ€ 지연 λ‘œλ”© μ½”λ“œλ₯Ό νŠΈλžœμž­μ…˜ μ•ˆμœΌλ‘œ λ„£μ–΄μ•Ό ν•˜λŠ” 것이닀.

 

EntityManagerκ°€ νŠΈλžœμž­μ…˜μ˜ 컀밋과 ν•¨κ»˜ λ‹«ν˜€ νŠΈλžœμž­μ…˜ μ™ΈλΆ€μ—μ„œ lazy loading을 μ‚¬μš©ν•˜λ©΄ no Session μ΄λΌλŠ” μ˜ˆμ™Έκ°€ λ°œμƒν•˜κ²Œ λœλ‹€.

μ΄λŠ” μš”μ²­λΆ€ν„° μ‘λ‹΅κΉŒμ§€λ₯Ό ν•˜λ‚˜μ˜ νŠΈλžœμž­μ…˜μœΌλ‘œ λ¬ΆλŠ” λ°©λ²•μœΌλ‘œ ν•΄κ²°ν•  수 μžˆμ§€λ§Œ μ΄λŠ” open-in-viewλ₯Ό true둜 μ„€μ •ν•˜λŠ” 것과 λΉ„μŠ·ν•˜λ‹€.

 

🀧 Command와 Query 뢄리

μ‹€λ¬΄μ—μ„œ OSIVλ₯Ό 끈 μƒνƒœλ‘œ λ³΅μž‘μ„±μ„ κ΄€λ¦¬ν•˜λŠ” 쒋은 방법은 Command와 Queryλ₯Ό λΆ„λ¦¬ν•˜λŠ” 것이라고 ν•œλ‹€.

 

λ³΄ν†΅μ˜ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ€ νŠΉμ • μ—”ν‹°ν‹° λͺ‡ 개λ₯Ό λ“±λ‘ν•˜κ±°λ‚˜ μˆ˜μ •ν•˜λŠ” 것이기 λ•Œλ¬Έμ— μ„±λŠ₯이 크게 λ¬Έμ œλ˜μ§€ μ•ŠλŠ”λ‹€.

κ·ΈλŸ¬λ‚˜ λ³΅μž‘ν•œ 화면을 좜λ ₯ν•˜κΈ° μœ„ν•œ μΏΌλ¦¬λŠ” 화면에 λ§žμΆ”μ–΄ μ„±λŠ₯을 μ΅œμ ν™” ν•˜λŠ” 것이 μ€‘μš”ν•˜λ‹€.

ν•˜μ§€λ§Œ 그런 λ™μž‘μ€ λ³΅μž‘μ„±μ— λΉ„ν•΄ 핡심 λΉ„μ¦ˆλ‹ˆμŠ€μ— 큰 영ν–₯을 μ£ΌλŠ” 것은 μ•„λ‹ˆλ‹€.

 

λ”°λΌμ„œ 크고 λ³΅μž‘ν•œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ κ°œλ°œν•œλ‹€λ©΄, 이 λ‘˜μ˜ 관심사λ₯Ό λͺ…ν™•ν•˜κ²Œ λΆ„λ¦¬ν•˜λŠ” 선택이 μœ μ§€λ³΄μˆ˜μ˜ κ΄€μ μ—μ„œ μ˜λ―Έκ°€ μžˆμ„ 것이닀.

ex) OrderService

  • OrderService: 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ 둜직
  • OrderQueryService: ν™”λ©΄μ΄λ‚˜ API에 맞좘 μ„œλΉ„μŠ€ (주둜 읽기 μ „μš© νŠΈλžœμž­μ…˜ μ‚¬μš©)

두 μ„œλΉ„μŠ€ λͺ¨λ‘ νŠΈλžœμž­μ…˜μ„ μœ μ§€ν•˜λ©΄μ„œ 지연 λ‘œλ”©μ„ μ‚¬μš©ν•  수 μžˆλ‹€.

 


μ•„λž˜ κΈ€μ—μ„œ μ—”ν‹°ν‹° λ§€λ‹ˆμ €κ°€ μƒμ„±λ˜λŠ” 과정을 μ—„μ²­ μžμ„Ένžˆ μ•Œ 수 μžˆλ‹€.

μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” λ¦¬ν€˜μŠ€νŠΈ λ‹Ή ν•˜λ‚˜λ§Œ μƒμ„±λ˜μ§€ μ•Šμ„ 수 μžˆλ‹€.

 

(JPA) μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” λ¦¬ν€˜μŠ€νŠΈ λ‹Ή ν•˜λ‚˜λ§Œ μƒμ„±λ˜μ§€ μ•Šμ„ 수 μžˆλ‹€.

3쀄 μš”μ•½ OSIVκ°€ 꺼져있으면 νŠΈλžœμž­μ…˜μ΄ μ‹œμž‘λ  λ•Œ μ—”ν‹°ν‹° λ§€λ‹ˆμ €κ°€ μƒμ„±λ˜κ³ , νŠΈλžœμž­μ…˜μ΄ 끝날 λ•Œ μ—”ν‹°ν‹° λ§€λ‹ˆμ €λ₯Ό μ’…λ£Œν•œλ‹€. OSIVκ°€ 꺼져있고, λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄λΌλ©΄ μ—”ν‹°ν‹° λ§€λ‹ˆμ €κ°€ κ³΅μœ λ˜μ§€

perfectacle.github.io

 

 

 

https://perfectacle.github.io/2021/05/24/entity-manager-lifecycle/

https://medium.com/frientrip/spring-boot%EC%9D%98-open-in-view-%EA%B7%B8-%EC%9C%84%ED%97%98%EC%84%B1%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-83483a03e5dc

https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94

https://ykh6242.tistory.com/entry/JPA-OSIVOpen-Session-In-View%EC%99%80-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94