강의

멘토링

커뮤니티

스프링 데이터 JPA 소개

소개

소리가 좀 큼

프로젝트 환경설정

프로젝트 생성

start.spring.io

의존 관계

잘 되네

세팅 추가

단축키 : 설정

설정 : 속도 빠르게

롬복 세팅

세팅 완료

라이브러리 살펴보기

라이브러리

HikariCP

SLF4J 인터페이스
LogBack 구현체

중요한거

h2 버전 1.4.200

스프링 데이터 JPA와 DB 설정, 동작확인

application.yml 생성

show_sql
org.hibernate.SQL

org.hibernate.type

entity

@Entity
@Getter @Setter
public class Member {

@Id @GeneratedValue
private Long id;
private String username;
}

repository

@Repository
public class MemberJpaRepository {

@PersistenceContext
private EntityManager em;

public Member save(Member member){
em.persist(member);
return member;
}

public Member find(Long id){
return em.find(Member.class, id);
}

}

test

단축키 : 테스트

junit5

@SpringBootTest
이제는 RunWith 안씀

import org.junit.jupiter.api.Test;

생성자에서 파라미터 넘기는게 더 나은 방법

protected Member(){  }

JPA가 사용할수 있게 기본생성자를 사용.
그러나 사람이 사용 못하게 protected

에러가 나네요.

No EntityManager with actual transaction available

디버그 : yml

format_sql: true

영속성 복습

스프링 Data jpa 리포지토리

test 생성

Optional 제공 = 있을수도 있고 없을수도 있다.

원래 이렇게 쓰면 안되요.

NosuchElementException 에러 뜸

Member findMember = memberRepository.findById(savedMember.getId()).get();

내부 확인

외부 라이브러리 : p6spy

예제 도메인 모델

예제 도메인 모델과 동작확인

Member 수정

실무에서 좋아하는 방법

@Column(name="member_id")

맴버랑 팀은 다 : 1

private List<Member> members = new ArrayList<>();

프로젝트 세팅

상용 버전

도움이 됩니다.

@OneToMany(mappedBy = "team")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of = {"id", "username", "age"})

team 넣으시면 큰일남

@Getter @Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(of={"id" , "name"})
public class Team {

연관관계 메소드

public void changTeam(Team team){
this.team = team;
team.getMembers().add(this);
}

Team 생성자 추가.

setter 쓰지마

터트리거나 무시하거나

Team teamA = new Team("teamA");
Team teamB = new Team("teamB");

em.persist(teamA);
em.persist(teamB);

Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 10, teamA);
Member member3 = new Member("member3", 10, teamB);
Member member4 = new Member("member4", 10, teamB);

em.persist(member1);
em.persist(member2);
em.persist(member3);
em.persist(member4);
em.flush();
em.clear();
//확인
List<Member> members = em.createQuery(
"select m from Member m", Member.class).getResultList();

for (Member member : members) {
System.out.println("member = " + member);
System.out.println("-> member.team = " + member.getTeam());
}

@XxToOne 관계는 fetch = LAZY 필수

정리

공통 인터페이스 기능

순수 JPA 기반 리포지토리 만들기

공통 인터페이스

jpa 모르면 한계가 옴

em.remove

private void delete(Member member){
em.remove(member);
}

public List<Member> findAll(){
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}

JAVA8 Optional 중요해

public Optional findById(Long id){
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public long count(){
return em.createQuery("select count(m) from Member m", Long.class)
.getSingleResult();
}

단축키 : 같은 변수명 바꾸기

public long count(){
return em.createQuery("select count(t) from Team t", Long.class)
.getSingleResult();
}

거의 같다.

JPA 데이터 변경감지 = 수정 함수가 필요 없음.

단축키 : 테스트 파일로 이동

Member member1 = new Member("member1");
Member member2 = new Member("member2");
memberJpaRepository.save(member1);
memberJpaRepository.save(member2);

옵셔널로 쓰세요

Member findMember1 = memberJpaRepository.findById(member1.getId()).get();
Member findMember2 = memberJpaRepository.findById(member2.getId()).get();

assertThat(findMember1).isEqualTo(member1);
assertThat(findMember2).isEqualTo(member2);
//리스트 조회 검증
List<Member> all = memberJpaRepository.findAll();
assertThat(all.size()).isEqualTo(2);

//카운트 검증
long count = memberJpaRepository.count();
assertThat(count).isEqualTo(2);

//삭제 검증
memberJpaRepository.delete(member1);
memberJpaRepository.delete(member2);

long deletedCount = memberJpaRepository.count();
assertThat(count).isEqualTo(2);

개발자의 유일한 여유

변경 되는거 맞나요.

더티 체킹

변경 감지 검증

팀 테스트는 생략

공통 인터페이스 설정

공통 인터페이스 설정

Java config

스프링 부트는 안써도됨

@EnableJpaRepositories(basePackages = "study.datajpa.repository")

DataJpaApplication 하위 레벨의 엔티티는 다 인식이 됨

객체를 만들어서 꽂아줌
memberRepository = class com.sun.proxy.$Proxy117

@Repository 어노테이션 생략 가능

어노테이션의 기능

공통 인터페이스 적용

이미 해버림

<클래스, PK>

큰그림 그렸습니다.

단축키 : 현재 클래스 돌리기

공통 인터페이스 분석

공통 인터페이스 분석

공통은 commons
jpa,redis,mongo

현실적인 평가는
어차피 자주쓰는거 함수들과 어차피 바뀌지 않는 DB 의 조합..

상상할수 있는 공통기능이 들어있음.

이름으로 찾고 싶어요.
특화 기능 입니다.

구현해 볼게요.

인터페이스 상속의 문제

구현하지 않아도 동작합니다.

쿼리 메소드 기능

메소드 이름으로 쿼리 생성

쿼리 메소드 기능

쿼리 메소드

기가막힙니다.

public List<Member> findByUsernameAndAgeGreaterThen(String username, int age){
return em.createQuery(
"select m from Member m" +
" where m.username = :username" +
" and m.age > : age")
.setParameter("username", username)
.setParameter("age", age)
.getResultList();
}

단축키 : 이전파일

@Test
public void findByUsernameAndAgeGreaterThen(){
Member m1 = new Member("AAA", 10);
Member m2 = new Member("AAA", 20);

memberJpaRepository.save(m1);
memberJpaRepository.save(m2);

memberJpaRepository.findByUsernameAndAgeGreaterThen("AAA", 15);
}
public Member(String username, int age) {
this.username = username;
this.age = age;
}
assertThat(result.get(0).getUsername()).isEqualTo("AAA");
assertThat(result.get(0).getAge()).isEqualTo(20);
assertThat(result.size()).isEqualTo(1);

잘 되는거에요. = 내가 귀찮은거에요.

상용버전

public void findByUsernameAndAgeGreaterThan(){
Member m1 = new Member("AAA", 10);
Member m2 = new Member("AAA", 20);

memberRepository.save(m1);
memberRepository.save(m2);

List<Member> result = memberRepository.findByUsernameAndAgeGreaterThan("AAA", 15);
assertThat(result.get(0).getUsername()).isEqualTo("AAA");
assertThat(result.get(0).getAge()).isEqualTo(20);
assertThat(result.size()).isEqualTo(1);
}

Did you mean username?

스프링 데이터 메뉴얼

개인적으로 2개정도 까지는 쓴다.

컨디션 안넣으면 전체 조회

find별명ByuserName.. 이렇게 식별하수 있다.

find별명2ByuserName..

Count, Exists, Delete, Distinct, limit

짧은 쿼리들

컴파일에 또 오류를 검출할수 있다.

사람 처럼 말해줌

JPA NamedQuery

네임드 쿼리

편하게 들으시면 됩니다.
실무에서 안씀.

기차나

@Query(name="Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);

관례가 있어서 제네레이트함.

다음 기능이 너무 막강해

런타임 디버깅

@Query, 리포지토리 메소드에 쿼리 정의하기

@Query 실무에서 많이 씀

@Query("select m from Member m where m.username = :username and m.age = :age")
List<Member> findUser(@Param("username") String username, @Param("age") int age);

이 기능이 좋은 이유

런타임 디버깅

간단 = 이름으로 해결

복잡 = @Query로 해결

동적 쿼리는 queryDsl

@Query, 값, DTO 조회하기

@Query 값, DTO

실무 테스트 케이스에서는 assert로 해야됨.

@Query("select m.username from Member m")
List<String> findUsernameList();

Dto에 @Data 쓰지마 getter, setter 다들어가 있음.

@Query("select new study.datajpa.dto.MemberDto(m.id, m.username, t.name) from Member m join m.team t")
List<MemberDto> findMemberDto();

롬복 toString

Dto 를 쓸때 주로 많이 씀.

파라미터 바인딩

파라미터 바인딩

이름 기반으로 짜

@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);

실무에서 많이 씁니다.

반환 타입

유연한 반환타입 지원

List<Member> findListByUsername(String username); //컬렉션
Member findMemberByUsername(String username); //단건
Optional<Member> findOptionalByUsername(String username); // 단건 optional
Optional<Member> aaa = memberRepository.findOptionalByUsername("AAA");
System.out.println("aaa = " + aaa.get());

컬렉션 조회 = 데이터가 없을수 있음

없으면 빈 컬렉션을 반환함

안좋은 코드 = 큰일납니다.

if(result != null){        }

단건일때 결과가 없음. = NULL

SpringDataJPA 는 개발자가 불편할까봐 try catch로 감싼다음 NULL 반환

JPA는 NoResultException

더 좋은건 Optional

result = Optional.empty

Optional<Member> result = memberRepository.findOptionalByUsername("AAAB");
System.out.println("result = " + result.orElse());

1개 나와야 되는데 2개가 나오면 = 예외가 터집니다.

IncorrectResultSizeDataAccessException: query did not return a unique result: 2;

NonUniqueResultException 을 바꿔 준거임

page<T>

순수 JPA 페이징과 정렬

public List<Member> findByPage(int age, int offset, int limit){
return em.createQuery(" select m from Member m where m.age = :age order by m.username desc")
.setParameter("age", age)
.setFirstResult(offset)
.setMaxResults(limit)
.getResultList();
}
public long totalCount(int age){
return em.createQuery("select count(m) from Member m where m.age = :age", Long.class)
.setParameter("age", age)
.getSingleResult();
}
memberJpaRepository.save(new Member("member1", 10));
memberJpaRepository.save(new Member("member2", 10));
memberJpaRepository.save(new Member("member3", 10));
memberJpaRepository.save(new Member("member4", 10));
memberJpaRepository.save(new Member("member5", 10));

int age = 10;
int offset = 0;
int limit = 3;

//when
List<Member> members = memberJpaRepository.findByPage(age, offset, limit);
long totalCount = memberJpaRepository.totalCount(age);

좋은게 있습니다.

스프링 데이터 JPA 페이징과 정렬

아주 기가 막힙니다.

요즘 더보기 페이징 = Slice

단축키 : 

Pageable = 페이지의 상태

Page = 반환 타입

PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC, "username"));
Page<Member> findByAge(int age, Pageable pageable);

total count 가져올 필요가 없음.

조회 쿼리

디버깅 : Repository 쪽 pageable 참조가 문제가 되서 호출 부분 에서도 캐스팅이 안되는 문제가 발생.
양쪽을다 맞춰주자.

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

count 쿼리

assertThat(content.size()).isEqualTo(3);
assertThat(page.getTotalElements()).isEqualTo(5);
assertThat(page.getNumber()).isEqualTo(0);
assertThat(page.getTotalPages()).isEqualTo(2);
assertThat(page.isFirst()).isTrue();
assertThat(page.hasNext()).isTrue();

정리

인터페이스

Slice 무한 페이징

내 주문 0번째 에서 3개

왜 total count를 부를까요.

주의할점

여기는

Page<Member> findByAge(int age, Pageable pageable);

얘는 슬라이스

//when
Slice<Member> page = memberRepository.findByAge(age, pageRequest);

count 쿼리가 없음.

내 주문은 3개

리미티는 4개

모바일 리스팅 기법

보통의 경우 " 아 안되요"

여기는 반환 타입 바꾸거나 

같은 코드인데 반환 타입만 다른 api를 추가하거나.

페이징 쿼리를 안쓰는 이유

totalCount가 데이터 많아질수록 최적화가 힘듬
잘짜야 될때가 있음.

LEFT OUTER를 할때 카운트는 조인을 할 필요가 없음.

@Query(value = "select m from Member m left join m.team t",
countQuery = "select count(m.username) from Member m")
List<Member> findByAge(int age, Pageable pageable);

실무썰

이제는 집중해야되는 쿼리만 짜면됨

Sorting 도 복잡해지면 저걸로 안됨.

소팅 정보도 넣을수 있음.

실무 꿀팁 - 활용편 2탄

그대로 반환하면 큰일남.

왜냐면 엔티티라는건 절대 노출하면 안됨

쉽게 DTO로 변환하는 방법

Page<MemberDto> map = page.map(m -> new MemberDto(m.getId(), m.getUsername(), null));

뒤에 강의에 있습니다.

퇴근 쌉가능

너무 편해져서 감회가 있다.

정리

주의 페이지가 0부터 시작한다!

벌크성 수정 쿼리

벌크성 수정 쿼리

"모든 직원에 연봉을 10% 인상해"

코딩

회원의 나이를 한번에 변경하는 쿼리 JPA

응답 값의 개수가 나옴

executeUpdate

단축키 : 인라인


public void bulkUpdate(){
memberJpaRepository.save(new Member("member1", 10));
memberJpaRepository.save(new Member("member2", 19));
memberJpaRepository.save(new Member("member3", 20));
memberJpaRepository.save(new Member("member4", 21));
memberJpaRepository.save(new Member("member5", 40));

//when
int resultCount = memberJpaRepository.bulkAgePlus(20);
//then
assertThat(resultCount).isEqualTo(3);
}

단축키 : 지금 코드블럭 돌리기

update member set age=age+1 where age>=20;

반환타입 맞춰야됨.

@Query("update Member m set m.age = m.age + 1 where m.age >= : age")
int bulkAgePlus(@Param("age") int age);

@Modifying = 변경하는구나 = 함수,결과값 다름을 표시

디버그 : ": age" -> ":age" 한칸

@Query 빼면 어노테이션 하라고 에러뜸.
Not supported for DML operations [update study.datajpa.

이게 되게 쉬운데.. 

문제가 있어요.

조심해야할 부분이 있다.
영속성

em.flush();
em.clear();

save 같은 로직 때도 영속성 초기화를 한다.

영속성 옵션이 있다.

@Modifying(clearAutomatically = true)

@EntityGraph

@EntityGraph

//given
//member1->teamA
//member2->teamB

Team teamA = new Team("teamA");
Team teamB = new Team("teamB");
teamRepository.save(teamA);
teamRepository.save(teamB);

Member member1 = new Member("member1", 10, teamA);
Member member2 = new Member("member2", 10, teamB);
memberRepository.save(member1);
memberRepository.save(member2);
em.flush();
em.clear();

//when
List<Member> members = memberRepository.findAll();

for (Member member : members) {
System.out.println("member = " + member.getUsername());
}

@엔티티그래프로 패치 조인을 이용할수 있다.

for (Member member : members) {
System.out.println("member = " + member.getUsername());
System.out.println("member.team = " + member.getTeam().getName());
}

프록시 복습

System.out.println("member.teamClass = " + member.getClass());

fetch join 설명

@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();

한번에 다긁어옴

    select
        member0_.member_id as member_i1_0_0_,
        team1_.team_id as team_id1_1_1_,
        member0_.age as age2_0_0_,
        member0_.team_id as team_id4_0_0_,
       member0_.username as username3_0_0_,
        team1_.name as name2_1_1_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id

member.teamClass = class study.datajpa.entity.Member

LAZY 때문에 프록시로 만들고 나중에 가져오다가

한번에 긁어옴

쿼리쓰기 귀찮은데

그래서 나온게 @엔티티그래프

findAll은 상위 함수.

@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();

member = member1

member.teamClass = class study.datajpa.entity.Member

member.team = teamA

member = member2

member.teamClass = class study.datajpa.entity.Member

member.team = teamB

패치 조인만 추가 해도됨.

@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findAll();

 

회원만 있으면 가져올 필요가 없다.

그러나 얽혀 있는것이 너무 많다.

같은 이름 변경

@EntityGraph 는 패치조인을 편하게 사용할수 있다.

JPA 표준 스팩

Named = 거의 안씀

@NamedEntityGraph(name="Member.all", attributeNodes = @NamedAttributeNode("team"))
public class Member {
@EntityGraph("Member.all")
List<Member> findEntityGraphByUsername(@Param("username") String username);

간단하게 쓸때는 이렇게

아니면 JPQL 에서 패치 조인을 쓴다.

JPA Hint & Lock

하이버네이트 에게 알려주는 힌트.

우리가 아는 그 힌트가 아니다.

ReadyOnly 쿼리 기능

@Test
public void queryHint(){
memberRepository.save(new Member("member1", 10));
em.flush();
em.clear();
}

결과 동기화

em.flush();

영속성 컨텐스트 날리기

em.clear();

변경 감지의 치명적인 단점
= 원본이 있어야된다.

변경 안할꺼야 = 메모리 안쓸꺼야

@QueryHints(value=@QueryHint(name="org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String username);

변경 감지 체크를 안함.

삼천포

다 ReadOnly 로 해야지 = 성능 향상 거의 없음

그냥 레디스 쓰세요.

ROCK

//select for update

JPA 기능 : 비관적인 락

import javax.persistence.LockModeType;
@Lock(LockModeType.PESSIMISTIC_READ)
List<Member> findLockByUsername(String username);
List<Member> result = memberRepository.findLockByUsername("member1");

member0_.username=? for update

짤막 광고

확장 기능

사용자 정의 리포지토리 구현

사용자 정의 리포지토리

우리가 봣던 모든 인터페이스 구현 = 현실적이지 않음.

마이바티스 기능을 활용한다거나.

실무에서 굉장히 많이 씀
특히 QueryDsl

@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery(
"select m from Member m")
.getResultList();
}
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom 

스프링 Data JPA가 엮어서 해주는것

@Test
public void callCustom(){
List<Member> memberCustom = memberRepository.findMemberCustom();
}

쿼리 나옴

이름을 맞춰야됨 
MemberRepository + Impl

바꿀수 있는데 하지마

핵심 비즈니스 로직 쿼리랑 화면 쿼리랑 분리할 필요가 있다.

멀리간 이야기긴한데 망하는 지름길이 될수도 잇다.

핵심 비즈니스 리파지토리 클래스가 다름

//분리된 쿼리
@Autowired MemberQueryRepository memberQueryRepository;

Auditing

JPA-Auditing

기본적으로 테이블을 만들때 남기는것들
등록일, 수정일, 등록자, 수정자

실무에서 많이 사용합니다.

실수로 바꿔도 디비에 적용 안됨.

@Column(updatable = false)

persist 하기전에 이벤트 발생

@PrePersist

this는 중요해서 강조할때.

쿼리 할때 null이 있으면 지저분해짐

public class Member extends JpaBaseEntity

create table member에 없음.

JPA 에는

진짜 상속(기본편 상속 강의)과

속성만 상속이 있다.

@MappedSuperclass
Member member = new Member("member1");
memberRepository.save(member); //@PrePersist

테스트에 슬립 안좋은 코드

Thread.sleep(100);
member.setUsername("member2");

em.flush(); //@PreUpdate
em.clear();

//when
Member findMember = memberRepository.findById((member.getId())).get();

//then
System.out.println("findMember = " + findMember.getCreatedDate());
System.out.println("findMember = " + findMember.getUpdatedDate());

디비 확인

모든 등록일 수정일의 자동화

스프링 JPA DATA

@EnableJpaAuditing

설정 필요

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;

@LastModifiedDate
private LocalDateTime lastModifiedDate;
}

등록자 수정자

@CreatedBy
@Column(updatable = false)
private String createBy;

@LastModifiedBy
private String lastModifiedBy;
@Bean
public AuditorAware<String> auditorProvider(){
// 스프링 시큐리티 자리
return ()-> Optional.of(UUID.randomUUID().toString());
}

UUID 사용

Optional.of(UUID.randomUUID().toString());

 

실제는 스프링 시큐리티 쓰시면 홀드 세션 정보와서 꺼내 와서

전체적용 XML도 있다.

저 같은 경우에는

테이블마다 특성이 달라서 필요할때도 있고 아닐때도 있다.

(부)베이스타임 - (모)베이스엔티티

Web 확장 - 도메인 클래스 컨버터

Web 확장 도메인 클래스 컨버터

잘보세요

@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Long id){
Member member = memberRepository.findById(id).get();
return member.getUsername();
}

@GetMapping("/members2/{id}")
public String findMember(@PathVariable("id") Member member){
return member.getUsername();
}

개인적으로 권장하지 않음.

왜냐면 Long 으로 막 접근하는것 찾는것도 쉽지 않다.

!주의 조회용

Web 확장 - 페이징과 정렬

(필수)Web 확장 - 페이징과 정렬

글로벌 설정

data:
web:
pageable:
default-page-size: 10
max-page-size: 2000

컨트롤러에만 설정하고 싶어.

count(member0_.member_id) as col_0_0_ 

엔티티 노출하지마세요.

@GetMapping("/members")
public Page<MemberDto> list(@PageableDefault(size=5, sort="username")Pageable pegeable){
Page<Member> page = memberRepository.findAll(pegeable);
Page<MemberDto> map = page.map(m -> new MemberDto(m.getId(), m.getUsername(), null));
return map;
}

단축키 : 인라인

꿀팁

생성자로 들어가야된다.

단축키 : 부가기능(람다-메소드레퍼런스)

반환 데이터

페이지를 1부터 시작하고 싶어

궁극적인 해결 방법:
pageable 말고 임의의 객체를 사용.
귀찬아

2번 좀 한계가 있다.

one-indexed-parameters: true

디버깅 = this 잘쓰자.

뭔가 되는거 같은데 여기서 막혀요.

밑에 데이터가 안맞음

스프링 데이터 JPA 분석

스프링 데이터 JPA 구현체 분석

소리큼

스프링 데이터 JPA 구현체 분석 

기능 : 내부 확인

2가지 의미

1. Bean
2. 영속성 예외 처리 (JDBC, JPA => SPRING)

스프링 Data Jpa 는 일단 트랜잭션 걸고 본다.

정리

(중요)save 쓰지마시고 변경감지 쓰세요.

Merge = 교체 = DB 셀렉트 1회 추가

새로운 엔티티를 구별하는 방법

새로운 엔티티를 구별하는 방법

전략

기능: 

디버그

단축키 : 이전 테스트
shift+F9

primitiv

단축키 : 디버깅 넘김 F8

@SpringBootTest
class ItemRepositoryTest {

@Autowired ItemRepository itemRepository;

@Test
public void save(){
Item item = new Item("UUID");
itemRepository.save(item);
}

}
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {

@Id
private String id;

public Item(String id) {
this.id = id;
}

public String getId() {
return id;
}
}

PK에 값이 있다. = persist 호출 안됨.

DB에 없는거 확인
select item0_.id as id1_0_0_ from item item0_ where item0_.id='UUID';

그리고 넣는다.
insert into item (id) values ('UUID'); 

이래서 Merge를 수정으로 쓰면 안된다.

강사님은 업무에 깔고 들어간다.

ID를 생성해야 할때가 있다. = 데이터 많을때

Persistable

@Entity
@EntityListeners(AuditingEntityListener.class)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable {

@Id
private String id;

@CreatedDate
private LocalDateTime createDate;

public Item(String id) {
this.id = id;
}

public String getId() {
return id;
}

@Override
public boolean isNew() {
return createDate == null;
}
}

실무 사용법 - 많이 써요

(중요)persist 되기전에 createdDate가 호출 

@Override
public boolean isNew() {
return createDate == null;
}

ㅋㅋ

디버깅: 

@EntityListeners(AuditingEntityListener.class)

 정리

나머지 기능들

네이티브 쿼리

왠만하면 쓰지 마세요.

@Query(value="select * from member where username?", nativeQuery = true)
Member findByNativeQuery(String username);

디버그 : username? -> username = ?

한계가 너무 많아요.

엔티티 가져오기도 난감함

Jdbc 탬플릿이나 마이바티스

Sort가 이상함

@Query(value="select m.member_id as id, m.userName, t.name as teamName" +
" from member m left join team t",
countQuery = "select count(*) from member",
nativeQuery = true)
Page<MemberProjection> findByNativeProjection(Pageable pageable);
Page<MemberProjection> result = memberRepository.findByNativeProjection(PageRequest.of(1, 10));

List<MemberProjection> content = result.getContent();
for (MemberProjection memberProjection : content) {
System.out.println("memberProjection.getUsername() = " + memberProjection.getUsername());
System.out.println("memberProjection.getTeamname() = " + memberProjection.getTeamname());
}

디버그 : 페이지는 0부터 시작

값 나옴

정적 쿼리 좀 쓸때는 쓸수도 있을듯

정리

진짜 복잡한건 하둡에서 가져옴