[Spring Basic] JPA와 AOP
JDBC나 JPA로 스프링과 데이터베이스를 연결한 후, 테스트를 진행해보자.
@SpringBootTest
@Transactional
class MemberServiceIntTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@Test
public void 회원가입() throws Exception {
//Given
Member member = new Member();
member.setName("hello");
//When
Long saveId = memberService.join(member);
//Then
Member findMember = memberRepository.findById(saveId).get();
assertEquals(member.getName(), findMember.getName());
}
@Test
public void 중복_회원_예외() throws Exception {
//Given
Member member1 = new Member();
member1.setName("spring");
Member member2 = new Member();
member2.setName("spring");
//When
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class,
() -> memberService.join(member2));//예외가 발생해야 한다.
assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
}
}
@SpringBootTest 애너테이션은 스프링 컨테이너와 테스트를 함께 실행할 수 있도록 한다.
작은 단위의 Unit Test는 보통 스프링 컨테이너를 올리지 않고 진행하는데, 가끔씩 이렇게 스프링 컨테이너를 올려서 테스트를 진행하는 통합 테스트가 필요할 때가 있다.
Transactional 애너테이션은 테스트 시작 전에 트랜잭션을 먼저 시작하고 테스트가 끝난 후 데이터베이스를 롤백시킨다.
데이터를 남기지 않아 다음 테스트를 진행 할 때도 영향을 주지 않는다.
Jdbc를 사용해 데이터베이스를 연결한 경우 JdbcTemplate 라이브러리를 사용해 JDBC API에서 작성한 반복되는 코드들을 제거해 깔끔하게 사용할 수 있으니 참고하자. (SQL은 직접 작성해야 한다.)
JPA를 사용해서 DB를 관리하는 방법도 있다.
JdbcTemplet라이브러리가 제공하는 기능인 반복 코드의 제거는 물론, 기본적인 SQL문법도 JPA가 직접 만들어서 DB에 던져줄 수 있어 개발 생산성을 크게 높일 수 있다.
JPA는 인터페이스로 제공되고, 보통 그 구현체로 hibernate를 사용한다.
JPA는 객체와 Object Relational Mapping. 객체와 Relational Database를 매핑한다.
package start.startspring.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity 애너테이션으로 객체와 데이터베이스를 매핑해 Member객체를 JPA가 관리하는 엔티티로 등록한다.
쿼리에 값을 넣을 때 넣지 않아도 자동으로 생성되는 요소를 Identity라고 한다.
Id GeneratedValue 애너테이션으로 데이터베이스에서 Primary Key임을 명시해 주자.
위의 애너테이션들을 바탕으로 insert, update, delete... 등 여러 작업을 수행한다.
package start.startspring.repository;
import start.startspring.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
public Member save(Member member) {
em.persist(member);
return member;
}
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public 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();
}
}
JPA에서는 EntityManager를 통해 모든 작업을 수행한다.
스프링 부트는 현재 데이터베이스를 연결해 EntityManager를 만들어주고, DB와 통신을 처리한다.
저장할 때 em.persist(member)를 실행하면 JPA가 Insert쿼리문 싹다 만들어서 집어넣어주고, setID까지 처리해준다.
찾을 때는 엔티티를 대상으로 쿼리를 보내 SQL로 번역시킨다. (JPQL)
이 외에 저장하고 조회하고 업데이트 하는 기능은 SQL짤 필요 없이 알아서 해 준다. (pk 기반이 아닌 요소들은 JPQL을 작성해 줘야 한다.)
JPA를 사용해 데이터를 저장하거나 변경할 때는 Transactional로 등록해 줘야 하니 서비스 계층에 Transactional 애너테이션을 붙여주자. (JPA를 통해 데이터를 조작할 땐 꼭 Transaction안에서 처리돼야한다.)
@Configuration
public class SpringConfig {
private EntityManager em;
@Autowired
public SpringConfig(EntityManager em) {
this.em = em;
}
@Bean
public MemberService memberService(){
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository(){
return new JpaMemberRepository(em);
}
}
스프링 설정 파일에서 EntityManager를 주입받고 데이터베이스 연결 부분을 수정해서 완성하자.
스프링 부트와 JPA만 사용해도 생산성이 엄청나게 증가하는데, 여기서 끝이 아니고 스프링 데이터 JPA기술을 사용하게 되면 리포지토리에 구현 클래스 없이 인터페이스 만으로도 개발을 완료할 수 있게 되고, 지금까지 하나하나 작성했던 기본 CRUD 기능도 스프링 데이터 JPA가 처리하게 할 수 있다.
모든 SQL문을 자동화해서 제공하는건 아니다. 개발 시 복잡한 동적 쿼리를 만나게 되는 경우도 많을텐데, 이런 경우는 JPA가 제공하는 네이티브 쿼리를 사용하거나 JdbcTemplate를 사용하는 등 자동화 외의 방법으로 작성할 수 있다.
AOP
Asepect Oriented Programming 의 약자로 공통 관심사와 핵심 관심사를 분리하는 기술이다.
스프링에서는 AOP기술을 잘 적용할 수 있도록 도와주는 기능들이 많다.
@Component
@Aspect
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString()+ " " + timeMs +
"ms");
}
}
}
프로젝트의 모든 메서드에 동작 시간을 출력하는 기능을 추가하는 작업을 AOP로 처리해보자.
@Aspect 애너테이션으로 AOP를 사용함을 알려주고 @Around 애너테이션으로 해당 메서드를 어디에 적용할 지 작성한다.
적용 대상인 메서드가 호출될 때 AOP로 작성한 메서드가 걸려서 작동하는 로직인데, 조건에 따라 AOP메서드를 작동하지 않게 할 수도 있다.
AOP 서비스가 지정되면 스프링이 실행될 때 해당 요소를 스프링 컨테이너로 등록하기 전 프록시라고 불리는 가짜 memberService를 만든다.
만들어진 프록시는 실제 빈의 참조를 가지고 있고, AOP가 적용되는 메서드가 호출되면 프록시가 호출돼 프록시의 내부에서 실제 객체의 메서드가 호출되는 방식으로 작동한다.
AOP를 적절히 사용해 관심사를 분리해 변경과 유지 보수에 유리한 웹 페이지를 설계하자..
'Spring > Spring' 카테고리의 다른 글
[Spring Basic] Singleton (0) | 2022.08.07 |
---|---|
[Spring Basic] 스프링과 IoC (0) | 2022.08.06 |
[Spring Basic] 동기와 비동기 (0) | 2022.07.15 |
[Spring Basic] MyBatis (0) | 2022.07.12 |
[Spring Basic] DAO Repository DTO VO Entity (0) | 2022.07.09 |
댓글
이 글 공유하기
다른 글
-
[Spring Basic] Singleton
[Spring Basic] Singleton
2022.08.07 -
[Spring Basic] 스프링과 IoC
[Spring Basic] 스프링과 IoC
2022.08.06 -
[Spring Basic] 동기와 비동기
[Spring Basic] 동기와 비동기
2022.07.15 -
[Spring Basic] MyBatis
[Spring Basic] MyBatis
2022.07.12