[JPA] EntityManager (활용)

LDB·2024년 11월 27일
0

JPA 기본

목록 보기
6/10
post-thumbnail

EntityManager

EntityManager는 EntityManager (이론) 게시글에서도 설명했지만 EntityManager는 DB와 연관있는 Entity 객체와 영속성 컨텍스트끼리 상호작용하기 위해 만들어진 인터페이스이다.

@Query 어노테이션 게시글을 작성할 때 사용한 예시문 일부를 그대로 사용했습니다.
https://velog.io/@half-phycho/JPA-Query-어노테이션


EntityManager 사용준비

(해당 예시는 Spring Boot로 작성했고 빌드도구는 gradle로 했다.)

build.gradle 파일

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    
    runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
    
    annotationProcessor 'org.projectlombok:lombok'
}

application.properties 파일

spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=

entity 파일

@Entity
@Getter
@NoArgsConstructor
@Table(name="member")
public class Member{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int seq;
    private String name;
    private int age;
    
    @Builder
    public Member(String name, int age){
        this.name = name;
        this.age = age;
    }
    
    // 이 메서드는 업데이트용으로 만든 메서드이다.
    public void updtMember(int seq, String name, int age){
        this.seq = seq;
        this.name = name;
        this.age = age;
    }
}

(의존성 추가에 spring-data-jpa가 포함이 안 된 이유는 entityManager만 사용한다면 굳이 필요없기 때문이다.)

사용예시

repository등록 및 EntityManager등록

// repository class

import org.springframework.stereotype.Repository;

import jakarta.persistence.EntityManager;

@Repository
public class memberRepository{
	
    @Autowired
    private EntityManager em;
}

기본적으로 Spring Boot로 EntityManager를 사용한다면 EntityManagerFactory를 생성 할 필요가 없다.

엔터티 관리 메서드

  • persist(Object entity): 엔티티를 영속화(저장)
  • merge(Object entity): 엔티티를 병합(영속 상태의 엔티티를 업데이트할 때 사용)
  • remove(Object entity): 엔티티를 삭제
  • find(Class entityClass, Object primaryKey): 엔티티를 조회
  • getReference(Class entityClass, Object primaryKey): 엔티티의 프록시 객체를 조회
@Repository
public class memberRepository{
  
	@Autowired
    private EntityManager em;
  
	@Transactional
    // 영속성 컨텍스트에 insert쿼리를 저장하는 메서드이다.
    public void mamberSave() { 
        
        Member member = Member.builder()
                            .age(20)
                            .name("tester")
                            .build();

        em.persist(member);
    }
  
	@Transactional
    // 영속성 컨텍스트에 update쿼리를 저장하는 메서드이다.
    public void memberUpdate(int id) {
        Member member = em.find(Member.class, id);
        if (member != null) { // 결과가 있는경우 업데이트
            member.updtMember(member.getSeq(), "tester1", 111);        

            em.merge(member);
        }
    }
  
  	@Transactional
  	// 영속성 컨텍스트에 delete쿼리를 저장하는 메서드이다.
    public void memeberDelete(int id) {
        Member member = em.find(Member.class, id);
        if (member != null) {
            em.remove(member);
        }
    }
}
  

생성, 수정, 삭제는 이렇게 하면 되지만 find는 위의 코드에서 나타낸대로 @Id 어노테이션에 해당하는 id값을 넣어야한다.

find와 getReference메서드 비교

특징findgetReference
결과값엔티티 객체 반환프록시 객체 반환
조회시점즉시조회프록시 객체를 사용할 때 조회
데이터 없는 경우null 반환EntityNotFoundException 예외 발생
사용목적즉시 데이터가 필요한 경우지연 로딩으로 성능 최적화가 필요한 경우
@Transactional
public void memberGetReference(int id) {
	Member member = em.find(Member.class, id); // member db 조회시점
    System.out.println("Member class: " + member.getClass());
    System.out.println("Member name: " + member.getName());

    //em.clear();

    Member memberProxy = em.getReference(Member.class, member.getSeq());
    System.out.println("Member proxy class: " + memberProxy.getClass());
    System.out.println("Member proxy name: " + memberProxy.getName()); // memberProxy db 조회시점
}  

위의 예시에서 clear메서드를 사용했었는데 clear를 사용하지 않고 find를 사용한 후에 조회를하면 프록시 객체가 아닌 entity객체를 반환하고 clear를 한 후 조회하면 프록시 객체를 조회한다.

(clear를 한 후 조회한 경우) (clear 없이 조회한 경우)

정리하자면 결국 find와 getReference는 조회한다는 관점은 같지만 find는 메서드를 사용하면 바로 DB를 조회하고 getReference는 객체를 선언한 후 객체를 사용하는 경우에 DB를 조회한다는 차이점이 있다.


쿼리 생성 및 실행 메서드

  • createQuery(String jpql) : JPQL 쿼리 생성
  • createNamedQuery(String name) : entity객체에서 생성한 JPQL 쿼리 생성
  • createNativeQuery(String sql) : 네이티브 SQL 쿼리 생성


createQuery
createQuery메서드는 JPQL을 생성하는 가장 기본적인 방법이다, 예시로 select만 했지만 insert, update, delete도 가능하다.

@Repository
public class memberRepository{
	
  	@Autowired
    private EntityManager em;
  	
  	@Transactional
    public void createjpql() { // createQuery 방식
        String findQuery1 = "select m from Member as m"; // 방식 1
        String findQuery2 = "select m.seq, m.name, m.age from Member as m"; // 방식 2
        
        // 일반적인 조회방법
        List<Member> resultList = em.createQuery(findQuery1, Member.class).getResultList();

        //반환 타입이 Member 로 명확한 경우 TypedQuery 사용
        TypedQuery<Member> findsql = em.createQuery(findQuery2, Member.class);
        List<Member> typesqlList = findsql.getResultList();

        for(Member member : resultList){
            System.out.println(member.toString());
        }

        System.out.println("===================================");

        for(Member member : typesqlList){
            System.out.println(member.toString());
        }
    }
}

createNamedQuery
createNamedQuery는 entity객체에 미리 JPQL쿼리를 만들어두고 불러오는 방식이다.

// entity
  
@Entity
@Getter
@NoArgsConstructor
@NamedQuery(name = "Member.findMember", query="select m from Member m")
@NamedQueries({
    @NamedQuery(name = "Member.findMemberBySeq", query="select m from Member m where m.seq = :seq"),
    @NamedQuery(name = "Member.findMemberByAge", query="select m from Member m where m.age = :age")
})
@Table(name="member")
public class Member{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int seq;
    private String name;
    private int age;
    
    @Builder
    public Member(String name, int age){
        this.name = name;
        this.age = age;
    }
    
    // 이 메서드는 업데이트용으로 만든 메서드이다.
    public void updtMember(int seq, String name, int age){
        this.seq = seq;
        this.name = name;
        this.age = age;
    }
}

쿼리를 entity에 만들어두기 위해서는 @NamedQuery 어노테이션과 @NamedQueries을 사용하는데 단일쿼리는 @NamedQuery를 사용하지만 다중쿼리를 사용한다면 @NamedQueries를 활용해 묶어서 사용하면 된다.

그리고 이름을 Member.* 방식으로 쿼리이름을 작성했지만 단순히 Member빼고 findMember로 작성해도 문제없다.

(@NamedQueries로 안묶고 @NamedQuery 3개를 사용해도 정상적으로 동작한다.)

// repository
  
@Repository
public class memberRepository{
	
  	@Autowired
    private EntityManager em;
  	
  	@Transactional
    public void createnamedsql() { // createNamedQuery 방식
        List<Member> findMember = em.createNamedQuery("Member.findMember", Member.class)
              .getResultList();
        
        List<Member> findMemberBySeq = em.createNamedQuery("Member.findMemberBySeq", Member.class)
              .setParameter("seq", {seq})
              .getResultList();
		
        List<Member> findMemberByAge = em.createNamedQuery("Member.findMemberByAge", Member.class)
              .setParameter("age", {age})
              .getResultList();    
        
        for(Member member : findMember){
            System.out.println("findMember : "+member.toString());
        }

        for(Member member : findMemberBySeq){
            System.out.println("findMemberBySeq : "+member.toString());
        }

        for(Member member : findMemberByAge){
            System.out.println("findMemberByAge : "+member.toString());
        }
    }
}

createNativeQuery
createNativeQuery메서드는 기존의 JPQL이 아닌 SQL방식으로 작성하는 방법이다.

@Repository
public class memberRepository{
	
  	@Autowired
    private EntityManager em;  
  
  	@Transactional
    public void createsql() {
        String sqlQuery1 = "select * from Member"; // 방식 1
        String sqlQuery2 = "select seq, name, age from Member"; // 방식 2

        List<Member> resultList = em.createNativeQuery(sqlQuery1, Member.class).getResultList();
        Query findsql = em.createNativeQuery(sqlQuery2, Member.class);
        List<Member> typesqlList = findsql.getResultList();

        for(Member member : resultList){
            System.out.println(member.toString());
        }

        System.out.println("===================================");

        for(Member member : typesqlList){
            System.out.println(member.toString());
        }
    }
}

예시를 보면 createNativeQuery의 예제에 Query타입으로 리턴을 하는데 createQuery는 TypedQuery타입으로 리턴해야 한다.


참고 사이트

https://parkhyeokjin.github.io/jpa/2019/11/08/JPA-chap10.html

https://bnzn2426.tistory.com/143

https://lifedeveloper.tistory.com/entry/JPA%ED%94%84%EB%A1%9D%EC%8B%9C-getReference

profile
가끔은 정신줄 놓고 멍 때리는 것도 필요하다.

0개의 댓글

관련 채용 정보