Open yepdi opened 2 years ago
SimpleJpaRepository
내에 위치@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
isNew
함수에서 true
값을 반환한다 Persistable
을 사용하여 새로운 엔티티 확인 여부를 직접 구현하는 것이 효과적임. 또한, @CreateDate 를 조합하여 사용한다면 새로운 엔티티 여부를 알 수 있음@Entity
@EntityListeners(AuditingEntityListener::class)
class Item(
@Id
val identifier: String,
@CreatedDate
private var createdDate: LocalDateTime? = null
): Persistable<String> {
override fun getId(): String? = identifier
override fun isNew(): Boolean {
return createdDate == null
}
}
createdDate == null
조건을 추가하여 새롭게 추가되었는지 아닌지를 판단한다 개인적인 경험으로는 로그 메시지를 쌓을 때 Persistable
을 사용하여 구현한 경험이 있다.
database 내 해당 데이터가 없을 것이라고 확실하게 사용하는 경우에 사용하면 좋을 듯
static kotlin 에서 static object나 method는 companion object를 통해 사용할 수 있다
JvmStatic @JvmStatic 은 object 선언이나 companion object 를 Java 에서 static method 로 보이도록 한다.
companion object {
fun teamName(teamName: String): Specification<Member> {
return Specification<Member> { root: Root<Member>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder ->
if (teamName.isEmpty()) return@Specification null
val t = root.join<String, JoinType> ("team", JoinType.INNER)
val name: Path<Set<String>> = t.get("name")
return@Specification criteriaBuilder.equal(name, teamName )
}
}
fun userName(username: String): Specification<Member> {
return Specification<Member> { root: Root<Member>, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder ->
val usernames: Path<Set<String>> = root.get("username")
return@Specification criteriaBuilder.equal(usernames, username)
}
}
}
interface UserNameOnly {
// username과 age를 둘다 가져와서 넣을 수 있다 entity에 있는 내용을 다 가져온다 (open projection)
@Value("#{target.username + ' ' + target.age}")
fun getUsername(): String
// 실제 구현체는 spring data jpa에서 만들어서 반환한다
}
fun findProjectionsByUsername(@Param("username") username: String): List<UserNameOnly>
@Value
annotation을 통해 가져오는 데이터를 조합할 수 있음class UsernameOnlyDto {
val username: String
constructor(username: String) {
this.username = username
}
}
fun findProjectionsDtoByUsername(@Param("username") username: String): List<UsernameOnlyDto>
interface NestedClosedProjections {
// username만 가져오지만
fun getUsername(): String
// 전체 entity를 가져온다
fun getTeam(): TeamInfo
interface TeamInfo {
fun getName(): String
}
}
fun findProjectionsNestedByUsername(@Param("username") username: String): List<NestedClosedProjections>
getName
을 정의했으나 entity 전체에 정의된 데이터를 가져온다가급적 네이티브 쿼리를 사용하지 않는 것이 좋다
최근에는 Projections 와 같이 사용
페이징 지원
반환타입
제약
Native SQL을 DTO로 조회할 때는 JdbcTemplate or myBatis 권장
interface MemberProjection {
fun getId(): Long
fun getUsername(): String
fun getTeamName(): String
}
@Query(value = "select m.member_id as id, m.username as username, t.name as teamName " +
"from member m left join team t",
countQuery = "select count(*) from member",
nativeQuery = true)
fun findByNativeProjection(pageable: Pageable): Page<MemberProjection>
Auditing
JPA 제공하는 Auditing 기능
MappedSuperclass
annotation을 꼭 추가해줘야 한다PrePersist
PreUpdate
annotation을 통해 생성(persist), 추가 될 때 자동으로 업데이트 해준다SpringDataJpa 에서 제공하는 Auditing 기능
MappedSuperclass
와EntityListeners
어노테이션을 반드시 추가해줘야 동작한다AuditorAware
도 구현해 줘야한다