application.properties보다 application.yml 을 더 선호하신다고.. application.properties를 지우고 만듦.
spring:
datasource:
url: jdbc:h2:tcp://localhost/./datajpa
username: sa
password: 1234
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
# show_sql: true
format_sql: true
logging.level:
org.hibernate.SQL: debug
# org.hibernate.type: trace
스프링의 데이터 소스는
url, username, password
그리고 db 종류
jpa에서는
ddl-auto옵션은 개발할 때 편리하게 바꾸면서 실험해 볼 수 있다. 당연히 운영환경에서 켜놓으면 안된다.
jpa의 속성, 하이버네이트는 show_sql은 말 그대로 sql 보여주는거, 즉 로그로 남겨서 보여준다.
format_sql은 그걸 sql의 format으로 맞춰서 보여주는 거고.
저기 show_sql은 주석으로 처리해놨는데, 이유가 콘솔로 남겨서 그럼.
그래서 아래
logging.level.org.hibernate.sql 보면 debug레벨까지,
즉 hibernate.sql을 debug레벨까지 로그를 본다는 거임.
그러면 하이버네이트가 사실 디버그 레벨에서는 나가는 쿼리까지 다 로그로 남기고 있었음.
org.hibernate.type은 그 sql 나가는데 나가는 파라미터까지 다 볼 수 있는거.
근데 p6spy라는 더 깔끔히 보여주는 걸 쓸거라 주석.
쿼리 나가는 거 보고 싶으면
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.0'
이거 종속성 추가 해 주면 됨. 설치만 해도 알아서 읽어서 보여줌
먼저 JPA로 한번 간단하게 만들어 봄
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
private Long id;
protected Member() {
}
public Member(String username) {
this.username = username;
}
private String username;
}
엔티티
@Repository
@RequiredArgsConstructor
public class MemberJpaRepository {
private final EntityManager em;
public Member save(Member member){
em.persist(member);
return member;
}
public Member find(Long id){
return em.find(Member.class, id);
}
}
리포지토리
@SpringBootTest
@Transactional
class MemberJpaRepositoryTest {
@Autowired MemberJpaRepository memberJpaRepository;
@Test
public void testMember(){
Member member = new Member("memberA");
memberJpaRepository.save(member);
Member findMember = memberJpaRepository.find(member.getId());
assertThat(member.getId()).isEqualTo(findMember.getId());
assertThat(member.getUsername()).isEqualTo(findMember.getUsername());
assertThat(member).isEqualTo(findMember);
}
}
그냥 save 해 보고 찾아서 같은 지 비교.
save 할 때 알아서 id도 넣어주고(영속성 컨텍스트에서)
그래서 id로 엔티티매니저로 찾아봐서 얻어오고,
둘이 같은 지 비교
같은 트랜잭션이므로, 같은 영속성 컨텍스트에서는 ==을 보장함.
영속성 컨텍스트에 있는 레퍼런스를 주는 거라서.
먼저 테스트 하기 위해 간단히 SpringDataJPA 만들어 봄
public interface MemberRepository extends JpaRepository<Member, Long> {
}
끝임. 저 JpaRepository<대상엔티티, ID타입>가 SpringDataJPA인데,
이렇게 하면 알아서 구현체 만든 다음 빈으로 등록시켜 줌.
그래서, 그 구현체 사용 하려면,
@SpringBootTest
@Transactional
@Rollback(value = false)
class MemberRepositoryTest {
@Autowired MemberRepository memberRepository;
@Test
public void testMember(){
Member member = new Member("memberA");
Member savedMember = memberRepository.save(member);
Member findedMember = memberRepository.findById(member.getId()).get();
assertThat(member.getUsername()).isEqualTo(findedMember.getUsername());
assertThat(member.getId()).isEqualTo(findedMember.getId());
assertThat(member).isEqualTo(findedMember);
}
}
저렇게 그냥 주입 받아서 쓰면 됨.
테스트 내용은 똑같음.
참고로 테스트환경에서 @Transactional은 자동으로 롤백을 시켜줌. 그래야 테스트 여러번 진행하기 편하니까.
기본이 그거기 때문에 우리는 그냥 값 남나 보기 위해 롤백 꺼버림. 그럼 커밋함.
여튼, 보면 우리가 구현하지도 않은 save(), findById 등 사용할 수 있음.
이거는 앞으로 자세히 배우긴 할 테지만, 프리뷰에서도 말 했듯이,
저 JpaRepository가 상속받은 인터페이스들 중에(Crud말고도 정말 많은 기능들이 있음)
CrudRepository라는 것도 있는데,
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
이게 실제로 저 save() 타고 들어가서 본거임.
여튼 잘 됨.
SpringDataJPA가 알아서 저 JpaRepository 상속 받은 거 구현체로 잘 만들어서 빈으로 등록해주고 잘 사용됨.
'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글
5. 순수 JPA 리포지토리 (0) | 2023.11.18 |
---|---|
4. 예제 도메인 모델 구현 (0) | 2023.11.18 |
2. 프로젝트 생성 (0) | 2023.11.18 |
1. 스프링 데이터 JPA (0) | 2023.11.18 |
0. 프리뷰 (0) | 2023.11.17 |