ํด๋น ๊ธ์ ๋ฐฐ์๋ณด์ Spring Data JPA ์๋ฆฌ์ฆ ์ ๋๋ค.
ํด๋น ์๋ฆฌ์ฆ์ ๋ด์ฉ์ด ์ด์ด์ง๋ ํํ์ด๋ฏ๋ก ๊ธ์ ๋ด์ฉ ์ค์ ์๋ต๋๋ ๋ง๋ค์ด ์์ ์ ์์ผ๋, ์์ธํ ์ฌํญ์ ์๋ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์!
- Spring Data JPA์ ๊ธฐ๋ณธ๊ณผ ํ๋ก์ ํธ ์์ฑ :: ์๋ฆฌ์ฆ ํ์ต ํ๊ฒฝ ์ค๋น
- JPA์ ๊ธฐ๋ณธ๊ณผ Spring Data JPA
- Springboot project ์์ JPA ์ค์ ํ๊ธฐ
- JPA์ ๊ธฐ๋ณธ ์ด๋
ธํ
์ด์
๋ค :: JPA์ ์์๊ณผ ๋์์ ๋
- ์ํฐํฐ์ ํ ์ด๋ธ ๋งคํ
- ํ๋์ ์ปฌ๋ผ ๋งคํ
- ๋งคํ ํ
์ด๋ธ๊ณผ ์ฐ๊ด๊ด๊ณ ๋งคํํ๊ธฐ :: RDB์ ๊ฝ, ์ฐ๊ด ๊ด๊ณ
- ์ฐ๊ด๊ด๊ณ๋?, ์ธ๋ ํค๋?, ๋งคํ ํ ์ด๋ธ์ด๋?
- ์ผ๋์ผ, ์ผ๋๋ค, ๋ค๋์ผ ์ฐ๊ด๊ด๊ณ
- ๊ณตํต ์ธํฐํ์ด์ค ๊ธฐ๋ฅ :: ์ด๋ป๊ฒ Data JPA ๋ ๋์ํ ๊น?
- ๋จ๊ฑด ์กฐํ ๋ฐํ ํ์
- ์ปฌ๋ ์ ์กฐํ ๋ฐํ ํ์
- ์ฌ์ฉ์ ์ ์ ์ฟผ๋ฆฌ ์ด์ฉํ๋ ๋ฐฉ๋ฒ :: ๋ด๊ฐ ์ํ๋ ์ฟผ๋ฆฌ๋ฅผ JPA ์์ ๋ง๋ค์ด๋ณด์!
- ๋ฉ์๋ ์ด๋ฆ์ผ๋ก ์ฟผ๋ฆฌ ์์ฑํ๊ธฐ
@Query
๋ฅผ ์ด์ฉํ์ฌ ๋ฉ์๋์ ์ ์ ์ฟผ๋ฆฌ ์์ฑํ๊ธฐ
- ํ์ด์ง๊ณผ ์ ๋ ฌ :: ๊ฒ์ํ๊ณผ ๊ฐ์ ํ์ด์ง๊ฐ ์๋ ์๋น์ค์์ ๋น์ ๋ฐํ๋ JPA ํ์ด์ง!
- Data JPA์ ํ์ด์ง๊ณผ ์ ๋ ฌ
- Web MVC ์์ JPA ํ์ด์ง๊ณผ ์ ๋ ฌ
- Auditing :: ๋ชจ๋ ์์ฒญ๊ณผ ์๋ต์
๋๊ฐ, ์ธ์
์ ๊ทผํ๋์ง ํ๋์ ์ํฐํฐ๋ก ๊ด๋ฆฌํ์.- ์์ JPA์ Auditing
- Spring Data JPA์ Auditing
์๋น์ค๋ฅผ ์ด์ํ ๋ ์ฌ์ฉ์์ ๊ธฐ๋ณธ์ ์ธ ๋ก๊ทธ๋ฅผ DB์ ๋จ๊ฒจ์ผ ํ ๋๊ฐ ์๋ค.
์ด๋ฅผํ ๋ฉด ๋ง์ง๋ง ๋ก๊ทธ์ธ ์๊ฐ์ด๋ผ๋์ง ์ํฐํฐ ์์ฑ ์๊ฐ, ๋ณ๊ฒฝ๋ ์๊ฐ๊ณผ ๋ณ๊ฒฝํ ์ฌ๋์ ์ด๋ฆ๋ฑ๋ฑ.
์๋ฅผ ๋ค์ด๋ณด์.
public class User {
private Long id;
private String name;
private String address;
}
public class OrderItem {
private Long id;
private User userId;
private Item itemId;
}
public class Item {
private Long id;
private String name;
private String description;
}
์ด์ ๊ฐ์ ์ํฐํฐ๊ฐ ์กด์ฌํ๋ค๊ณ ํด๋ณด์.
์๋ฅผ ๋ค์ด ๊ฐ๊ฐ์ ์์ฑ ์๊ฐ์ ์ถ๊ฐํด์ผ ํ๋ค๊ณ ํด๋ณด์.
public class User {
private Long id;
private String name;
private String address;
private LocalDateTime createdAt;
private String createdBy;
}
public class OrderItem {
private Long id;
private User userId;
private Item itemId;
private LocalDateTime createdAt;
private String createdBy;
}
public class Item {
private Long id;
private String name;
private String description;
private LocalDateTime createdAt;
private String createdBy;
}
์ด๋ด ๊ฒฝ์ฐ ๋ชจ๋ ์ํฐํฐ๊ฐ LocalDateTime createdAt
, String createdBy
์ ๋ํ ์ฐ์ฐ์ ์ํํด์ผ ํ๋ค.
๊ทผ๋ฐ ์๊ฐ ํ ๋ฒ ํด๋ณด์. ๋ชจ๋ ์ํฐํฐ๊ฐ ๋์ผํ ์ฐ์ฐ์ ์ํํด์ผ ํ๋ค๋ฉด ์ฐ์ฐ์ ์ฒ๋ฆฌํ๋ ๊ธฐ๋ณธ ์ํฐํฐ๋ฅผ ๋ง๋ค๊ณ ๋ชจ๋ ๋ฐ์ดํฐ ์์ฒญ์์ ์ํฐํฐ๊ฐ ์ ๋ฐ์ดํธ๋๋ค๋ฉด ๋์ง ์์๊น?
์ด ๋ ์ฆ, ์์ฑ์ผ/์์ ์ผ/์์ฑ์๋ฅผ ์๋ํํ ๋ ์ฌ์ฉํ๋๊ฒ ๋ฐ๋ก JPA Auditing ์ด๋ค.
Spring Data JPA ์์์ Auditing
Spring Data JPA ์์๋ ์์ Auditing ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
Auditing์ Spring Data JPA ์์๋ง ์ฌ์ฉํ ์ ์๋ ๊ฐ๋ ์ ์๋๋ค.
JPA ์์ฒด์ ์ผ๋ก๋ Auditing ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ง๋ง Spring Data JPA ์์๋ ๋ ๊น๋ํ๊ณ ์ฝ๊ฒ ์ ๊ณตํ๋ค.
ํ์ฌ ์๋ฆฌ์ฆ์ ๋ชฉ์ ์ Spring Data JPA๋ฅผ ํ์ตํ๋ ๊ฒ์ด๋ฏ๋ก JPA ์์์ Auditing ์ ๋ํด์๋ ์๋ตํ๊ฒ ๋ค.
๋ง์ฝ ๊ถ๊ธํ๋ค๋ฉด@PrePersist
,@PostPersist
,@PreUpdate
,@PostUpdate
ํค์๋๋ก ์ฐพ์๋ณผ ๊ฒ์ ์ถ์ฒํ๋ค.
@EnableJpaAuditing ์ฌ์ฉํ๊ธฐ
Spring Data JPA ์์ JPA ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ SpringBoot ์ค์ ํด๋์ค์ @EnableJpaAuditing
์ ์ ์ด์ค์ผํ๋ค.
๋ณดํต Springboot ๋ฅผ ์คํ์ํค๋ ํด๋์ค ์๋จ์ ๋ง์ด ์ฌ์ฉํ๊ณ ๋ ํ๋ค.
@EnableJpaAuditing
@SpringBootApplication
public class DatajpaApplication {
public static void main(String[] args) {
SpringApplication.run(DatajpaApplication.class, args);
}
}
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ๊ฐ Auditing์ ํ ํ๋๋ฅผ ๊ฐ๋ ๊ธฐ๋ณธ ์ํฐํฐ๋ฅผ ์์ฑํ์.
์ฒ์ ๋ณด๋ ์ฝ๋๋ค์ด ๋์ค๋๋ผ๋ ๊ฑฑ์ ํ์ง ๋ง์! ํ๋์ฉ ๊ฐ์ด ์์๋ณผ ๊ฒ์ด๋
@EntityListeners(AuditingEntityListener.class) // 1
@MappedSuperClass // 2
@Getter // 3
public BaseEntity {
@CreatedDate // 4
@Column(updatable = false) // 5
private LocalDateTime createdDate;
}
์ด๋ ๊ฒ ์์ฑํ๋ฉด ํด๋น ์ํฐํฐ ํด๋์ค๊ฐ JPA ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ค๋ฉด Auditing ์ ์ํํ์ฌ ๊ฐ์ ์ ๋ฐ์ดํธ ํ๋ค.
์! ๋ฌด์จ ์๋ก์ด ์ด๋
ธํ
์ด์
๋ค์ด ์กด์ฌํ๋ค. ํ๋์ฉ ์์๋ณด์.
@EntityListeners(AuditingEntityListener.class)
@MappedSuperClass
@Getter
@CreatedDate
@Column(updatable = false)
@EntityListeners
@EntityListeners
๋ ์ํฐํฐ๋ฅผ DB์ ์ ์ฉํ๊ธฐ ์ , ์ดํ์ ์ปค์คํ
์ฝ๋ฐฑ์ ์์ฒญํ ์ ์๋ ์ด๋
ธํ
์ด์
์ด๋ค.
@EntityListeners
์ ์ธ์๋ก ์ปค์คํ
์ฝ๋ฐฑ์ ์์ฒญํ ํด๋์ค๋ฅผ ์ง์ ํด์ฃผ๋ฉด ๋๋๋ฐ, Auditing ์ ์ํํ ๋๋ JPA ์์ ์ ๊ณตํ๋ AuditingEntityListener.class
๋ฅผ ์ธ์๋ก ๋๊ธฐ๋ฉด ๋๋ค.
๊ทธ๋ผ ์์ ๋ณด๋๋ฐ์ ๊ฐ์ด@PrePersist ์ด๋ ธํ ์ด์ ์ผ๋ก JPA ์ Auditing ๊ธฐ๋ฅ์ Spring Data JPA ๊ฐ ์ฌ์ฉํ๊ฒ ๋๋ ๊ฒ์ด๋ค.
@MappedSuperClass
@MappedSuperClass
์ ์ํฐํฐ์ ๊ณตํต ๋งคํ ์ ๋ณด๊ฐ ํ์ํ ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ค.
์ฆ, ๋ถ๋ชจ ํด๋์ค(์ํฐํฐ)์ ํ๋๋ฅผ ์ ์ธํ๊ณ ๋จ์ํ ์์ฑ๋ง ๋ฐ์์ ์ฌ์ฉํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
์ฐ๋ฆฌ๋ BaseEntity
๋ฅผ ์์ฑํ๊ณ Auditing ๊ธฐ๋ฅ์ด ํ์ํ ์ํฐํฐ ํด๋์ค์์ ์ฌ์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ @MappedSuperClass
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
@CreatedDate
@CreatedDate
์ด๋
ธํ
์ด์
์ Spring Data JPA์ Auditing ์์ ๊ฐ์ฅ ํฅ๋ฏธ๋ก์ด ์ด๋
ธํ
์ด์
์ด๋ค.
์ฌ์ค ์ด ์ด๋ ธํ ์ด์ ๋ Spring Data JPA ์ ๊ณ ์ ๊ธฐ๋ฅ์ ์๋๊ณ Spring Data ์ ์๋ ์ด๋ ธํ ์ด์ ์ผ๋ก Spring Data ์์ ์ถ์ํ ํด๋์ ๊ฒ์ด๋ค.
CreatedDate์ javadoc ์ ๋์จ ์ค๋ช ์ ์ฐธ๊ณ ํด๋ณด์.
Declares a field as the one representing the date the entity containing the field was created.
๋ฒ) ํ๋๋ฅผ ํฌํจํ๋ ์ํฐํฐ๊ฐ ์์ฑ๋ ๋ ์ง๋ฅผ ๋ํ๋ด๋ ํ๋๋ผ๊ณ ์ ์ธํ๋ค.
javadoc ์ ๋์จ ๋ด์ฉ์ ๋ณด๋ฉด ์ฐ๋ฆฌ๊ฐ ํด๋น ํ๋๋ฅผ ์ ์ธํ๋ฉด ์ํฐํฐ๊ฐ ์์ฑ๋ ๋ ์ง, created ๋ ๋ ์ง๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค๋ ๊ฒ์ด๋ค.
์ด์ ๋น์ทํ ์ด๋ ธํ ์ด์ ์ด ๋ช ๊ฐ ๋ ์กด์ฌํ๋ค.
CreatedDate
- ํด๋น ์ํฐํฐ๊ฐ ์์ฑ๋ ๋, ์์ฑํ๋ ์๊ฐ์ ์๋์ผ๋ก ์ฝ์ ํด์ค๋ค.
CreatedBy
- ํด๋น ์ํฐํฐ๊ฐ ์์ฑ๋ ๋, ์์ฑํ๋ ์ฌ๋์ด ๋๊ตฌ์ธ์ง ์๋์ผ๋ก ์ฝ์ ํด์ค๋ค.
- ์์ฑํ๋ ์ฃผ์ฒด๋ฅผ ์ง์ ํ๊ธฐ ์ํด์
AuditorAware<T>
๋ฅผ ์ง์ ํด์ผ ํ๋ค.์ด๋ Spring Security ์ ํจ๊ป ๋ค๋ค์ผ ํ๋ ๋ด์ฉ์ด๋ฏ๋ก ์ถํ ์ ๋ก๋ ์์
LastModifiedDate
- ํด๋น ์ํฐํฐ๊ฐ ์์ ๋ ๋, ์์ ํ๋ ์๊ฐ์ ์๋์ผ๋ก ์ฝ์ ํด์ค๋ค.
LastModifiedBy
- ํด๋น ์ํฐํฐ๊ฐ ์์ ๋ ๋, ์์ ํ๋ ์ฃผ์ฒด๊ฐ ๋๊ตฌ์ธ์ง ์๋์ผ๋ก ์ฝ์
ํด์ค๋ค.
- ์์ฑํ๋ ์ฃผ์ฒด๋ฅผ ์ง์ ํ๊ธฐ ์ํด์
AuditorAware<T>
๋ฅผ ์ง์ ํด์ผ ํ๋ค.์ด๋ Spring Security ์ ํจ๊ป ๋ค๋ค์ผ ํ๋ ๋ด์ฉ์ด๋ฏ๋ก ์ถํ ์ ๋ก๋ ์์
- ์์ฑํ๋ ์ฃผ์ฒด๋ฅผ ์ง์ ํ๊ธฐ ์ํด์
- ํด๋น ์ํฐํฐ๊ฐ ์์ ๋ ๋, ์์ ํ๋ ์ฃผ์ฒด๊ฐ ๋๊ตฌ์ธ์ง ์๋์ผ๋ก ์ฝ์
ํด์ค๋ค.
์์์ ์ธ๊ธํ์๋ฏ
CreatedBy
์LastModifiedBy
์ด๋ ธํ ์ด์ ์ ์ถํ Spring Security ์๋ฆฌ์ฆ์ ํจ๊ป ์ด์ผ๊ธฐ ํด๋ณด๋ ค ํ๋ค.
๋ง์ฝ ๊ถ๊ธํ๋ค๋ฉดAuditorAware ์ ์ฉ
ํค์๋๋ก ๊ฒ์ํด๋ณผ ๊ฒ์ ์ถ์ฒํ๋ค.
@Column(updatable = false)
์ด๋ JPA์ ๊ธฐ๋ณธ ์ด๋ ธํ ์ด์ ์์ ๋์จ @Column๊ณผ ๋์ผํ๋ค.
updatable ์ ์ false ๋ก ํ์๊น?
ํน์ ๋ชจ๋ฅผ ์ํฉ์ ๋๋นํด์์ด๋ค.
์ฐ๋ฆฌ๋ ํด๋น BaseEntity๋ฅผ JPA๊ฐ ํ ์ด๋ธ์ ์ ๊ทผํ๋ ์์ ์๋ง JPA๊ฐ ์ฌ์ฉํ๋๋ก ํ๊ณ ์ถ์๋ฐ ๋ง์ฝ ๊ฐ๋ฐ์์ ์ํด ์์ ๋๋ฉด ์๋๊ธฐ ๋๋ฌธ์ updatable์ false๋ก ํด์ฃผ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
ํ ์คํธ
์ ์ด์ ํ ์คํธ๋ฅผ ํด๋ณด์.
์ฐ๋ฆฌ๋ User ์ํฐํฐ๋ฅผ ์์ฑํ ๋ createdBy ์ updatedBy ๋ฅผ Auditing ํ๊ฒ ํ ๊ฒ์ด๋ค.
BaseEntity
.class- Auditing ์ ์ํํ ๊ธฐ๋ณธ ์ํฐํฐ์ด๋ค.
User
.class- BaseEntity ๋ฅผ ์์๋ฐ์ BaseEntity์ Auditing ์ ์ด์ฉํ ๊ฐ์ฒด์ด๋ค.
UserRepository
.interface- ๊ธฐ๋ณธ
JpaRepository
๋ฅผ ์์๋ฐ์ ์ธํฐํ์ด์ค์ด๋ค.
- ๊ธฐ๋ณธ
UserRepositoryTest
.class- ํ ์คํธ๋ฅผ ์ํ ํ ์คํธ ํด๋์ค์ด๋ค.
BaseEntity.class
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(updatable = false)
private LocalDateTime updatedAt;
}
์ฐ๋ฆฌ์ ์์คํ Auditor ์ํฐํฐ์ด๋ค.
์์ ๋ง ํ๋ 4๊ฐ์ง ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
User.class
@Entity
public class User extends BaseEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String address;
private int age;
}
์ค์ํ ๊ฒ์ BaseEntity ๋ฅผ extends ํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
์ญ์ ๋กฌ๋ณต ๊ด๋ จ๋ ์ด๋
ธํ
์ด์
์ ์ญ์ ํ๋ค.
UserRepository.interface
public interface UserRepository extends JpaRepository<User, Long> {
}
UserRepositoryTest.class
@SpringBootTest
@Transactional
@Rollback(false)
class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
@DisplayName("Auditing ๊ธฐ๋ฅ ์ ์ฉ")
void findUser() {
// given
User user = User.builder()
.username("user ")
.age(20)
.address("Korfea")
.build();
// when
User savedUser = userRepository.save(user);
// then
assertNotNull(savedUser.getCreatedAt());
assertNotNull(savedUser.getUpdatedAt());
}
}
ํ ์คํธ๊ฐ ํต๊ณผํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ฟผ๋ฆฌ๋ฌธ๋ ์ ์ ํ๊ฒ ๋๊ฐ๊ฒ ๋๊ณ h2 ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ ์ ์ ์ฉ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋๊ธ