./h2.sh : h2 데이터베이스 실행
application.yml
spring:
database:
url: jdbc:h2:tcp://localhost/~/jpashop;MVCC=TRUE // 여러개가 한번에 접근했을 때 빨리 처리할 수 있게 도와줌.
username: sa
password:
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
rg.hibernate.type: trace // 쿼리 파라미터를 로그에 남기는 것이 가능해짐.
만약, 장고에서 하는 것처럼 request로 데이터를 받는다고 가정해보면
request에 대한 모든 프로퍼티를 작성해줘야 하기 때문에 그 번거러움을 해소하기 위해서 커맨드 객체
가 존재한다.
커맨드 객체는 우리가 일반적으로 사용하는 getter/setter 메서드가 있는 VO라고 생각하면 되는데,
우리는 매개변수로 객체를 받을 수 있게 되고 객체의 생성자, 혹은 getter/setter메서드를 통해서 컨트롤러가 ObjectMapper를 통해 바인딩을 시키게 되는 것이다.
강의에서 "커맨드( 명령 )와 쿼리를 구분하는 것이 좋다!" 라고 하는데, 그게 무엇이냐
- 전통적 MVC에서는 하나의 모델이 DB 조회·갱신을 모두 담당했으나,
DB조회를 담당하는 쿼리 모델, DB갱신을 담당하는 커맨드 모델로 역할을 분리하는 것 !
➡️ 즉, 상태조회 쿼리(query), 상태변경 명령(command) 모델로 분리하는 패턴이다.
MemberRepository
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public Long save(Member member) {
em.persist(member);
return member.getId(); // 보통은 저장하고 나면 거의 리턴값을 만들지 않는다.
}
public Member find(Long id) {
return em.find(Member.class, id);
}
}
MemberRepositoryTest
@RunWith(SpringRunner.class)
@SpringBootTest
class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
@Transactional // 엔티티 매니저를 통한 모든 데이터 변경은 트랜젝션 안에서 이뤄져야 함 !!!
@Rollback(false) // Test에 있는 transaction은 실행이 끝나면 바로 롤백이 되기 때문에, Test 결과를 디비에 저장하려면 Rollback(false)를 해줘야 함
public void testMember() {
Member member = new Member();
member.setUsername("memberA");
Long savedId = memberRepository.save(member);
Member findMember = memberRepository.find(savedId);
Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
Assertions.assertThat(findMember).isEqualTo(member);
// 같은 트랜잭션 안에서 저장하고 조회하면 같은 엔티티로 취급한다. => 결과는 true !
// JPA 엔티티 동일성 보장
}
}