-
DDD - ORM과 투명한 영속성 8부 9부JAVA/DDD 2021. 1. 24. 07:43
- OrderLineItem에도 Long 타입의 IDENTITY FILED를 추가한다.
- @Cofigurable Annotation이 계속 사용되고 있음에 주목하자.
- @Cofigurable Annotation은 Spring 컨테이너 외부에서 생성되는 객체에 Spring 컨테이너에서 선언된 빈을 의존 삽입하기 위해 사용된다.
- 여기에서는 Hibernate가 생성하는 OrderLineItem 객체에 ProductRepository 타입의 빈을 의존 삽입하기 위해 사용되고 있다.
12345678910111213141516171819202122@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() 메소드를 사용한다.
1234567891011121314151617181920212223242526272829...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일 필요는 없다.
123456789101112131415public 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의 코드를 살펴보자.
1234567891011121314151617181920212223242526272829303132333435public class HibernateOrderRepository extends HibernateDaoSupportimplements 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” 빈을 의존 주입하도록 설정한다.
참조
'JAVA > DDD' 카테고리의 다른 글
DDD - ORM과 투명한 영속성 10부 11부 (0) 2021.01.24 DDD - ORM과 투명한 영속성 6부 7부 (0) 2021.01.24 DDD - ORM과 투명한 영속성 5부 (0) 2021.01.24 DDD. ORM과 투명한 영속성 4부 (0) 2021.01.24 DDD. ORM과 투명한 영속성 3부 (0) 2021.01.23