JPA 의존성 제거와 ORM XML을 통한 엔티티 관리

궁금하면 500원·2024년 7월 14일

미생의 스프링

목록 보기
17/47

기본 예제와 실무 적용

이 포스팅에서는 JPA의 의존성을 줄이기 위한 방법과 ORM XML을 사용하여
엔티티를 관리하는 방법을 소개합니다.

기존의 JPA 애노테이션을 제거하고 XML 기반의 ORM 명세를 통해 엔티티를 정의하는 예제를 통해,
JPA에 대한 의존성을 효과적으로 줄일 수 있음을 보여드립니다.

또한, PlatformTransactionManager와 관련된 기본 트랜잭션 관리 개념도 간단히 다뤄보겠습니다.

이 포스팅은 JPA와 ORM XML을 활용하여 코드의 독립성을 높이고자 하는 개발자들에게
유용한 정보가 될 것입니다.

실제 코드 예제와 함께 JPA 의존성을 줄이는 방법에 대한 기초적인 이해를 돕고,
실무에서의 적용 가능성도 탐색해 봅니다.

Entity


import jakarta.persistence.*;

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue
    private Long id;
    @Column(unique = true)
    private String no;
    private BigDecimal total;
}

기존 JPA 엔티티 코드입니다.

@Entity, @Table, @Id, @GeneratedValue, @Column 등의
애노테이션은 jakarta 패키지를 사용합니다.

=> JPA 라이브러리에 의존적이라 저는 생각합니다.

어떤 환경이든, 어떤 맥락이든 "** 에 의존적이다." 라는 메시지는 그리 긍정적이진 않습니다.

저는 이직 경험이 많지 않아서 아직 제가 찾은 개발환경은 전부 JPA를 쓰고 있기 때문에
JPA에 의존적인 개발이 딱히 이슈로 여겨질만한 상황은 없었습니다.

하지만 이 의존성을 어떻게 타파하고 대비할 수 있는지를 알아두면 좋겠다 그런 생각이 많이들었습니다.

// resources/META-INF/orm.xml
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
                 version="1.0">
    <entity class="tobyspring.hellospring.order.Order" name="Order" access="FIELD">
        <table name="orders"/>
        <attributes>
            <id name="id">
                <generated-value strategy="AUTO"/>
            </id>
            <basic name="no">
                <column name="no" length="255"/>
            </basic>
            <basic name="total">
                <column name="total" column-definition="number(38,2)"/>
            </basic>
        </attributes>
    </entity>
</entity-mappings>

간단하게 작성한 orm 명세 xml 입니다.

이 내용을 바탕으로 entity class 경로의 대상을 JPA 엔티티로 특정할 수 있습니다.

따라서, 해당 엔티티 클래스에서 jakarta 패키지 및 어노테이션을 제거하였습니다.

@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    private Long id;
    private String no;
    private BigDecimal total;
}

원래대로라면 JPA Entity 관련 명세가 붙어있지 않기 때문에
Repository 로 저장하려고 해도 코드가 실행되지 않을 것입니다.

10:41:09.731 [main] INFO org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean -- Initialized JPA EntityManagerFactory for persistence unit 'default'
Hibernate: select next value for orders_SEQ
Hibernate: insert into orders (no,total,id) values (?,?,?)
Hibernate: select next value for orders_SEQ
Hibernate: insert into orders (no,total,id) values (?,?,?)
order = Order{id=1, no='100', total=10}

하지만 코드가 실행되는데 이슈는 없었다. 'resources/META-INF/orm.xml' 파일이 그 명세를 대신 해주었숩니다.
따라서, 엔티티 클래스 자체로는 JPA 의존성을 제거했다고 볼 수 있겠습니다.

PlatformTransactionManager

트랜잭션을 관리하는 역할의 인터페이스이다. 스프링에서 기본으로 제공하며,
JpaTransactionManager, JDBCTransactionManager 등이 이 인터페이스를 구현하여
트랜잭션 관리를 제공합니다.

따라서, (JPA 의존성을 분리하기 위해선) 이 PlatformTransactionManager 이
의존하는 기술을 변경할 수 있도록 조정해야합니다.

@Configuration
public class DataConfig {
    ...
    @Bean
    public PlatformTransactionManager transactionManager(final EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }
}
@Service
@RequiredArgsConstructor
public class OrderService {
    private final PlatformTransactionManager transactionManager;
    private final OrderRepository orderRepository;

    public Order saveOrder(final Order order) {
        return new TransactionTemplate(this.transactionManager).execute(status -> {
            this.orderRepository.save(order);
            return null;
        });
    }
}

물론, 실무에선 @TransactionManager 어노테이션을 통해 트랜잭션을 관리하는 것을 알고 있지만,
위와 같이 Basic 단계에서 이렇게 동작하는구나 라고 대략적인 맛을 보았습니다.

참조

토비의스프링 6 - 이해와 원리
Spring Framework 공식 문서
Spring Data JPA Reference Documentation

profile
에러가 나도 괜찮아 — 그건 내가 배우고 있다는 증거야.

0개의 댓글