728x90
반응형
SMALL

제네릭에 대해서 설명하고, 컬렉션 클래스에서 왜 제네릭을 사용하는 지 설명해주세요.

제네릭(generic)이란?

자바에서 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미한다.

클래스나 메서드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다. 이를 통해 타입 안전성을 보장하고, 타입 변환에 따른 불필요한 코드를 줄일 수 있다.

예를 들어, Java의 ArrayList 클래스는 객체를 저장할 때 어떤 타입의 객체를 저장할 것인지 미리 지정할 수 있다. 이런 방식으로 사용하면, 컴파일러는 이 리스트에 잘못된 타입의 객체가 추가되는 것을 방지하며, 객체를 꺼낼 때 타입 변환을 수행하지 않아도 된다.

ArrayList<String> list = new ArrayList<String>();
list.add("hello"); // 올바른 사용
list.add(new Integer(5)); // 컴파일 오류
String s = list.get(0); // 타입 변환 불필요

제네릭을 사용하는 주요 이유는 다음과 같습니다:

  1. 타입 안전성(Type Safety): 제네릭을 사용하면 컴파일 시점에 타입 체크를 수행할 수 있으므로, 실행 시점에 발생할 수 있는 타입 변환 오류를 방지할 수 있다.
  2. 코드 간결성(Boilerplate code reduction): 제네릭을 사용하면, 개발자가 명시적으로 타입 변환을 수행하는 코드를 작성할 필요가 없어진다. 이로 인해 코드가 더 간결하고 읽기 쉬워진다.

따라서 컬렉션 클래스에서 제네릭을 사용하면, 컴파일 시점의 타입 체크와 함께 코드의 간결성과 가독성을 향상시킬 수 있다.


제네릭의 장점

1. 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있다.

2. 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없다. 즉, 관리하기가 편하다.

3. 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.


제네릭의 사용방법

보통 제네릭은 아래 표의 타입들이 많이 쓰인다.

타입 설명

<T> Type
<E> Element
<K> Key
<V> Value
<N> Number

물론 반드시 한 글자일 필요는 없다. 또한 설명과 반드시 일치해야 할 필요도 없다. 예로들어 <Ele>라고 해도 전혀 무방하다. 다만 대중적으로 통하는 통상적인 선언이 가장 편하기 때문에 위와같은 암묵적(?)인 규칙이 있을 뿐이다.


제네릭의 선언 및 생성

자바에서 제네릭은 클래스와 메소드에만 다음과 같은 방법으로 선언할 수 있다.

class MyArray<T> {
	T element;
	void setElement(T element) { this.element = element; }
	T getElement() { return element; }
}

위의 예제에서 사용된 'T'를 타입 변수(type variable)라고 하며, 임의의 참조형 타입을 의미한다.

꼭 'T'뿐만 아니라 어떠한 문자를 사용해도 상관없으며, 여러 개의 타입 변수는 쉼표(,)로 구분하여 명시할 수 있다.

타입 변수는 클래스에서뿐만 아니라 메소드의 매개변수나 반환값으로도 사용할 수 있다.

위와 같이 선언된 제네릭 클래스(generic class)를 생성할 때에는 타입 변수 자리에 사용할 실제 타입을 명시해야 한다.


제한된 제네릭과 와일드 카드

<K extends T>	// T와 T의 자손 타입만 가능 (K는 들어오는 타입으로 지정 됨)
<K super T>	// T와 T의 부모(조상) 타입만 가능 (K는 들어오는 타입으로 지정 됨)
 
<? extends T>	// T와 T의 자손 타입만 가능
<? super T>	// T와 T의 부모(조상) 타입만 가능
<?>		// 모든 타입 가능. <? extends Object>랑 같은 의미

보통 이해하기 쉽게 다음과 같이 부른다.

extends T : 상한 경계

? super T : 하한 경계

<?> : 와일드 카드(Wild card)

이 때 주의해야 할 게 있다. K extends T와 ? extends T는 비슷한 구조지만 차이점이 있다.

'유형 경계를 지정'하는 것은 같으나 경계가 지정되고 K는 특정 타입으로 지정이 되지만, ?는 타입이 지정되지 않는다는 의미다.


1. <K extends T>, <? extends T>

이 것은 T 타입을 포함한 자식(자손) 타입만 가능하다는 의미다. 즉, 다음과 같은 경우들이 있다.

<T extends B>	// B와 C타입만 올 수 있음<T extends E>	// E타입만 올 수 있음<T extends A>
// A, B, C, D, E 타입이 올 수 있음<? extends B>	// B와 C타입만 올 수 있음<? extends E>	
// E타입만 올 수 있음<? extends A>	// A, B, C, D, E 타입이 올 수 있음

주석에 썼듯이 보면 알겠지만, 상한 한계. 즉 extends 뒤에 오는 타입이 최상위 타입으로 한계가 정해지는 것이다.

대표적인 예로는 제네릭 클래스에서 수를 표현하는 클래스만 받고 싶은 경우가 있다. 대표적인 Integer, Long, Byte, Double, Float, Short 같은 래퍼 클래스들은 Number 클래스를 상속 받는다.

즉,  Integer, Long, Byte, Double, Float, Short 같은 수를 표현하는 래퍼 클래스만으로 제한하고 싶은 경우 다음과 같이 쓸 수 있다.

public class ClassName <K extends Number> { ... }

이렇게 특정 타입 및 그 하위 타입만 제한 하고 싶을 경우 쓰면 된다. 좀 더 구체적으로 예로 들자면, 다음과 같다. Integer는 Number 클래스를 상속받는 클래스라 가능하지만, String은 Number클래스와는 완전 별개의 클래스이기 때문에 에러(Bound mismatch)를 띄운다.

public class ClassName <K extends Number> {	...}
	public class Main {	
		public static void main(String[] args) {
			ClassName<Double> a1 = new ClassName<Double>();	
// OK!		ClassName<String> a2 = new ClassName<String>();	// error!	
	}
}

2. <K super T>, <? super T>

이 것은 T 타입의 부모(조상) 타입만 가능하다는 의미다. 즉, 다음과 같은 경우들이 있다.

<K super B>	
// B와 A타입만 올 수 있음<K super E>	
// E, D, A타입만 올 수 있음<K super A>	
// A타입만 올 수 있음<? super B>	
// B와 A타입만 올 수 있음<? super E>	
// E, D, A타입만 올 수 있음<? super A>	
// A타입만 올 수 있음

주석에 썼듯이 보면 알겠지만, 하한 한계. 즉 super 뒤에 오는 타입이 최하위 타입으로 한계가 정해지는 것이다.

대표적으로는 해당 객체가 업캐스팅(Up Casting)이 될 필요가 있을 때 사용한다.

예로들어 '과일'이라는 클래스가 있고 이 클래스를 각각 상속받는 '사과'클래스와 '딸기'클래스가 있다고 가정해보자.

이 때 각각의 사과와 딸기는 종류가 다르지만, 둘 다 '과일'로 보고 자료를 조작해야 할 수도 있다. (예로들면 과일 목록을 뽑는다거나 등등..) 그럴 때 '사과'를 '과일'로 캐스팅 해야 하는데, 과일이 상위 타입이므로 업캐스팅을 해야한다. 이럴 때 쓸 수 있는 것이 바로 super라는 것이다.

조금 더 현실성 있는 예제라면 제네릭 타입에 대한 객체비교가 있다.

public class ClassName <E extends Comparable<? super E>> { ... }

특히 PriorityQueue(우선순위 큐), TreeSet, TreeMap 같이 값을 정렬하는 클래스 만약 여러분이 특정 제네릭에 대한 자기 참조 비교를 하고싶을 경우 대부분 공통적으로 위와 같은 형식을 취한다.

extends는 앞서 말했듯 extends 뒤에오는 타입이 최상위 타입이 되고, 해당 타입과 그에 대한 하위 타입이라고 했다. 그럼 역으로 생각해보면 이렇다. E 객체는 반드시 Comparable을 구현해야한다는 의미 

public class SaltClass <E extends Comparable<E>> { ... }
	public class Student implements Comparable<Student> {	
		@Override	
		public int compareTo(Person o) { ... };}

public class Main {	
	public static void main(String[] args) {		
		SaltClass<Student> a = new SaltClass<Student>();	}}

이렇게만 쓴다면 E extends Comparable<E> 까지만 써도 무방하다.

즉, SaltClass의 E 는 Student 이 되어야 하는데, Comparable<Student> 의 하위 타입이어야 하므로 거꾸로 말해 Comparable을 구현해야한다는 의미인 것이다.

그러면 왜 Comparable<E> 가 아닌 <? super E> 일까?

잠깐 설명했지만, super E는 E를 포함한 상위 타입 객체들이 올 수 있다고 했다.

만약에 위의 예제에서 학생보다 더 큰 범주의 클래스인 사람(Person)클래스를 둔다면 어떻게 될까? 한마디로 아래와 같다면?

public class SaltClass <E extends Comparable<E>> { ... }	
// Error가능성 있음
public class SaltClass <E extends Comparable<? super E>> { ... }	
// 안전성이 높음
public class Person {...}
public class Student extends Person implements Comparable<Person> {	
@Override	
public int compareTo(Person o) { ... };}
public class Main {	
public static void main(String[] args) {		
SaltClass<Student> a = new SaltClass<Student>();	}}

쉽게 말하면 Person을 상속받고 Comparable 구현부인 comparTo에서 Person 타입으로 업캐스팅(Up-Casting) 한다면 어떻게 될까?

만약 <E extends Comparable<E>>라면 SaltClass<Student> a 객체가 타입 파라미터로 Student를 주지만, Comparable에서는 그보다 상위 타입인 Person으로 비교하기 때문에 Comparable<E>의 E인 Student보다 상위 타입 객체이기 때문에 제대로 정렬이 안되거나 에러가 날 수 있다.

그렇기 때문에 E 객체의 상위 타입, 즉 <? super E> 을 해줌으로써 위와같은 불상사를 방지할 수가 있는 것이다.

즉, <E extends Comparable<? super E>> 는 쉽게 말하자면 E 타입 또는 E 타입의 슈퍼클래스가 Comparable을 의무적으로 구현해야한다는 뜻으로 슈퍼클래스타입으로 Up Casting이 발생하더라도 안정성을 보장받을 수 있다.

이 부분은 중요한 것이 이후 필자가 PriorityQueue와 TreeSet 자료구조를 구현할 것인데, 이 부분을 이해하고 있어야 가능하기 때문에 조금은 어렵더라도 미리 언급하고 가려한다.

<E extends Comparable<? super E>> 에 대해 설명이 조금 길었다. 이 긴 내용을 한 마디로 정의하자면 이렇다.

"E 자기 자신 및 조상 타입과 비교할 수 있는 E"

3. <?> (와일드 카드 : Wild Card)

마지막으로 와일드 카드다.

이 와일드 카드 <?> 은 <? extends Object> 와 마찬가지라고 했다. Object는 자바에서의 모든 API 및 사용자 클래스의 최상위 타입이다. 한마디로 다음과 같은 의미나 마찬가지다.

public class ClassName { ... }public class ClassName extends Object { ... }

public class ClassName extends Object {} 를 묵시적으로 상속받는 것이나 다름이 없다.

한마디로 <?>은 무엇이냐. 어떤 타입이든 상관 없다는 의미다. 당신이 String을 받던 어떤 타입을 리턴 받던 알빠 아니라는 조금 과격한 얘기..

이는 보통 데이터가 아닌 '기능'의 사용에만 관심이 있는 경우에 <?>로 사용할 수 있다.


제네릭의 제거 시기

자바 코드에서 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입 변환된다.

그리고서 코드 내의 모든 제네릭 타입은 제거되어, 컴파일된 class 파일에는 어떠한 제네릭 타입도 포함되지 않게 된다.

이런 식으로 동작하는 이유는 제네릭을 사용하지 않는 코드와의 호환성을 유지하기 위해서이다.

728x90
반응형
LIST
728x90
반응형
SMALL

List, Set, Map, HashMap의 차이에 대해서 설명해주세요.

  1. List : 순서가 있는 컬렉션을 정의
  • 사용자가 요소의 취치를 정확하게 제어 가능
  • 요소의 중복을 허용하며, 인덱스를 기반으로 요소에 접근(ArrayList, LinkedList 등이 이에 속함 등)
  • 저장공간이 필요에 의해 자동으로 늘어난다.(순서가 있는 저장공간)
    • 특징 : 순서가 있고, 중복을 허용(배열과 유사)
    • 장점 : 가변적인 배열 (배열이 자동으로 늘어남)
    • 단점 : 원하는 데이터가 뒤쪽에 위치하는 경우 속도의 문제
    • 방식 : equals()를 이용한 데이터 검색

자바에서 List자료 구조는 크게 vector, arrayList, LinkedList로 나눠진다.

1) ArrayList : 객체 내부에 있는 배열에 데이터 저장

  • 가변적인 배열로, 배열의 크기를 동적으로 변경할 수 있음
  • 일반적인 배열과 달리 ArrayList는 크기를 미리 지정할 필요가 없으며, 요소를 추가하거나 제거함에 따라 자동으로 크기가 조절됨
  • 상당히 빠르고 크기를 맘대로 조절할 수 있는 배열
  • 단방향 포인터 구조로 자료에 대한 순차적인 접근에 강점

ArrayList 특징

  • 동적 크기 : 요소가 추가되거나 제거될 때마다 자동으로 크기가 조정되는 동적배열
  • 인덱스 접근 : 인덱스 기반의 자료구조로, 특정 위치의 요소에 빠르게 접근가능
  • 순서 유지 : 요소를 추가한 순서대로 요소를 저장함, 요소의 순서를 유지
  • 중복 허용 : 동일한 요소를 여러 번 포함할 수 있음
  • Null 요소 허용 : null 요소를 허용함
  • 데이터를 순서대로 저장하고 빠르게 접근해야 하는 경우 매우 유용함 중간에 요소를 삽입하거나 삭제하는 연산은 비효율적이므로, 이런 경우 LinkedList 같은 다른 자료구조를 고려하는 것이 좋음

2) Vector : ArrayList와 동일하게 사용이 가능

  • ArrayList의 구형버전이며, 모든 메소드가 동기회 됨
  • 잘 쓰이진 않음

Vector 특징

  • 동적 크기 : 요소가 추가되거나 제거될 때마다 자동으로 조정되는 동적 배열
  • 인덱스 접근 : 특정 위치의 요소에 빠르게 접근할 수 있음
  • 순서 유지 : 요소를 추가한 순서ㅐ로 요소를 저장하므로, 요소의 순서를 유지
  • 중복 허용 : 동일한 요소를 여러 번 포함할 수 있음
  • Null 요소 허용 : null 요소를 허용함
  • 스레드 안전 : 모든 메서드는 동기화되어 있어 여러 스레드가 동시에 Vector를 수정하는 경우에도 데이터의 일관성이 유지함
  • 여러 스레드가 동시에 데이터를 수정해야 하는 병렬 처리 상황에서 매우 유용함 하지만, 동기화는 성능을 저하시킬 수 있으므로, 단일 스레드 환경에서는 ArrayList를 사용하는 것이 더 효율적일 수 있음

Arraylist와 Vector의 차이점

  • 한 데이터에 동시접속이 발생했을 때 처리가 가능한 기능이 있나 없나의 차이점
  • 동시 접속을 고려하여 만들어진 리스트가 Vector이다
  • Arraylist는 동시접속을 고려안하고 가볍다는 장점이 있다.

3) Linkedlist

  • 양방향 포인터 구조로 데이터의 삽입, 삭제가 빈번할 경우 빠른 성능 보장
  • 스택, 큐, 양방향 큐 등을 만들기 위한 용도로 쓰임
  • lterator 사용
  • 노드가 다음 노드와 이전 노드를 참조하는 방식으로 데이터 저장
  • 각 요소가 이전 요소와 다음 요소의 정보를 가지고 있는 것이 “연결 리스트”의 특정

Linkedlist 특징

  • 동적 크기 : 요소가 추가되거나 제거될 대마다 자동으로 크기 조정됨
  • 요소의 삽입 및 삭제 효율성 : 중간에 요소를 삽입하거나 삭제하는 것이 매우 효율적, 노드 간의 연결만 변경하면 되기 때문
  • 순서 유지 : 요소를 추가한 순서대로 요소를 저장하므로, 요소의 순서를 유지함
  • 중복 허용 : 동일한 요소를 여러 번 포함함
  • Null 요소 허용 : null 요소를 허용함Iterator 추출 전용 인터페이스
    • 데이터를 추출하기 위한 데이터 임시 저장공간
    • 주로 순서가 없는 자료구조의 값들을 추출할 때 사용
    • 보통 hasNext와 next 메소드를 이용한 while문으로 값을 추출한다.
  • LinkedList는 인덱스를 이용한 빠른 접근이 상대적으로 느림 특정 위치의 요소를 찾기 위해서는 처음 또는 끝에서부터 해당 위치까지 순차적으로 이동해야하기 때문 LinkedList는 자주 요소의 삽입이나 삭제가 발생하고, 인덱스를 이용한 접근이 빈번하지 않은 상황에서 효율적으로 사용할 수 있음

linked 특성상 항상 처음부터 같은 경로를 반복적으로 지나면서 데이터의 위치를 검색해야하기 때문에 마지막으로 접근한 데이터를

기준으로 그 다음 데이터를 알아내는 것이 더 쉽다


2. Set : 터페이스는 중복을 허용하지 않는 컬렉션을 정의

  • 중복되지 않는 숫자(데이터)를 구할 때 사용하면 유용
  • 같은 요소가 한 번 이상 Set에 포함될 수 없음
  • 대부분의 Set 구현체는 요소의 순서를 보장하지 않으며, 이들 요소에는 인덱스가 없음
  • HashSet, TreeSet 등이 이에 속함
    • 특징 : 순서가 없고, 중복을 허용하지 않음
    • 장점 : 빠른 속도
    • 단점 : 단순 집합의 개념으로 정렬하려면 별도의 처리가 필요함

hashSet의 key값은 hashcode 비교에 의해 중복여부가 확인된다.

hashCode()를 가지고 비교하고 ==로 비교해서 true를 리턴하거나 equals()로 비교해서 true를 리턴하는지 체크

element를 덮어 쓸 것인지 결정하면 된다.

Hashset은 Iterator를 사용한다

왜냐하면, set은 순서가 없기 때문에 데이터에 순서를 정해 추출해야한다.


3. Map : 키와 값 쌍의 컬렉션을 정의

  • 키의 중복을 허용하지 않지만 다른 키에 대해 동일한 값을 가질 수 있음
  • Map 인터페이스를 사용하면 키를 이용하여 값을 검색할 수 있음
  • HashMap, TreeMap, LinkedHashMap 등이 이에 속함
    • 특징 : Key(키)랑 Value(값)으로 나눠서 데이터 관리, 순서는 없으며, 키에 대한 중복은 없음
    • 장점 : 빠른 속도
    • 단점 : Key의 검색 속도가 검색 속도를 좌우함

4. HashMap : Map 인터페이스의 한 구현체로서, 객체들을 해시 함수를 통해 해시값으로 변환하고 이를 인덱스로 사용하여 데이터를 저장하고 검색하는 자료구조

  • 해시 충돌(hash collision)이 발생할 경우, LinkedList 혹은 TreeNode 형태로 체이닝되어 저장됨
  • HashMap은 순서를 보장하지 않음
  • index번호 대신 키값으로 값을 찾는 맵형태의 자료구조도 iterator 클래스를 이용해서 키값을 순서대로 iterator에 저장해두면 순서대로 데이터 추출이 가능하다.

 


 

728x90
반응형
LIST
728x90
반응형
SMALL

JPA의 영속성 컨텍스트에 대해서 설명해 주세요

영속성 컨텍스트란?

엔티티를 영구 저장하는 환경 애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 메니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.

영속성 컨텍스트의 특징

  • 엔티티 매니저를 생성할 때 하나 만들어진다.
  • 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하고 관리할 수 있다.

엔티티의 생명주기

  • 비영속성 (new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
    Member member = new Member();
    
    엔티티 객체를 생성했지만 아직 영속성 컨텍스트에 저장하지 않는 상태

 

  • 영속(managed) : 영속성 컨텍스트에 저장된 상태 엔티티 매니저를 통해서 엔티티를 영속성 컨텍스트에 저장한 상태를 말하며 영속성 컨텍스트에 의해 관리
em.persist(member);

 

  • 준영속(derached) : 영속성 컨텍스트에 저장되었다가 분리된 상태

영속성 컨텍스트가 관리하던 영속 상태의 엔티티 더이상 관리하지 않으면 준영속 상태가 됨 엔티티를 준영속 상태로 만들려면 em.datach()를 호출하면 된다.

// 엔티티를 영속성 컨텍스트에 분리해 준영속 상태로 만든다.
em.detach(member);
// 영속성 컨텍스트를 비워도 관리되던 준영속 상태가 된다.
em.claer();
// 영속성 컨텍스트를 종료해도 관리되던 엔티티는 준영속 상태가 된다.
em.close();
준영속 상태의 특징

- 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩을 포함한 영속성 컨텍스트가 제공하는 어떠한 기능도 동작하지 않는다.
- 식별자 값을 가지고 있다.

 

  • 삭제(removed) : 삭제된 상태
    엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제
em.remove(member);


영속성 컨텍스트의 특징

영속성 컨텍스트의 식별자 값 영속성 컨텍스트는 엔티티를 식별자 값으로 구분한다. 따라서 영속 상태는 식별자 값이 반드시 있어야 한다.

영속성 컨텍스트와 데이터베이스 저장 JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터베이스에 반영하는데 이를 flush라 한다.

영속성 컨텍스트가 엔티티를 관리하면 다음과 같은 장점이 있다.

  1. 1차 캐시
  2. 동일성 보장
  3. 트랜잭션을 지원하는 쓰기 지연
  4. 변경 감지
  5. 지연 로딩

1차 캐시

영속성 컨텍스트 내부에는 캐시가 있는데 이를 1차 캐시라고 한다. 영속 상태의 엔티티를 이곳에 저장한다. 1차 캐시의 키는 식별자 값(데이터베이스의 기본 키)이고 값은 엔티티 인스턴스이다.

JPA의 EntityManager 인스턴스인 em을 사용하여 식별자 값이 "member1"인 Member 엔티티를 데이터베이스에서 조회하는 코드

// em.find(엔티티 클래스 타입, 식별자 값);
Member member = em.find(Member.class, "member1");

조회의 흐름

  • 1차 캐시에서 엔티티를 찾는다.
  • 있으면 메모리에 있는 1차 캐시에서 엔티티를 조회한다.
  • 없을 경우 데이터베이스에서 조회한다.
  • 조회한 엔티티를 생성해 1차 캐시에 저장한다. (엔티티를 영속상태로 만든다)
  • 조회한 엔티티를 반환한다.

영속엔티티의 동일성 보장

영속성 컨텍스트는 엔티티의 동일성을 보장한다.

1차캐시를 통해 동일성을 보장한다. 완전히 같은 엔티티를 반환해준다.

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
Ststem.out.print(a==b) // true

동일성 비교 : 실제 인스턴스가 같다. ==을 사용해 비교한다.

동등성 비교 : 실제 인스턴스는 다를 수 있지만 인스턴스가 가지고 있는 값이 같다. equals() 메소드를 구현해서 비교한다.


트랜잭션을 지원하는 쓰기 지연 ( 데이터베이스 접근을 최적화)

em.find(member)를 사용해 member를 저장해도 바로 INSERT SQL이 DB에 보내지는 것이 아니다. 엔티티 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 INSERT SQL을 모아둔다. 그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 보낸다. 이것을 트랜잭션을 지원하는 쓰기 지연이라 한다.

영속 상태의 엔티티에 대한 변경 사항을 즉시 데이터베이스에 반영하지 않고, 대신 내부적으로 SQL을 저장해두는 것입니다. 그 후, 트랜잭션이 커밋되는 시점에 이 모아둔 SQL을 한 번에 데이터베이스에 전송한다. 이 방식을 통해 데이터베이스 접근 횟수를 최소화하며 성능을 향상시킬 수 있다.

예시로, **em.persist(member)**를 사용해 엔티티를 영속 상태로 만들면, 이 메서드는 즉각적으로 데이터베이스에 INSERT SQL을 보내지 않는다. 대신, SQL은 내부에 저장되고, 트랜잭션 커밋 시에 데이터베이스로 전송된다.

즉, JPA의 이런 특성을 이해하고 활용하면, 애플리케이션의 데이터베이스 접근을 보다 효율적으로 관리할 수 있다.


변경 감지

JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해서 데이터를 변경하면 된다. JPA는 엔티티의 변경을 감지하여 수정된 엔티티에 대한 UPDATE SQL을 자동으로 생성하고, 트랜잭션 커밋 시점에 데이터베이스에 반영한다. 이를 더티 체킹이라고 한다.

변경감지의 흐름

  • 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 프러시가 호출된다.
  • 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾는다.
  • 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰지 지연 SQL 저장소에 저장한다.
  • 쓰기 지연 저장소의 SQL을 플러시한다.
  • 데이터베이스 트랜잭션을 커밋한다.

변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티만 적용된다.

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();

Memeber member = em.find(Member.class, "member1");
member.setName("HA");

transaction.commit();

지연 로딩 ( 필요한 데이터만 로딩하는 성능 최적화 전략)

JPA는 연관된 엔티티를 즉시 로딩하는 것이 아니라, 실제로 사용하는 시점에 데이터베이스에서 로딩하는 지연로딩을 지원한다.

연관된 엔티티의 로딩을 실제로 사용하는 시점까지 늦추는 방법입니다. 이는 성능 최적화의 한 방법으로, 필요한 데이터만 로딩하고 불필요한 데이터의 로딩을 방지함으로써 성능을 향상시킬 수 있다.

예를 들어, 특정 엔티티와 연관된 다른 엔티티들이 있을 때, 이 연관된 엔티티들을 모두 즉시 로딩하면 데이터베이스에 부담을 주고 애플리케이션의 성능을 저하시킬 수 있다. 그러나 지연 로딩을 사용하면, 이 연관된 엔티티들을 실제로 필요로 하는 시점에만 로딩하므로 불필요한 데이터베이스 접근을 줄일 수 있다.

JPA에서는 @ManyToOne, @OneToMany 등의 관계 매핑 어노테이션에 fetch 속성을 **FetchType.LAZY**로 설정하여 지연 로딩을 구현할 수 있다. 이렇게 하면 해당 연관 엔티티는 필요할 때까지 로딩이 지연되며, 실제로 엔티티에 접근하는 시점에 데이터베이스에서 로딩된다.


플러시 (변경 사항을 데이터베이스에 반영하는 과정)

영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 과정이다. 영속성 컨텍스트의 엔티티를 지우는게 아니라 변경 내용을 데이터베이스에 동기화하는 것이다.

영속성 컨텍스트에 있는 변경 내용을 데이터베이스에 반영하는 과정이다. 플러시는 주로 트랜잭션을 커밋하는 시점에 자동으로 발생하며, 이 때 영속성 컨텍스트에 저장된 쿼리(INSERT, UPDATE, DELETE 등)가 데이터베이스에 전송된다. 또한, 필요에 따라 개발자가 직접 em.flush() 메서드를 호출하여 플러시를 수행할 수도 있다.

플러시의 흐름

  • 변경 감지가 동작해서 스냅샷과 비교해서 수정된 엔티티를 찾는다.
  • 수정된 엔티티에 대해서 수정 쿼리를 만들거나 SQL 저장소에 등록한다.
  • 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.

플러시하는 방법

  • em.flush()
  • 트랜잭션 커밋시 자동 호출
  • JPQL 쿼리 실행시 자동 호출

 

728x90
반응형
LIST

+ Recent posts