Java/ORM

JAVA ORM - JPA(proxy, loading)

티코딩 2024. 8. 6. 19:24

프록시란

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

역할로는

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 를 명시해주면 즉시로딩을 사용할 수 있다.