EntityManager는 EntityManager (이론) 게시글에서도 설명했지만 EntityManager는 DB와 연관있는 Entity 객체와 영속성 컨텍스트끼리 상호작용하기 위해 만들어진 인터페이스이다.
@Query 어노테이션 게시글을 작성할 때 사용한 예시문 일부를 그대로 사용했습니다.
https://velog.io/@half-phycho/JPA-Query-어노테이션
(해당 예시는 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 class
import org.springframework.stereotype.Repository;
import jakarta.persistence.EntityManager;
@Repository
public class memberRepository{
@Autowired
private EntityManager em;
}
기본적으로 Spring Boot로 EntityManager를 사용한다면 EntityManagerFactory를 생성 할 필요가 없다.
@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메서드 비교
특징 | find | getReference |
---|---|---|
결과값 | 엔티티 객체 반환 | 프록시 객체 반환 |
조회시점 | 즉시조회 | 프록시 객체를 사용할 때 조회 |
데이터 없는 경우 | 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
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