개요
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; }
- 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 에 올려두었다.
하나하나 헤딩하는 과정이 힘들지만 모레알처럼 작은 지식들 하나하나가 쌓여가고 있다고 행복회로를 돌려본다.