Hibernate, JPA, Spring Data JPA의 관계

Nine-JH·2023년 11월 7일
0

들어가며

지독한 SQL의 굴레에서 저를 구원해준 것이 바로 ORM Framework 인 JPA와 Hibernate인데요.
정작 JPA와 Hibernate, Spring Data JPA의 관계에 대해 정립할 생각은 해보지 못했더군요. 이번 기회에 차이점을 정확하게 구분하는 예배시간을 갖고자 합니다.

참고로 ORM Framework가 무엇인지는 아는 상태임을 가정하고 설명을 하겠습니다.

  • ORM Framework 역할은 Java 객체의 패러다임을 RDB 패러다임과 일치시키는 역할을 합니다.


Hibernate

HibernateORM Framework 중 하나입니다. Hibernate를 사용하는 프로세스를 간략하게나마 설명해드리자면 아래와 같은데요.

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.Session;

public class HibernateUtil {
    private static final SessionFactory sessionFactory;

    static {
        try {
            Configuration config = new Configuration().configure("hibernate.cfg.xml");
            StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
                    .applySettings(config.getProperties());
            sessionFactory = config.buildSessionFactory(builder.build());
        } catch (Throwable ex) {
            log.info("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtil.getSessionFactory().openSession();

        // pseudo : 여기서부터 Hibernate을 사용하여 데이터베이스 작업을 수행

        session.close();
    }
}

익숙한 EntityManager 는 온데간데 없고 SessionFactory 를 사용하는 것이 눈에 띕니다. 당연히 EntityManager는 없습니다! 그건 Hibernate가 가지고 있던게 아니기 때문입니다.


JPA

HibernateEJB의 모듈 중 하나로 개발이 시작되었습니다. 실제로 Hibernate 3.2을 보시면 EJB가 존재하고 있다는 것을 볼 수 있습니다.

시간이 흘러 Hibernate의 가능성을 눈여겨 보고 EJB에서 분리해 별도의 모듈로 확장하게 되었고, 더욱 확장성 있는 ORM Framework를 제공하기 위해 java 5버전에서 부터 ORM에 관한 표준 API를 만들어 제공하게 되었습니다. (DB 벤더사에게 Connection 관련 SPI를 제공하는 JDBC 처럼요.) 이것이 바로 JPA(Java Persistence API) 입니다.

JPA의 구현체

JPA에서는 PersistenceProvider 라는 spi를 제공하고 있습니다. ORM Framework들이 이를 구현하면 사용자측에서는 의존성을 추가하는 것 만으로도 별 다른 코드 없이 사용이 가능하죠!

package jakarta.persistence.spi;

...

/**
 * Interface implemented by the persistence provider.
 *
 * <p> It is invoked by the container in Jakarta EE environments and
 * by the {@link Persistence} class in Java SE environments to
 * create an {@link EntityManagerFactory} and/or to cause
 * schema generation to occur.
 *
 * @since 1.0
 */
public interface PersistenceProvider {
    ...
}
META-INF/services/jakarta.persistence.spi.PersistenceProvider` 

org.hibernate.jpa.HibernatePersistenceProvider           // hibernate
org.eclipse.persistence.jpa.PersistenceProvider          // eclipselink
org.apache.openjpa.persistence.PersistenceProviderImpl   // openjpa

구현체의 종류로는 hibernateEclipseLink, OpenJPA가 있습니다.

hibernate


JPA와 Hibernate와 JDBC

결론적으로 JPA와 Hibernate는 다음와 같이 도식화할 수 있습니다.



Spring Data Jpa

Spring을 사용하시는 분들 중에서는 EntityManager을 처음 보시는 분도 계실겁니다. 괜찮습니다. 그게 바로 Spring이 원한 방향이기 때문입니다.

JPA의 BoilerPlate

public void persistSomething() {
	EntityManager em = emf.createEntityManager();
	EntityTransaction transaction = em.getTransaction();
	transaction.begin();
    // 작업 시작
	transaction.commit();
}

문제는 DB에 접근할 때마다 EntityManagerTransaction을 생성하는 코드를 작성해야 한다는 점입니다. 이런 BoilerPlate Code를 줄일 방법이 없을까요?


Boiler Plate를 제거하는 Spring Data JPA

Spring 진영에서는 JPA를 한 단계 더 추상화 한 Spring Data JPA 모듈을 제공함으로써 해당 BoilerPlate를 제거해주었습니다.

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {

	...

	private final JpaEntityInformation<T, ?> entityInformation;
	private final EntityManager entityManager;
	private final PersistenceProvider provider;

	...

	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null");

		if (entityInformation.isNew(entity)) {
			entityManager.persist(entity);
			return entity;
		} else {
			return entityManager.merge(entity);
		}
	}
    
    // Optional Wrapping도 담당함.
    @Override
	public Optional<T> findById(ID id) {

		Assert.notNull(id, ID_MUST_NOT_BE_NULL);

		Class<T> domainType = getDomainClass();

		if (metadata == null) {
			return Optional.ofNullable(entityManager.find(domainType, id));
		}

		LockModeType type = metadata.getLockModeType();
		Map<String, Object> hints = getHints();

		return Optional.ofNullable(type == null ? entityManager.find(domainType, id, hints) : entityManager.find(domainType, id, type, hints));
	}
    
    ...
}

덕분에 실제로 Spring Data Jpa를 사용하는 개발자 입장에서는 EntityManagerTransaction은 생각하지 하지 않고 제공하는 API를 사용하면 됩니다.

public void saveEntity(AEntity aEntity) {
	aRepository.save(aEntity);
}

Spring Data JPA와 Spring JPA

결론적으로 다음과 같은 형태가 될 것 같네요

정리

  • Hibernate : JPA 구현체 중 하나
  • JPA : ORM Framework 의 표준 API Spec
  • Spring Data JPA : JPA의 보일러 플레이트를 줄이기 위해 Spring 에서 제공하는 API

0개의 댓글