개요

Spring-data-jpa를 사용하면 Entity Manager 를 사용하지 않고 쉽게 구현 가능하지만 가끔 Entity Manager로 직접 영속성 컨텍스트를 컨트롤 해야할 필요가 생긴다. 어떤식으로 둘을 함께 사용할 수 있는지 헤딩한 내용을 기록해본다.

  • 목차
    • Spring-data-jpa 와 jpa의 차이
    • 함께 사용해보자
    • 정리

 

Spring-data-jpa 와 jpa의 차이


Spring-data-jpa  JPA를 추상화 시켜둔 개념으로, 개발자가 EntityManager로 직접 접근 없이도 쉽게 개발 할 수 있도록 도와주는 모듈이다. Spring-data-jpa를 구성하는 내부에 Entity Manager가 구현되어 있어 사용자는 Repository Interface를 정의하는 것 만으로 JPA를 손쉽게 사용할 수 있다.

spring-data-jpa Repository


흔히 사용하는 spring-data-jpa Repository 예시

@Repository
public interface workRepository {
	int insertNtisNotice(FeedMessage feedMessage) throws Exception;
	List<FeedMessage> findByMessageLikeOrderByIDDesc(String message) throws Exception;
	int insertNtisNoticeAuto(FeedMessage feedMessage) throws Exception;
	int deleteNtisNoticeAutoCheck() throws Exception;
	List<AccountInfoDTO> selectAccountInfo(AccountInfoDTO accountInfoDTO) throws Exception;
	AccountInfoDTO findByManagerByID(int id) throws Exception;
}

interface 만으로 쿼리에 접근하도록 돕는다. 이것만으로도 반복에 가까운 자바 코드가 줄어든다.

하지만 뭔가 부족하다. 어쩌다 한 두번씩 EntityManager를 사용해야 할 때가 온다.. 그럼 그냥 함께 사용해보자.

 

함께 사용해보자


SpringBoot 프로젝트로 간단하게 구현해보자.

1. 프로젝트 생성하기

스프링부트 프로젝트 만들기 에서 프로젝트를 만들거나, 또는 하단 예제처럼 이클립스에서 New Spring Starter Project 로 프로젝트를 만든다.

설정은 크게 상관없다. Gradle 이나 자바 버전을 8로 선택한 건 단지 내가 익숙해서다.

간단하게 구현 테스트만 해 볼 것이기 때문에 메모리타입 DB를 지원하는 H2 DB와 편의성이 엄청난 Lombok, 당연히 Spring Data Jpa 와 웹서비스를 구현하는 것이므로 Spring Web도 선택한다.

자주 사용하는 항목에 저게 없으면 하단 검색창에서 검색하여 선택하면 된다.

사실 제대로 선택 안해도 상관없다. 프로젝트가 생성된 후 gradle 파일을 직접 수정하고 Refresh gradle project 해주면 제대로 반영된다.

finish 하면 프로젝트가 생성된다.

2. build.gradle 파일

plugins {
	id 'java'
	id 'org.springframework.boot' version '2.4.5'
	id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

예제를 따라 했다면 위 내용은 버젼만 좀 다르고 거의 같을 것이다.

3. H2 DB 설정하기

spring:
  datasource: 
    url: jdbc:h2:mem:demo #메모리 타입DB로 demo라는 DB에 접근한다는 뜻.
    username: sa          #사용자는 필수 입력사항.
    password:             #패스워드는 선택사항.
    driver-class-name: org.h2.Driver
  h2:
    console:
      enabled: true # H2 DB의 웹 콘솔 사용여부 설정. (http://localhost:8080/h2-console 로 접근)
  jpa:
    database: H2
    show-sql: true # jpa 에서 쿼리 출력여부 설정
    properties:
      hibernate:
        format_sql: true # 쿼리 출력시 정렬 여부 설정 

src/main/resource 경로에 application.yml 파일을 생성하고 위 내용을 입력한다. DB 콘솔 접근법은 하단에서 설명하겠다.

4. 패키지 생성, 소스 작성

위와 같이 패키지 혹은 폴더를 생성하고 다음 파일들을 작성한다. 핵심 내용만 보면

  • Member.java
    public class Member {
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name="ID")
      private long id;
    
      private String name;
    
      @Column(nullable=true)
      private Integer age;
    }
    
    간단하게 id와 이름, 나이 컬럼을 사용하려 한다. 실행시 테이블 자동생성을 위해 id에 GeneratedValue 어노테이션과 strategy를 설정해 준다.

 

  • MemberController.java
public class MemberController {
	@Autowired
    private MemberService memberService;

    // 멤버 등록 (spring-data-jpa)
    @PostMapping(value = "/member")
    public String createMember(@RequestBody Member member){
        return memberService.createMember(member);
    }

    // 멤버 등록 (jpa EntityManager)
    @PostMapping(value = "/memberjpa")
    public String createMemberJpa(@RequestBody Member member){
        return memberService.createMemberJpa(member);
    }
	
	// 멤버 조회
    @GetMapping(value = "/member")
    public List<Member> getMember(@RequestParam(required = false, defaultValue = "") String name){
        return memberService.getMemberList(name);
    }
}

두 가지 방식의 멤버 등록을 위한 메써드를 준비하고, 등록이 잘 되었는지 확인할 조회 메서드를 하나 만든다.

 

  • MemberService.java
public class MemberService {

	@Autowired
    private MemberRepository memberRepository;

    // spring-data-jpa
    public String createMember(Member member){
    	memberRepository.save(member); // 사용자 등록 처리
        return "success";
    }

    // jpa
    public String createMemberJpa(Member member){
    	log.info("member name : " + member.getName());
    	memberRepository.createUserAuto(member);
    	return "jpa success";
	}

    public List<Member> getMemberList(String name){
        if(name.isEmpty()) {
            return memberRepository.findAll();
        } else {
        	return memberRepository.findByNameLikeOrderByName(name);
        }
    }
}

컨트롤러와 같은 목적으로 생성한다.

 

  • MemberRepository.java

Repository는 우선 spring-data-jpa를 위해 기본적인 JpaRepository를 상속하는 interface를 만들어준다.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
	List<Member> findByNameLikeOrderByName(String name);
}

이제 JPA를 만들어본다. JpaRepository는 EntityManager에 직접 접근할 수 없으므로 우회로를 만들어주자.

추후 MemberRepository에 상속하기 위한 interface repository를 만드는데 service에서 jpa용으로 만들어 둔 함수를 사용한다.

public interface MemberCustomReposity {
	String createUserAuto(Member member);
}

그리고 해당 인터페이스를 구현할 MemberCustomReposityImpl 파일도 만든다.

@Transactional(readOnly = true)
public class MemberCustomReposityImpl implements MemberCustomReposity {
	@PersistenceContext	// 영속성 컨택스트를 spring-data-jpa 에서 사용하기 위한 어노테이션
	EntityManager em;

	@Override
	@Transactional
	public String createUserAuto(Member member){
		log.info(member.getName());
		em.persist(member);

		Member member2 = em.find(Member.class, member.getId());
		log.info(member2.getName() + " / " + member2.getId());

		return "실행 완료";
	}
}

아주 단순하게 넘어온 멤버객체 이름을 출력하고, 등록된 값의 id로 데이터를 찾아 정보를 출력해 보았다.

@PersistenceContext를 통해 EntityManager를 가져다 사용하고, 클래스 단의 트랜잭션은 readonly 상태로, 트랜잭선 처리가 필요한 메서드는 @Transactional 어노테이션을 추가해야 EntityManager가 1차캐쉬를 플러쉬 하면서 처리할 수 있다.

여기까지 작성한 내용을 MemberRepository에 추가해 주자.

@Repository
public interface MemberRepository extends JpaRepository<Member, Long>, MemberCustomReposity {
	List<Member> findByNameLikeOrderByName(String name);
}

말은 길었지만 정작 코드는 참 쉽다. 이제 서버를 구동하고 테스트를 해보자.

 

5. 테스트

컨트롤러에 작성한 접근 방식을 이용하여 하나씩 테스트해보자.
먼저 spring-data-jpa 로 등록.

success 응답이 왔다.
다음 JPA EntityManager로 등록.


콘솔 로그도 정상 확인.


제대로 등록 되었는지 조회해보자.

제대로 등록되어 있다.

DB에 직접 붙어서 확인하고 싶다면,

http://localhost:8080/h2-console 로 접속한다.

이와같은 화면을 볼 수 있는데 이것이 h2 DB console 이다. 다른건 그대로 두고, JDBC URL 만 본인 설정에 맞게 변경한다.

연결 테스트 해보고 접속해보자.

그럼 SQL을 직접 작성해 볼 수 있는 창이 보인다. 마음껏 쿼리를 날려보자.

 

정리


  • spring-data-jpa를 기반으로 EntityManager를 위한 repository 를 따로 구현한다.
  • EntityManager를 사용하기 위해 @PersistenceContext 어노테이션을 이용한다.
  • JPA는 트랜잭션 기반으로 동작하기 때문에 EntityManager 설정 시 트랜잭션 설정을 해준다. 정도가 되겠다.

해당 내용의 소스 파일은 Github 에 올려두었다.

하나하나 헤딩하는 과정이 힘들지만 모레알처럼 작은 지식들 하나하나가 쌓여가고 있다고 행복회로를 돌려본다.

+ Recent posts