ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DDD - ORM과 투명한 영속성 8부 9부
    JAVA/DDD 2021. 1. 24. 07:43
    • OrderLineItem에도 Long 타입의 IDENTITY FILED를 추가한다.
    • @Cofigurable Annotation이 계속 사용되고 있음에 주목하자.
    • @Cofigurable Annotation Spring 컨테이너 외부에서 생성되는 객체에 Spring 컨테이너에서 선언된 빈을 의존 삽입하기 위해 사용된다. 
    • 여기에서는 Hibernate가 생성하는 OrderLineItem 객체에 ProductRepository 타입의 빈을 의존 삽입하기 위해 사용되고 있다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Configurable(value="orderLineItem",preConstruction=true)
    public class OrderLineItem {
        private Long id;
        private Product product;
        private int quantity;
     
        
        private ProductRepository productRepository;
        
        public OrderLineItem() {
        }
        
        public OrderLineItem(String productName, int quantity) {
            this.product = productRepository.find(productName);
            this.quantity = quantity;
        }
        
     
        public void setProductRepository(ProductRepository productRepository) {
            this.productRepository = productRepository;
        }
    }
    cs
    • OrderLineItem Order HashSet에 저장되기 때문에 equals() hashCode()를 반드시 오버라이딩해야 한다. 
    • 하나의 Order 내에는 하나의 Product에 대해 하나의 OrderLineItem 만이 존재해야 한다는 도메인 규칙이 있으므로 Product quantity를 사용하여 비교를 수행하면 될 것 같다.
    • OrderLineItem 역시 지연 로딩 문제를 방지하기 위해 getQuantity() 메소드를 사용한다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
       ...
        
        int getQuantity() {
            return quantity;
        }
        
        public boolean equals(Object object) {
            
            if (object == this) {
                return true;
            }
            
            if (!(object instanceof OrderLineItem)) {
                return false;
            }
            
            final OrderLineItem other = (OrderLineItem)object;
     
            return this.product.equals(other.getProduct())
                    && this.quantity == other.getQuantity();
        }
        
        public int hashCode() {
            int result = 17;
            result = 37*result + product.hashCode();
            result = 37*result + quantity;
            return result;
        }
    }
    cs

     

    • Order 클래스를 ORDERS 테이블에, OrderLineItem 클래스를 ORDER_LINE_ITEMS 테이블에 맵핑한다.
    •  Hibernate는 테이블의 컬럼을 도메인 객체의 속성이나 프로퍼티 둘 중 하나에 맵핑되도록 할 수 있다. 
    • 개인적으로 속성 맵핑을 선호한다. 프로퍼티 맵핑을 위해 불필요한 gettter/setter 메소드를 추가하는 것은 클래스 추상화의 일관성을 깨기 쉬우며 개발이 번거롭기 때문이다. 
    • 프로퍼티 접근에 의한 캡슐화 보장이라는 장점은 ORM 이라는 문맥 상에서 볼 때 그다지 설득력이 없어 보인다.

     

     

     

    • 위 맵핑 정보에서 눈여겨 볼 부분은 Order 클래스의 lineItems 속성에 OrderLineItem Set으로 맵핑하는 부분이다.
    • Hibernate의 경우 맵핑 파일 내에 cascade 속성을 사용하여 영속성 전이(transitive persistence)를 지원한다.
    • OrderLineItem의 생명주기는 Order에 종속적이므로 Order OrderRepository를 통해 데이터베이스에 저장되거나 삭제되었을 때 함께 저장되어야 한다.

    어노테이션 방식

    •  또한 Order Set으로부터 제거되었을 때 데이터베이스에서 삭제되어야 한다. cascade = CascadeType.ALL 을 명시함으로써 AGGREGATE의 생명주기 제약 조건을 만족시킬 수 있다.

     

     

     

    • 이제 Produc 클래스의 변경 사항을 살펴 보자. 데이터베이스의 주 키를 추적하기 위한 id 속성과 함께 기본 생성자가 추가되었다. 
    • 이 역시 하부 인프라스트럭처의 제약 사항이 도메인 클래스의 구현에 영향을 미치는 예로 Hibernate의 경우 객체를 생성할 때 newInstance()를 호출하기 때문에 기본 생성자가 존재해야 한다. 
    • 기본 생성자는 public일 필요는 없다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Product {
     
        private Long id;
        private Money price;
        private String name;
     
        Product() {
        }
     
     
        public Product(String name, long price) {
            this.name = name;
            this.price = new Money(price);
        }
    }
    cs
    • Product VALUE OBJECT Money를 속성으로 포함한다. 
    • 앞에서 설명한 바와 같이 VALUE OBJECT는 별도의 테이블로 맵핑되지 않고 의존하는 ENTITY가 맵핑되는 테이블의 컬럼으로 맵핑된다.
    • Hibernate component를 사용하여 VALUE OBJECT의 개념을 지원한다. 

     

     

    • 이제 REPOSITORY를 구현하자. 우리는 이전 아티클에서 REPOSITORY의 인터페이스와 구현 클래스를 분리했으므로 도메인 레이어의 다른 부분에는 영향을 미치지 않고 Hibernate 전용 REPOSITORY를 구현할 수 있다.
    • Spring Hibernate를 쉽게 연동할 수 있는 기반 클래스인 org.springframework.orm.hibernate3.support.HibernateDaoSupport를 제공한다. 
    • 이 클래스는 앞에서 선언한 SessionFactory를 사용하여 내부적으로 HibernateTemplate을 생성한다.
    •  하위 클래스에서는 getHibernateTemplate()를 호출함으로써 Hibernate Session 관련 기능을 사용할 수 있다. OrderRepository 인터페이스의 구현 클래스인 HibernateOrderRepository의 코드를 살펴보자.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class HibernateOrderRepository extends HibernateDaoSupport
            implements OrderRepository {
        
        public Order delete(Order order) {
            getHibernateTemplate().delete(order);
            return order;
        }
     
        
        @SuppressWarnings("unchecked")
        public Order find(String orderId) {
            List<Order> result = (List<Order>)getHibernateTemplate()
                    .find("from Order where orderId=?", orderId);
            if (result != null && result.size() > 0) {
                return result.get(0);
            }
            return null;
        }
        
        @SuppressWarnings("unchecked")
        public Set<Order> findAll() {
            return new HashSet<Order>(getHibernateTemplate()
                    .loadAll(Order.class));
        }
     
        @SuppressWarnings("unchecked")
        public Set<Order> findByCustomer(Customer customer) {
            return new HashSet<Order>(getHibernateTemplate()
                    .find("from Order where customer=?", customer));
        }
        
        public void save(Order order) {
            getHibernateTemplate().save(order);
        }
    }
    cs

     

    • HibernateOrderRepository HibernateDaoSupport를 상속 받고 OrderRepository를 실체화한다. 
    • 각 메소들은 getHibernateTemplate()를 사용하여 데이터베이스 관련 작업을 수행한다.
    • 이제 REPOSITORY Hibernate와 연동하도록 수정했으므로 Spring 빈 컨텍스트에 추가하자.
    • Hibernate 기반 REPOSITORY를 설정하기 위해 “sessionFactory” 프로퍼티에 앞에서 설정한 “sessionFactory” 빈을 의존 주입하도록 설정한다.

     

     

     

    참조


    이터니티 - Domain-Driven Design의 적용

Designed by Tistory.