유쨈미 2024. 7. 25. 16:22

1. 1차 캐시로 인한 동일 객체 인스턴스 반환 확인

이 코드의 목적은 동일한 트랜잭션 내에서 같은 엔티티를 조회할 때 JPA가 제공하는 1차 캐시 (Entity Manager Context)로 인해 동일한 객체 인스턴스를 반환하는지 확인하는 방법이다.

Member m1 = repo.findById(dto.getNum()).get(); 
Member m2 = repo.findById(dto.getNum()).get(); 
boolean isEqual = m1 == m2; 

System.out.println("m1과 m2가 같냐?" + isEqual);

 

📌 JPA의 1차 캐시

 

1️⃣ 1차 캐시

  1. JPA Entity Manager는 1차 캐시를 사용하여 동일한 트랙잭션 내에서 동일한 객체 인스턴스를 반환한다.
  2. 같은 엔티티를 여러 번 조회하더라도, 처음 조회된 엔티티 객체를 1차 캐시에 저장하고, 이후 동일한 엔티티를
    다시 조회할 때 1차 캐시에서 반환한다. 이를 통해 데이터베이스 접근을 최소화하고 성능을 최적화할 수 있다.

 

2️⃣ 트랜잭션 내에서의 동작

  1. 트랜잭션 내에서 repo.findById(dto.getNum()).get()을 호출하면, JPA는 먼저 1차 캐시에서 해당 엔티티를
    찾는다.
  2. 1차 캐시에 엔티티가 존재하면, 데이터베이스를 다시 조회하지 않고 캐시에 저장된 객체를 반환한다.
  3. 이로 인해 m1과 m2는 동일한 인스턴스를 가리키게 된다.

 

3️⃣ 동일성 확인

  1. m1 == m2는 두 객체가 동일한 인스턴스인지를 확인한다.
  2. 동일한 트랜잭션 내에서 같은 엔티티를 두 번 조회하면 동일한 객체 인스턴스를 반환하는지 확인할 수 있다.
  3. System.out.println("m1과 m2가 같냐?" + isEqual);는 이를 출력하여 실제로 동일한 객체인지를 확인한다.

 

💻 예제 코드

@Transactional
@Override
public void update2(MemberDto dto) {
    // 수정할 회원의 번호를 이용해서 회원 정보 entity 객체 얻어내기
    Member m1 = repo.findById(dto.getNum()).get();
    Member m2 = repo.findById(dto.getNum()).get();
    
    // 동일성 검사
    boolean isEqual = m1 == m2;
    System.out.println("m1과 m2가 같냐?" + isEqual);
    
    // setter 메소드를 이용해서 이름과 주소 수정하기
    m1.setName(dto.getName());
    m1.setAddr(dto.getAddr());
}

 

 

1️⃣ @Transactional 어노테이션

  1. 이 메소드가 트랜잭션 내에서 실행됨을 보장한다.
    트랜잭션이 시작되고 메소드 실행이 완료되면 트랜잭션이 커밋되거나 롤백된다.
  2. 동일한 트랜잭션 내에서 동일한 엔티티를 여러 번 조회해도 1차 캐시로 인해 같은 객체 인스턴스를 반환하게 된다.

 

2️⃣ 동일한 엔티티 두 번 조회

  1. Member m1 = repo.findById(dto.getNum()).get();
  2. Member m2 = repo.findById(dto.getNum()).get();
  3. 두 번 조회하지만, 동일한 트랜잭션 내에서 JPA 1차 캐시 덕분에 동일한 객체를 가리키게 된다.

 

3️⃣ 동일성 검사 및 출력

  1. boolean isEqual = m1 == m2;
  2. System.out.println("m1과 m2가 같냐?" + isEqual);
  3. 두 객체가 동일한 인스턴스인지 확인하고 이를 출력한다. 동일한 트랜잭션 내에서는 true가 출력된다.

 

4️⃣ 엔티티 수정

  1. m1.setName(dto.getName());
  2. m1.setAddr(dto.getAddr());
  3. 동일한 객체의 필드를 수정하면 JPA가 트랜잭션 종료 시점에 변경 사항을 감지하고
    데이터베이스에 자동으로 반영한다.

그래서 위 코드는 동일한 트랜잭션 내에서 동일한 엔티티를 조회할 때 JPA가 동일한 객체 인스턴스를 반환하는지를
확인하기 위한 것이다. JPA의 1차 캐시 덕분에 동일한 트랜잭션 내에서는 동일한 엔티티를 여러 번 조회하더라도
동일한 객체를 반환한다. 이를 통해 데이터베이스 접근을 최소화하고 성능을 최적화할 수 있다.

 

🚀 보충 설명

더보기
더보기

JPA의 영속성 컨텍스트는 1차 캐시를 제공하여 동일한 엔티티를 조회할 때 데이터베이스 접근 없이 캐시 된 객체를 반환한다. 이는 성능 향상과 트랜잭션 내 데이터 일관성을 보장한다.

 

💡 주요 특징

  1. 영속성 컨텍스트 내에서 엔티티 ID로 객체 관리
  2. 동일 트랜잭션 내에서 항상 같은 인스턴스 반환
  3. 변경 감지 (Dirty Checking) 기능 제공

 

메소드 설명
entityManager.find() 1차 캐시에서 엔티티 조회
entityManager.persist() 엔티티를 1차 캐시에 저장
entityManager.clear() 1차 캐시 초기화

 

 💻 예시 코드

@Test
@Transactional
public void testFirstLevelCache() {
    User user = entityManager.find(User.class, 1L);
    User sameUser = entityManager.find(User.class, 1L);
    
    assertThat(user).isSameAs(sameUser);
}
 

1차 캐시로 인한 동일 객체 인스턴스 반환 확인

* 동일한 트랜잭션 내에서 같은 엔티티를 조회할 때 JPA가 제공하는 1차 캐시(엔티티 매니저 컨텍스트)로 인해 동일한 객체 인스턴스를 반환하는지 확인하는 방법 Member m1 = repo.fi

cafe.daum.net

- 출처 : 데이터 과학자 + 프로그래머 세상 다음카페


2. @Transactional

@Transactional은 Spring Framework에서 제공하는 선언적 트랜잭션 관리를 위한 어노테이션이다.
메소드나 클래스에 적용하여 트랜잭션 경계를 설정한다.

 

💡 주요 속성

  1. propagation
    트랜잭션 전파 방식

  2. isolation
    트랜잭션 격리 수준

  3. readOnly
    읽기 전용 트랜잭션 여부

  4. rollbackFor
    특정 예외 발생 시 롤백

 

📌 속성과 설명

속성 설명
propagation REQUIRED, REQUIRES_NEW, SUPPORTS 등
isolation READ_COMMITTED, REPEATABLE_READ 등
timeout 트랜잭션 제한 시간
rollbackFor 롤백을 발생시킬 예외 클래스 지정

 

💻 예시 코드

@Service
public class UserService {
    @Transactional(readOnly = true)
    public User getUser(Long id) {
        return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException(id));
    }

    @Transactional(rollbackFor = Exception.class)
    public User createUser(User user) {
        return userRepository.save(user);
    }
}
 

[Spring] 📚 @Transactional 이해하기

🔥 개념부터 시작하는 스프링 트랜잭션 A to Z

velog.io

- 참고 : Better.log

 

@Transactional 어노테이션의 이해

나는 보통 서비스 코드에 @Transactional 어노테이션을 활용해준다. 그런데 사실 뜻도 잘 모르고 좋다고 그래서 쓴거라...지나고 보니 정확히 설명하기가 어려웠다. 그런고로, 해당 어노테이션의 작

kafcamus.tistory.com

- 참고 : kafcamus


3. JavaScript Inline

JavaScript Inline은 HTML 문서 내에 직접 JavaScript 코드를 작성하는 방식이다.
주로 간단한 스크립트나 이벤트 핸들러에 사용된다.

 

💡 장점

  1. 즉시 실행 가능
  2. 외부 파일 로드 없음

 

💡 단점

  1. 코드 재사용성 낮음
  2. 유지보수 어려움

 

📌 사용 방식과 설명

사용 방식 설명
<script> 태그 내 작성 HTML 문서 내 스크랩트 블록 생성
이벤트 속성에 직접 작성 HTML 요소의 이벤트에 직접 연결

 

💻 예시 코드

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Inline JavaScript Example</title>
</head>
<body>
    <button id="exeBTN" class="btn btn-primary">실행</button>

<script type="text/javascript">
document.querySelector("#exeBTN").addEventListener("click", () => {
	const sql = document.querySelector("#inputSQL").value;
	
    // URLSearchParams : 주소창의 경로를 다룰 수 있는 브라우저의 내장 객체
	// URL의 Query 문자열을 쉽게 구성하고 조작할 수 있는 Web API I 
	const queryString = new URLSearchParams({query:sql}).toString();
	
	fetch("[[@{/}]]jpql/test", {
		method:"POST",
		headers:{"Content-Type":"application/x-www-form-urlencoded;charset=utf-8"},
		body:queryString
	})
	.then(res => res.json()) // promise 객체 반환. JSON으로 변환
	.then(data => {
		console.log(data);
		
		const result = JSON.stringify(data, null, 3);
		
		document.querySelector("#result").innerText = result;
	});
});
</script>
</body>
</html>

 

 

타임리프 - 자바스크립트 인라인(th:inline)

<script th:inline="javascript">var username = \[\[${user.username}]];인라인 사용전 -> var username = userA;인라인 사용후 -> var username = "userA";인라인은 안에 쌍 따움

velog.io

- 참고 : slee2.log


4. URLSearchParams

URLSearchParams는 URL의 쿼리 문자열을 쉽게 다룰 수 있게 해주는 Web API이다.
URL의 매개변수를 조작하거나 새로운 URL을 생성할 때 유용하다.

 

📌 주요 메소드

메소드 설명
append() 매개변수 추가
get() 매개변수 값 조회
set() 매개변수 값 설정
delete() 매개변수 삭제
has() 매개변수 존재 여부 확인

 

💻 예시 코드

// URL 쿼리 문자열 파싱
let params = new URLSearchParams("?name=John&age=30");
console.log(params.get("name")); // "John"

// 새로운 URLSearchParams 객체 생성
let newParams = new URLSearchParams();
newParams.append("category", "books");
newParams.append("sort", "price");
console.log(newParams.toString()); // "category=books&sort=price"

// 현재 URL의 쿼리 문자열 수정
let currentUrl = new URL(window.location.href);
currentUrl.searchParams.set("page", "2");
history.pushState({}, "", currentUrl);
 

자바스크립트의 URLSearchParams로 쿼리 스트링 다루기

Engineering Blog by Dale Seo

www.daleseo.com

- 참고 : www.daleseo.com

 

URLSearchParams 객체에 대해 알아보자! (feat. URL 객체)

Effitizer 프로젝트의 기존 코드를 보다가 new URLSearchParams라는 생소한 표현을 발견했다. 어딘가에서 import해 온 흔적이 보이지 않아서 구글에 검색해보니 주소창의 경로를 다룰 수 있는 브라우저의

velog.io

- 참고 : SheryLog