// Native Query 사용
@Query(value = "select * from book", nativeQuery = true)
List<Book> findAllCustom1();
// Native Query로 Update
@Transactional // UPDATE, INSERT, DELETE 수행하는 @Query 수행 시
@Modifying // UPDATE, INSERT,DELETE 수행하는 @Query 임을 알림
@Query(value = "update book set category = 'IT 전문서'", nativeQuery = true)
int updateCategories();
// DML 의 경우 리턴타입이 void, int, long 일수 있다.
// int 나 long 리턴하게 되면 affected row 를 받게 된다.
// JPA 에선 지원하지 않는 쿼리
@Query(value="show tables", nativeQuery = true)
List<String> showTables();
@Entity
public class Book {
private String name; // VARCHAR로 변환
private LocalDateTime createdAt; // TIMESTAMP로 변환
private Boolean isActive; // BOOLEAN/BIT로 변환
}
@Entity
public class Book {
@Convert(converter = BookStatusConverter.class)
private BookStatus status; // 단순 Integer가 아닌 커스텀 객체로 변환
}
@Converter
public class BookStatusConverter implements AttributeConverter<BookStatus, Integer> {
// BookStatus -> DB 칼럼값으로 변환
@Override
public Integer convertToDatabaseColumn(BookStatus bookStatus) {
return bookStatus.getCode();
}
// DB column 값 -> BookStatus로 변환
@Override
public BookStatus convertToEntityAttribute(Integer s) {
return s != null ? new BookStatus(s) : null;
}
}
@Data
public class BookStatus {
private int code;
private String description;
public BookStatus(int code) {
this.code = code;
this.description = parseDescription(code);
}
private String parseDescription(int code) {
return switch (code) {
case 100 -> "판매종료";
case 200 -> "판매중";
case 300 -> "판매보류";
default -> "미지원";
};
}
public boolean isDisplayed(){
return code == 200;
}
}
- null 처리를 확실히 한다
- 예외 발생 가능성을 최소화한다
- 변환 로직을 단순하게 유지한다

@Data
@AllArgsConstructor
@NoArgsConstructor
@Embeddable // Embed 할 수 있는 클래스임을 선언
public class Address {
private String city; // 도시
private String district; // 구/군
@Column(name = "address_detail")
private String detail; // 상세주소
private String zipCode; // 우편번호
}
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "home_city")),
@AttributeOverride(name = "district", column = @Column(name = "home_distirct")),
@AttributeOverride(name = "detail", column = @Column(name = "home_address_detail")),
@AttributeOverride(name = "zipCode", column = @Column(name = "home_zip_code")),
})
private Address homeAddress; // 집주소
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city", column = @Column(name = "company_city")),
@AttributeOverride(name = "district", column = @Column(name = "company_distirct")),
@AttributeOverride(name = "detail", column = @Column(name = "company_address_detail")),
@AttributeOverride(name = "zipCode", column = @Column(name = "company_zip_code")),
})
private Address companyAddress; // 회사 주소
@Test
void embeddedTest1() {
// 기본 저장 테스트
User user = new User();
user.setName("절미");
user.setHomeAddress(new Address("서울시", "강남구", "강남대로 888", "08865"));
user.setCompanyAddress(new Address("문선시", "문선구", "문선로 777", "12345"));
userRepository.save(user);
// 저장된 데이터 확인
userRepository.findAll().forEach(System.out::println);
userHistoryRepository.findAll().forEach(System.out::println);
}
@Test
void embeddedTest2() {
// 1. 정상적인 주소 데이터
User user1 = new User();
user1.setName("steve");
user1.setHomeAddress(new Address("서울시", "강남구", "강남대로 777 골드타워", "08765"));
user1.setCompanyAddress(new Address("서울시", "성동구", "성수1로 333 우리빌딩", "04455"));
userRepository.save(user1);
// 2. null 주소 데이터
User user2 = new User();
user2.setName("joshua");
user2.setHomeAddress(null);
user2.setCompanyAddress(null);
userRepository.save(user2);
// 3. empty 주소 데이터
User user3 = new User();
user3.setName("jordan");
user3.setHomeAddress(new Address());
user3.setCompanyAddress(new Address());
userRepository.save(user3);
// 저장된 데이터 확인
userRepository.findAll().forEach(System.out::println);
userHistoryRepository.findAll().forEach(System.out::println);
// DB에 실제 저장된 로우 데이터 확인
System.out.println("🤪".repeat(20));
userRepository.findAllRowRecord().forEach(a -> System.out.println(a.entrySet()));
}
// Embedded된 Address 추가
userHistory.setHomeAddress(user.getHomeAddress());
userHistory.setCompanyAddress(user.getCompanyAddress());
userHistoryRepository.save(userHistory);
}
}
BUT