Spring Boot ํ๊ฒฝ์์์ JPA
Spring Boot ํ๊ฒฝ์์๋ EntityManagerFactory๋ EntityManager๋ฅผ ์๋์ผ๋ก ์์ฑํด์ค
- application.properties์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ จ ์ ๋ณด๋ฅผ ์ฃผ๋ฉด, ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์๋์ผ๋ก EntityManagerFactory ๋ง๋ค์ด์ค
- Spring Boot์์ ์๋์ผ๋ก ์์ฑํด์ฃผ๋ EntityManager๋ฅผ ์ฃผ์ ๋ฐ์ ์ค๊ณ ์ถ์ ๋๋ @PersistenceContext ์ด๋ ธํ ์ด์ ์ฌ์ฉ
๋ฉ๋ชจ์ฅ ํ๋ก์ ํธ์ JPA ์ ์ฉํ๊ธฐ
1. build.gradle ํ์ผ ๋ด JPA ์ค์ ์ถ๊ฐ
// JPA ์ค์
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
- spring-book-starter-data-jpa ์ค์ ํ๋ฉด ์๋์ผ๋ก Hibernate Core๋ฅผ ๋น๊ฒจ์ด
dependencies {
// JPA ์ค์
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'
// jdbc ์ญ์
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-thymeleaf-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testCompileOnly 'org.projectlombok:lombok'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testAnnotationProcessor 'org.projectlombok:lombok'
}
2. application.properties ๋ด Hibernate ์ค์ ์ถ๊ฐ
# ddl-auto ์ต์
# 1. create: ๊ธฐ์กด ํ
์ด๋ธ ์ญ์ ํ ๋ค์ ์์ฑ(drop+create)
# 2. create-drop: create์ ๋น์ทํ์ง๋ง, ์ข
๋ฃ ์์ ์ table drop
# 3. update: ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ๋ฐ์(ํ
์ด๋ธ์ด ์์ผ๋ฉด ์์ฑ X, ์์ผ๋ฉด ์์ฑ)
# 4. validate: ์ํฐํฐ์ ํ
์ด๋ธ์ด ์ ์์ ์ผ๋ก ๋งคํ๋์ด์๋์ง๋ง ํ์ธ
# 5. none: ์๋ฌด๊ฒ๋ ํ์ง ์์
spring.jpa.hibernate.ddl-auto=update
# show_sql, format_sql, use_sql_comments๋
# Hibernate๊ฐ ์๋์ผ๋ก ๋ง๋ค์ด์ฃผ๋ SQL์ ๋ณด๊ธฐ ์ข๊ฒ ๋ง๋ค์ด์ค
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
3. Memo Entity ๋ง๋ค๊ธฐ
package com.sparta.memo.entity;
import com.sparta.memo.dto.MemoRequestDto;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity // JPA๊ฐ ๊ด๋ฆฌํ ์ ์๋ Entity ํด๋์ค ์ง์
@Getter
@Setter
@Table(name = "memo") // ๋งคํํ ํ
์ด๋ธ์ ์ด๋ฆ์ ์ง์
@NoArgsConstructor
public class Memo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false)
private String username;
@Column(name = "contents", nullable = false, length = 500)
private String contents;
public Memo(MemoRequestDto requestDto) {
this.username = requestDto.getUsername();
this.contents = requestDto.getContents();
}
public void update(MemoRequestDto requestDto) {
this.username = requestDto.getUsername();
this.contents = requestDto.getContents();
}
}
Spring Boot ํธ๋์ญ์
- Java ํ๊ฒฝ์์ JPA๋ฅผ ์ฌ์ฉํ ๋๋ EntityManager์์ Entity ํธ๋์ญ์ ์ ๊ฐ์ ธ์์ ํธ๋์ญ์ ํ๊ฒฝ์ ๋ง๋ค์์
- Spring Boot ํ๊ฒฝ์์๋ ์์ฝ๊ฒ ํธ๋์ญ์ ํ๊ฒฝ์ ๋ง๋๋ ๋ฐฉ๋ฒ ์ ๊ณตํจ → @Transactional ์ด๋ ธํ ์ด์ ์ด์ฉ
- ํด๋์ค ์๋จ์ @Transactional์ ๊ฑธ๋๊ฐ, ์๋๋ฉด method ์์ฒด์ ๊ฑธ์ด์ ํธ๋์ญ์ ํ๊ฒฝ์ด ์ค์ ๋จ
- method ์์ฒด์ ํธ๋์ญ์ ์ ๊ฑธ๋ฉด, ํด๋น method ๋ด์์ ์ํ๋๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ์ฐ ๋ด์ฉ๋ค์ ํ๋์ ํธ๋์ญ์ ์ผ๋ก ๋ฌถ์
- ์ด๋, ํด๋น method์์ ์ฝ๋๊ฐ ์ ๋ถ ์ ์์ ์ผ๋ก ์ํ์ด ๋๋ฉด ๋ง์ง๋ง์ ๋๋ ๋ Commitํ๊ณ , ์๋๋ฉด ์์ธ๊ฐ ๋ฐ์ํจ
- ์ผ์ชฝ ์ด๋ฏธ์ง์์ @Transactional(readOnly=true)๋ ํด๋์ค ๋ด๋ถ์ ์๋ ๋ชจ๋ method์ ํด๋น ํธ๋์ญ์
์ readOnly์ด ๋ถ์ฌ๋จ
- readOnly ์ต์ : ํธ๋์ญ์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ๋ง ํ ๋ ์ฌ์ฉ๋จ
- ์ฝ๊ธฐ ์์ ์ ๋ํ ์ต์ ํ ์ํ ๊ฐ๋ฅ
- ๋ง์ฝ readOnly = true ๊ฑธ์ด๋์ ์ํ์์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ค๊ณ ํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํจ
- save ๋ฉ์๋์ฒ๋ผ @Transactional์ด ๋ค์ ๊ฑธ๋ ค์๋ ๊ฒฝ์ฐ, readOnly = false ์ต์ ์ผ๋ก ์ ์ฉ๋จ


ํธ๋์ญ์ ํ ์คํธ
1) ๋ฉ๋ชจ ์์ฑ ์ฑ๊ณต ํ ์คํธ
package com.sparta.memo;
import com.sparta.memo.entity.Memo;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
public class TransactionTest {
@PersistenceContext
EntityManager em;
@Test
@Transactional
@Rollback(value = false) // ํ
์คํธ ์ฝ๋์์ @Transactional์ ์ฌ์ฉํ๋ฉด ํ
์คํธ๊ฐ ์๋ฃ๋ ํ ๋กค๋ฐฑํ๊ธฐ ๋๋ฌธ์ false ์ต์
์ถ๊ฐ
@DisplayName("๋ฉ๋ชจ ์์ฑ ์ฑ๊ณต")
void test1() {
Memo memo = new Memo();
memo.setUsername("Robbert");
memo.setContents("@Transactional ํ
์คํธ ์ค!");
em.persist(memo); // ์์์ฑ ์ปจํ
์คํธ์ ๋ฉ๋ชจ Entity ๊ฐ์ฒด ์ ์ฅ
}
}


2) ๋ฉ๋ชจ ์์ฑ ์คํจ ํ ์คํธ
@Test
@DisplayName("๋ฉ๋ชจ ์์ฑ ์คํจ")
void test2() {
Memo memo = new Memo();
memo.setUsername("Robbie");
memo.setContents("@Transactional ํ
์คํธ ์ค!");
em.persist(memo); // ์์์ฑ ์ปจํ
์คํธ์ ๋ฉ๋ชจ Entity ๊ฐ์ฒด๋ฅผ ์ ์ฅํฉ๋๋ค.
}

- Transaction์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ EntityManager ์ฌ์ฉํ ์ ์๋ค๋ ์๋ฌ ๋ฐ์ → Transaction์ด ์ ์ฉ๋์ด ์์ง ์๋ค๋ ์๋ฌ
- Insert, Update, Delete์ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ์์ ํ ๋๋ ํธ๋์ญ์ ํ๊ฒฝ์ด ๋ฐ๋์ ํ์ํจ!
- @Disabled ์ถ๊ฐํ๋ฉด ์ค๋ฅ๊ฐ ๋๋ ํ ์คํธ๋ผ๋ ํ ์คํธ๊ฐ ์์ ์ํ์ด ๋์ง ์์
@Test
@Disabled // ๋ ์ด์ ํ
์คํธ ์ํ ์ํ๊ฒ ๋ค๋ ๋ป
@DisplayName("๋ฉ๋ชจ ์์ฑ ์คํจ")
void test2() {
...
์์์ฑ ์ปจํ ์คํธ์ ํธ๋์ญ์ ์ ์๋ช ์ฃผ๊ธฐ

Spring ์ปจํ ์ด๋ ํ๊ฒฝ์์๋ ์์์ฑ ์ปจํ ์คํธ์ ํธ๋์ญ์ ์ ์๋ช ์ฃผ๊ธฐ๊ฐ ์ผ์นํจ
- ํธ๋์ญ์ ์ด ์ ์ง๋๋ ๋์์ ์์์ฑ ์ปจํ ์คํธ๋ ๊ณ์ ์ ์ง๋๊ธฐ ๋๋ฌธ์ ์์์ฑ ์ปจํ ์คํธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์
- Spring์์๋ ํธ๋์ญ์ ์ ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ Service๋ถํฐ Repository๊น์ง Transaction์ ์ ์งํ ์ ์์
ํธ๋์ญ์ ์ ํ
// MemoRepository ๋ด์ ์ฝ๋ ์ถ๊ฐ
@Transactional
public Memo createMemo(EntityManager em) {
Memo memo = em.find(Memo.class, 1);
memo.setUsername("Robbie");
memo.setContents("@Transactional ์ ํ ํ
์คํธ ์ค!");
System.out.println("createMemo ๋ฉ์๋ ์ข
๋ฃ");
return memo;
}
package com.sparta.memo;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
public class TransactionTest {
@PersistenceContext
EntityManager em;
@Autowired
MemoRepository memoRepository;
@Test
@Transactional
@Rollback(value = false) // ํ
์คํธ ์ฝ๋์์ @Transactional ๋ฅผ ์ฌ์ฉํ๋ฉด ํ
์คํธ๊ฐ ์๋ฃ๋ ํ ๋กค๋ฐฑํ๊ธฐ ๋๋ฌธ์ false ์ต์
์ถ๊ฐ
@DisplayName("๋ฉ๋ชจ ์์ฑ ์ฑ๊ณต")
void test1() {
Memo memo = new Memo();
memo.setUsername("Robbert");
memo.setContents("@Transactional ํ
์คํธ ์ค!");
em.persist(memo); // ์์์ฑ ์ปจํ
์คํธ์ ๋ฉ๋ชจ Entity ๊ฐ์ฒด๋ฅผ ์ ์ฅํฉ๋๋ค.
}
@Test
@Disabled
@DisplayName("๋ฉ๋ชจ ์์ฑ ์คํจ")
void test2() {
Memo memo = new Memo();
memo.setUsername("Robbie");
memo.setContents("@Transactional ํ
์คํธ ์ค!");
em.persist(memo); // ์์์ฑ ์ปจํ
์คํธ์ ๋ฉ๋ชจ Entity ๊ฐ์ฒด๋ฅผ ์ ์ฅํฉ๋๋ค.
}
@Test
@Transactional
@Rollback(value = false)
@DisplayName("ํธ๋์ญ์
์ ํ ํ
์คํธ")
void test3() {
memoRepository.createMemo(em);
System.out.println("ํ
์คํธ test3 ๋ฉ์๋ ์ข
๋ฃ");
}
}
- test3() ๋ฉ์๋๊ฐ ๋ถ๋ชจ ๋ฉ์๋์ด๊ณ , createMemo()๊ฐ ์์ ๋ฉ์๋์

- ์ฆ, ์์ ๋ฉ์๋์ ํธ๋์ญ์ ์ด ๋ถ๋ชจ ๋ฉ์๋์ ํธ๋์ญ์ ์ ํฉ์ณ์ง
- @Transactional์๋ propagation์ด๋ผ๋ ์ต์ ์ด ์กด์ฌํจ
@Transactional์ propagation ์ต์
REQUIRED: ๋ถ๋ชจ ๋ฉ์๋์ Transactional์ด ์กด์ฌํ๋ค๋ฉด, ์์ ๋ฉ์๋์ ํธ๋์ญ์ ์ ๋ถ๋ชจ ๋ฉ์๋์ ํธ๋์ญ์ ์ ํฉ๋ฅํ๊ฒ ๋จ(default ์ต์ )
@Transactional(propagation = Propagation.REQUIRED)
๋ง์ฝ, ๋ถ๋ชจ ๋ฉ์๋์ Transactional์ด ์กด์ฌํ์ง ์๋๋ค๋ฉด, ์์ ๋ฉ์๋ ํธ๋์ญ์ ์ข ๋ฃ ํ ๋๋จ
@Test
// @Transactional()
// @Rollback(value = false)
@DisplayName("ํธ๋์ญ์
์ ํ ํ
์คํธ")
void test3() {
memoRepository.createMemo(em);
System.out.println("ํ
์คํธ test3 ๋ฉ์๋ ์ข
๋ฃ");
}

Spring Data JPA
Spring Data JPA๋ JPA๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด๋์ ํ๋์ ๋ชจ๋

- JPA๋ฅผ ์ถ์ํ์ํจ Repository ์ธํฐํ์ด์ค ์ ๊ณตํจ
- Repository ์ธํฐํ์ด์ค๋ Hibernate์ ๊ฐ์ JPA ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ ํด๋์ค๋ฅผ ํตํด ์ฌ์ฉ๋จ
- ๊ฐ๋ฐ์๋ค์ Repository ์ธํฐํ์ด์ค๋ฅผ ํตํด JPA๋ฅผ ๊ฐํธํ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋จ

- Spring Data JPA์์๋ JpaRepository ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ์๋์ผ๋ก ์์ฑํด์ค
- Spring ์๋ฒ๊ฐ ๋ฐ ๋ JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ ์ธํฐํ์ด์ค๊ฐ ์๋์ผ๋ก ์ค์บ์ด ๋๋ฉด, ํด๋น ์ธํฐํ์ด์ค์ ์ ๋ณด๋ฅผ ํ ๋๋ก ์๋์ผ๋ก SimpleJpaRepository ํด๋์ค๋ฅผ ์์ฑํด์ฃผ๊ณ , ์ด ํด๋์ค๋ฅผ Spring Bean์ผ๋ก ๋ฑ๋กํจ
- ๋ฐ๋ผ์ ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค๋ฅผ ์ง์ ์์ฑํ์ง ์์๋ JpaRepository ์ธํฐํ์ด์ค๋ฅผ ํตํด JPA์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์
public interface MemoRepository extends JpaRepository<Memo, Long> {
}
- JpaRepository<"@Entity ํด๋์ค", "@Id์ ๋ฐ์ดํฐ ํ์
">๋ฅผ ์์๋ฐ๋ interface๋ก ์ ์ธํจ
- Spring Data JPA์ ์ํด ์๋์ผ๋ก Bean ๋ฑ๋ก ๋จ
- ์ ๋ค๋ฆญ์ค์ @Entity ํด๋์ค ์์น์ Memo Entity๋ฅผ ์ถ๊ฐํ๊ธฐ ๋๋ฌธ์ ํด๋น MemoRepository๋ DB์ memo ํ ์ด๋ธ๊ณผ ์ฐ๊ฒฐ๋์ด CRUD ์์ ์ ์ฒ๋ฆฌํ๋ ์ธํฐํ์ด์ค๊ฐ ๋จ
๋ฉ๋ชจ์ฅ ํ๋ก์ ํธ์ Spring Data JPA ์ ์ฉํ๊ธฐ
package com.sparta.memo.repository;
import com.sparta.memo.entity.Memo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// SimpleJpaRepository์ @Repository ์ด๋
ธํ
์ด์
์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ
// interface์ ๋ฐ๋ก @Repository ๋ฌ์์ฃผ์ง ์์๋ ๋จ!
// @Repository
public interface MemoRepository extends JpaRepository<Memo, Long> {
}
package com.sparta.memo.service;
import com.sparta.memo.dto.MemoRequestDto;
import com.sparta.memo.dto.MemoResponseDto;
import com.sparta.memo.entity.Memo;
import com.sparta.memo.repository.MemoRepository;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MemoService {
private final MemoRepository memoRepository;
// MemoService๊ฐ ์์ฑ์ด ๋ ๋, ํ๋ผ๋ฏธํฐ๋ก JdbcTemplate ๋ฐ์์ค๊ณ
// ๋ฑ ํ ๋ฒ๋ง MemoRepository๋ฅผ ๋ง๋ฆ๊ธฐ
public MemoService(MemoRepository memoRepository) {
this.memoRepository = memoRepository;
}
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
// RequestDto -> Entity
Memo memo = new Memo(requestDto);
// DB ์ ์ฅ
Memo saveMemo = memoRepository.save(memo);
// Entity -> ResponseDto
MemoResponseDto memoResponseDto = new MemoResponseDto(memo);
return memoResponseDto;
}
public List<MemoResponseDto> getMemos() {
// DB ์กฐํ
return memoRepository.findAll().stream().map(MemoResponseDto::new).toList();
}
@Transactional
public Long updateMemo(Long id, MemoRequestDto requestDto) {
// ํด๋น ๋ฉ๋ชจ๊ฐ DB์ ์กด์ฌํ๋์ง ํ์ธ
Memo memo = findMemo(id);
// memo ๋ด์ฉ ์์
memo.update(requestDto);
return id;
}
public Long deleteMemo(Long id) {
// ํด๋น ๋ฉ๋ชจ๊ฐ DB์ ์กด์ฌํ๋์ง ํ์ธ
Memo memo = findMemo(id);
// memo ์ญ์
memoRepository.delete(memo);
return id;
}
private Memo findMemo(Long id) {
// ํด๋น ๋ฉ๋ชจ๊ฐ DB์ ์กด์ฌํ๋์ง ํ์ธ
return memoRepository.findById(id).orElseThrow(() ->
new IllegalArgumentException("์ ํํ ๋ฉ๋ชจ๋ ์กด์ฌํ์ง ์์ต๋๋ค.")
);
}
}
JPA Auditing ์ ์ฉํ๊ธฐ
- ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋ ๋๋ง๋ค ์ ์ฅ๋ ์๊ฐ ํน์ ์์ ๋ ์๊ฐ์ ์ ์ฅํ๊ณ ๋ณด์ฌ์ฃผ๊ณ ์ถ์ ๊ฒฝ์ฐ, ์ฝ๋๋ฅผ ์ง์ ์์ฑํ๋ ๊ฒ์ ์๊ฐ๋ ๊ฑธ๋ฆฌ๊ณ ๋ก์ง๋ ๋ณ๊ฒฝํด์ผ ํจ
- ๋ํ, ์ฌ๋ฌ ์ํฐํฐ์ ์์ฑ ์๊ฐ๊ณผ ์์ ์๊ฐ์ ์ถ๊ฐํ๊ณ ์ถ์ ๊ฒฝ์ฐ, ๋ชจ๋ ์ํฐํฐ ํด๋์ค๋ฅผ ์ํํ๋ฉด์ ์ง์ ๋ฃ์ด์ฃผ์ด์ผ ํจ
Spring Data JPA๋ ์๊ฐ์ ๋ํด์ ์๋์ผ๋ก ๊ฐ์ ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ์ธ JPA Auditing ์ ๊ณตํ๊ณ ์์
package com.sparta.memo.entity;
import jakarta.persistence.*;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@Getter
// @MappedSuperclass ์ด๋
ธํ
์ด์
// JPA Entity Class๋ค์ด ํด๋น ์ถ์ Class๋ฅผ ์์ํ ๊ฒฝ์ฐ,
// ์ถ์ ํด๋์ค(abstract class)์์ ์ ์ธํ ๋ฉค๋ฒ ๋ณ์(์ฆ, ์ฌ๊ธฐ์๋ createdAt, modifiedAt)๋ฅผ ์ปฌ๋ผ์ผ๋ก ์ธ์ํด์ค
@MappedSuperclass
// @EntityListeners(AuditingEntityListener.class) ์ด๋
ธํ
์ด์
// ํด๋น ํด๋์ค ์ฆ Timestamped ํด๋์ค์ Auditing ๊ธฐ๋ฅ ํฌํจ์์ผ์ค
// ์ด ์ด๋
ธํ
์ด์
์ ๋ฌ์์ค์ผ์ง ์๋์ผ๋ก ์๊ฐ์ ๋ฃ์ด์ฃผ๋ ๊ธฐ๋ฅ์ด ์ํ๋จ
@EntityListeners(AuditingEntityListener.class)
public abstract class Timestamped {
@CreatedDate // Entity ๊ฐ์ฒด๊ฐ ์์ฑ๋์ด ์ ์ฅ๋ ๋ ์๊ฐ ๊ฐ์ด ์๋์ผ๋ก ์ ์ฅ๋จ
@Column(updatable = false) // ์ต์ด ์๊ฐ๋ง ์ ์ฅ๋๊ณ , ์์ ์๊ฐ์ ์ ์ฅ๋๊ฒ ํ์ง ์๊ธฐ ์ํด updatable = false ์ต์
์ถ๊ฐ
@Temporal(TemporalType.TIMESTAMP) // Java์ Date Type ํน์ Calender ๊ฐ์ ๋ ์ง ๋ฐ์ดํฐ๋ฅผ ๋งคํํ ๋ ์ฌ์ฉ
// DATE: 20xx-01-01
// TIME: 20:21:13
// TIMESTAMP: 20xx-01-01 20:21:13.33333
private LocalDateTime createdAt;
@LastModifiedDate // ์กฐํํ Entity ๊ฐ์ฒด ๊ฐ์ ๋ณ๊ฒฝํ ๋, ๋ณ๊ฒฝ๋ ์๊ฐ์ด ์๋์ผ๋ก ์ ์ฅ๋จ
@Column
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime modifiedAt;
}
@EnableJpaAuditing // JPA Auditing ๊ธฐ๋ฅ์ ์ฌ์ฉํ๊ธฐ ์ํด์ ์๋ ค์ค์ผํจ
@SpringBootApplication
public class MemoApplication {
public static void main(String[] args) {
SpringApplication.run(MemoApplication.class, args);
}
}
@Entity // JPA๊ฐ ๊ด๋ฆฌํ ์ ์๋ Entity ํด๋์ค ์ง์
@Getter
@Setter
@Table(name = "memo") // ๋งคํํ ํ
์ด๋ธ์ ์ด๋ฆ์ ์ง์
@NoArgsConstructor
public class Memo extends Timestamped{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
package com.sparta.memo.dto;
import com.sparta.memo.entity.Memo;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
public class MemoResponseDto {
private Long id;
private String username;
private String contents;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
public MemoResponseDto(Memo memo) {
this.id = memo.getId();
this.username = memo.getUsername();
this.contents = memo.getContents();
this.createdAt = memo.getCreatedAt();
this.modifiedAt = memo.getModifiedAt();
}
}

Query Methods๋?
Spring Data JPA์์๋ method ์ด๋ฆ์ผ๋ก SQL์ ์์ฑํ ์ ์๋ Query Methods ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ์์
public interface MemoRepository extends JpaRepository<Memo, Long> {
// ModifiedAt ํ๋ ๋ฐ์ดํฐ ๊ธฐ์ค์ผ๋ก ๋ด๋ฆผ์ฐจ์์ผ๋ก ์ ๋ ฌํด์ ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋
List<Memo> findAllByOrderByModifiedAtDesc();
}
- ์์ ์์์ฒ๋ผ ์ด๋ฏธ ์ ์๋์ด ์๋ ๊ท์น์ ๋ง๊ฒ๋ method๋ฅผ ์ ์ธํ๋ฉด, ํด๋น method ์ด๋ฆ์ ๋ถ์ํ ํ SimpleJpaRepository์์ ๊ตฌํ๋จ

public interface MemoRepository extends JpaRepository<Memo, Long> {
...
// ํน์ username์ ๊ฐ์ง ์ฌ๋์ Memo ๋ฐ์ดํฐ ๊ฐ์ ธ์ด
List<Memo> findAllByUsername(String username);
}'๋ฐฑ์๋ > spring&springboot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [๋ด์ผ๋ฐฐ์์บ ํ_Spring Boot ์๋ จ์ฃผ์ฐจ] ์ธ์ฆ๊ณผ ์ธ๊ฐ(+์ฟ ํค-์ธ์ , JWT) (0) | 2026.06.26 |
|---|---|
| [๋ด์ผ๋ฐฐ์์บ ํ_Spring Boot ์๋ จ์ฃผ์ฐจ] Bean (0) | 2026.06.26 |
| [๋ด์ผ๋ฐฐ์์บ ํ_Spring Boot ์ ๋ฌธ์ฃผ์ฐจ] JPA CORE (1) | 2026.06.25 |
| [๋ด์ผ๋ฐฐ์์บ ํ_Spring Boot ์ ๋ฌธ์ฃผ์ฐจ] IoC์ DI (1) | 2026.06.25 |
| [๋ด์ผ๋ฐฐ์์บ ํ_Spring Boot ์ ๋ฌธ์ฃผ์ฐจ] 3 Layer Architecture (0) | 2026.06.25 |