본문 바로가기
Java/ORM

JAVA ORM - JPA(proxy, loading)

by 티코딩 2024. 8. 6.

프록시란

정처기공부할때 디자인패턴에서 공부했던부분이다. 다른 객체에 대한 인터페이스 역할을 하는 객체를 말한다. 실제 객체에 대한 접근을 제어하거나 추가적인 기능을 제공한다. 객체의 구조를 변경하지 않고 기능을 확장하거나 제어할 수 있게 해준다.

역할로는

1. 접근제어 : 클라이언트가 실제 객체에 직접 접근하는것을 제어함. 권한이 있는 사용자만 접근하게 한다.

2. 성능향상 : 실제 객체에 대한 접근을 지연시켜서 시스템의 성능을 최적화 할 수 있다. 불필요한 객체 생성이나 DB접근을 피할 수 있다.

 

EntityManager의 두가지 메서드

1. em.find() : DB에서 실제 엔티티를 조회. 메서드가 실행될 때 즉시 쿼리를 실행한다.

즉시 로딩. 엔티티가 없을 때 null을 반환 한다. 

EntityManager em = entityManagerFactory.createEntityManager();
Product product = em.find(Product.class, productId);

if (product != null) {
    System.out.println("Product name: " + product.getName());
} else {
    System.out.println("Product not found.");
}

 

2. em.getReference() : DB조회를 미루는 프록시 엔티티 객체 조회. 프록시 객체를 반환하여 쿼리를 바로 실행하지않고 실제 쓰일때 쿼리를 실행하여 지연로딩을 수행한다.

지연 로딩. 엔티티가 없을 때 데이터 접근시점에 예외발생함.

EntityManager em = entityManagerFactory.createEntityManager();
Product productProxy = em.getReference(Product.class, productId);

// 데이터에 접근할 때 실제로 쿼리 실행
try {
    System.out.println("Product name: " + productProxy.getName());
} catch (EntityNotFoundException e) {
    System.out.println("Product not found.");
}

 

 

%주의%

프록시 객체는 처음 사용 할 때 한번만 초기화된다.

프록시 객체를 초기화 할 때, 프록시 객체가 실제 엔티티로 바뀌는게 아니다. 초기화 되면 프록시객체를 통해 실제 엔티티에 접근이 가능하다.

User user1 = new User();
user1.setName("thKim");
em.persist(user1);

em.flush();
em.clear();

User u1 = em.find(User.class, user1.getId());
System.out.println("u1 = " + u1.getClass());

User reference = em.getReference(User.class, user1.getId());
System.out.println("reference = " + reference.getClass());

 

실행결과 :

u1 = class jpa.User

reference = class jpa.User

왜이런 결과가 나올까? 영속성 컨텍스트에 찾는 엔티티가 있으면 em.getReference() 호출해도 실제 엔티티를 반환하기 때문이다.

 

 

지연로딩

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "product", fetch = FetchType.LAZY)
    private List<Order> orders;
	...
}
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id")
    private Product product;
	...
}

@OneToMany, @ManyToOne... 이 애너테이션들에 fetchType을 Lazy로 설정해준다.

 

즉시로딩

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // 즉시 로딩 설정
    @OneToMany(mappedBy = "product", fetch = FetchType.EAGER)
    private List<Order> orders;
	...
}
@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // 즉시 로딩은 기본값
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "product_id")
    private Product product;
	...
}

FetchType.EAGER 를 명시해주면 즉시로딩을 사용할 수 있다.