JPA는 기존의 반복 코드는 물론이고,
기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
→ 개발 생산성을 크게 높일 수 있다.
JPA를 사용하면,
SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있다.
JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
먼저,
JPA를 사용하려면 build.gradle에서
implementation 'org.springframework.boot:spring-boot-starter-data-jpa’
라는 것을 추가해 주어야 한다.
전에 만들어두었던
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc’
는 없애고 추가해 주면 된다.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa’
가 JPA뿐만 아니라 jdbc까지 포함하기 때문이다.
그리고 gradle refresh를 해준다.(코끼리 모양 버튼)
resources - application.properties에 JPA와 관련된 설정을 추가해 주어야 한다.
spring.jpa.show-sql=true
→ JPA에서 날리는 sql을 모두 볼 수가 있다.
spring.jpa.hibernate.ddl-auto=none
→ JPA를 사용하면 회원 객체를 보고 table을 다 만들어준다.
그런데 이미 table이 만들어져 있고, 만들어진 table을 쓸 것이기 때문에
auto = none (자동 생성 기능을 끔)으로 설정한다.
none말고 create를 하면 table을 모두 자동으로 만들어준다.
먼저 JPA를 쓰려면 entity를 mapping해야한다.
우선 jpa와
hibernate가 잘 설치되었는지 External Libraries에서 확인을 해준다.
JPA는 interface만 제공이 된다.
구현체로 hibernate, eclipse 등 여러 개가 있는데,
여기선 JPA interface의 hibernate만 거의 쓴다고 보면 된다.
JPA라는 것은 자바 진영의 표준 인터페이스이다.
구현은 여러 업체들이 한다.
JPA는 객체와 ORM(객체 object와, relational 데이터베이스 테이블을 mapping한다)이라는 기술이다.
mapping을 어떻게 하냐면, annotation으로 한다.
main - Member 클래스에
@Entity 라는 애노테이션을 치면, 이제부터 이것은 JPA가 관리하는 entity라고 표현한다.
그리고 PK를 mapping해줘야 한다.
지금 PK는 db에서 값을 생성해주고 있다.
query에 id를 넣는 것이 아니라,
db에 값을 넣으면 db가 id를 자동으로 생성해 주는 것을 identity라고 한다.
따라서
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
라고 적어줘야 한다.
oracle같은 경우는 시퀀스를 쓰기도 하고,
내가 직접 넣어줄 수도 있고 등등 여러 가지 방법이 있는데,
이렇게 db가 알아서 생성해 주는 것은 identity라고 하는 것이다.
만약 db의 column 명이 username이면,
@Column(name = "username")
이라고 해주면 된다.
하지만 현재는 name이기 때문에 그대로 두면 된다.
그러니까 이 애노테이션들을 가지고, db에 그대로 mapping이 되는 것이다.
이제 이 정보를 가지고 insert문, update문, select문 등등 모두 만들 수 있게 된다.
이제 repository를 하나 생성해보겠다.
main - repository에 JpaMemberRepository 를 생성하고,
MemberRepository를 implements 한다.
JPA는 entitymanager라는 걸로 모든 것이 동작한다.
따라서
private final EntityManager em(entity manager 줄인말);
을 해주고,
constructor을 추가해준다.
아까 build.gradle에서 data-jpa library를 받았던 것이 기억날 것이다.
이렇게 하면 스프링 부트가 자동으로 entitymanager를 생성해준다.
우리는 이렇게 만들어진 entitymanager을 주입받으면 된다.
그럼 이제 저장을 하려면
em.persist(member);
를 해주고,
return member;
를 해주면
JPA가 insert query를 다 만들어서 db에 집어넣고,
member에 setId까지 모두 해준다.
조회를 하려면,
@Overridepublic
Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.*ofNullable*(member);
}
em.find를 하고 조회 할 type과 식별자 pk값을 넣어주면 조회가 되는 것이다.
return을 할 땐,
Optional로 반환하기 때문에 null값일 수도 있어서
return Optional.ofNullable(member);
를 해준다.
findByName은
jpql이라는 객체지향 쿼리 sql을 써야한다.
차이가 어떻게 되냐면
findAll은
@Overridepublic
List<Member> findAll() {
List<Member> result = em.createQuery("select m from Member m", Member.class) .getResultList();return result;
}
을 해주면 되는데,
command + option + n(control + t 하고 선택)을 해주면
Inline Variable을 해준다.
→
@Overridepublic
List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
이게 jpql이라는 쿼리 언어이다.
보통 table 대상으로 sql을 날리는데, 이건 객체를 대상으로 쿼리를 날린다.
그럼 sql로 번역을 해준다.
Member entity를 대상으로 쿼리를 날리는데
select할 때 보통 sql은 m.id, m.name등등 다 적어야 했었다.
그런데 얘는 select m이 멤버 객체 자체를 조회한다는 뜻이다.
이렇게 mapping도 해줄 필요 없이 간단하게 끝낼 수가 있다.
mapping도 다 해주기 때문이다.
이제 남은 것은 findByName이다.
@Overridepublic
Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
을 해주면
저장과 조회가 완성이 된다.
저장, 조회, 업데이트, 삭제는 sql 짤 필요가 없다.
모두 자동으로 된다.
그러나 findByName이나 findAll과 같이 pk 기반이 아닌 나머지 것들은
jpql이라는 것을 작성해 주어야 한다.
이 다음시간에 배우겠지만,
이 JPA 기술을 spring에 감싸서 제공하는 기술이 있는데,
SpringDataJPA이다.
이것을 사용하면 findByName이나 findAll도 jpql로 작성하지 않아도 된다.
또한
JPA를 쓸 때 주의해야 할 점은
항상 @Transactional 이 있어야 한다는 점이다.
따라서
MemberService 클래스에 @Transactional 을 해주었다.
데이터를 저장하거나 변경할 땐 항상 @Transactional이 있어야 한다.
JPA는 join이 들어올 때 모든 데이터 변경이 모두 transaction으로 실행이 되어야 한다.
이제 실행을 해 보아야 한다.
그러려면 SpringConfig 클래스에 가서
public MemberRepository memberRepository에
return new JpaMemberRepository();
를 추가하고,
기존에 있던
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource){
this.dataSource = dataSource;
}
를 삭제하고,
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em){
this.em = em;
}
를 해준다.
이제 모든 구현이 끝났으니,
진짜 실행을 해 볼 차례이다.
test - MemberServiceIntegrationTest에 가서
회원가입만 살짝 돌려보았다.
잘 돌아가는 모습이 보인다.
@Commit도 해줘서 db에 반영이 되게 해 보았다.
저장한 spring이 잘 뜨는 것을 볼 수 있다.
name을 spring100으로 하고,
재실행한 후
다시 db에 들어가 보았더니,
추가한 spring100이 뜨는 것을 볼 수 있다.
확인 후
@commit은 다시 지워주었다.
db에서도 delete from member로 데이터를 모두 지워주었다.
그리고 나서
회원가입과 중복 회원 예외를 함께 실행해보았는데,
잘 돌아가는 모습을 볼 수 있었다.