웹사이트 검색

Spring ORM 예제 - JPA, Hibernate, 트랜잭션


Spring ORM 예제 자습서에 오신 것을 환영합니다. 오늘 우리는 Hibernate JPA 트랜잭션 관리를 사용하는 Spring ORM 예제를 살펴볼 것입니다. 다음과 같은 기능이 있는 Spring 독립 실행형 애플리케이션의 매우 간단한 예를 보여드리겠습니다.

  • 의존성 주입(@Autowired 주석)
  • JPA EntityManager(Hibernate에서 제공)
  • 주석이 있는 트랜잭션 메서드(@Transactional 주석)

스프링 ORM 예제

Spring ORM Maven 의존성

아래는 Spring ORM 종속성이 있는 최종 pom.xml 파일입니다. 우리는 Spring ORM 예제에서 Spring 4와 Hibernate 4를 사용했습니다.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>hu.daniel.hari.learn.spring</groupId>
	<artifactId>Tutorial-SpringORMwithTX</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<properties>
		<!-- Generic properties -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.7</java.version>

		<!-- SPRING & HIBERNATE / JPA -->
		<spring.version>4.0.0.RELEASE</spring.version>
		<hibernate.version>4.1.9.Final</hibernate.version>

	</properties>

	<dependencies>
		<!-- LOG -->
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- JPA Vendor -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- IN MEMORY Database and JDBC Driver -->
		<dependency>
			<groupId>hsqldb</groupId>
			<artifactId>hsqldb</artifactId>
			<version>1.8.0.7</version>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

  • Spring 종속성으로 spring-contextspring-orm이 필요합니다.
  • JPA 구현으로 Hibernate용 hibernate-entitymanager를 사용합니다. hibernate-entitymanagerhibernate-core에 의존하므로 pom.xml에 명시적으로 hibernate-core를 넣을 필요가 없습니다. Maven 전이 종속성을 통해 프로젝트로 끌어오고 있습니다.
  • 또한 데이터베이스 액세스를 위한 종속성으로 JDBC 드라이버가 필요합니다. 우리는 JDBC 드라이버와 작업 중인 메모리 데이터베이스를 포함하는 HSQLDB를 사용하고 있습니다.

스프링 ORM 모델 클래스

Hibernate가 JPA 구현을 제공하기 때문에 모델 빈에서 매핑을 위해 표준 JPA 주석을 사용할 수 있습니다.

package hu.daniel.hari.learn.spring.orm.model;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Product {

	@Id
	private Integer id;
	private String name;

	public Product() {
	}

	public Product(Integer id, String name) {
		this.id = id;
		this.name = name;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Product [id=" + id + ", name=" + name + "]";
	}

}

@Entity@Id JPA 주석을 사용하여 POJO를 엔터티로 한정하고 기본 키를 정의합니다.

스프링 ORM DAO 클래스

우리는 persist 및 findALL 메서드를 제공하는 매우 간단한 DAO 클래스를 만듭니다.

package hu.daniel.hari.learn.spring.orm.dao;

import hu.daniel.hari.learn.spring.orm.model.Product;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Component;

@Component
public class ProductDao {

	@PersistenceContext
	private EntityManager em;

	public void persist(Product product) {
		em.persist(product);
	}

	public List<Product> findAll() {
		return em.createQuery("SELECT p FROM Product p").getResultList();
	}

}

  • @Component는 Spring IoC(Dependency Injection)를 통해 이 클래스를 사용할 수 있음을 Spring 컨테이너에 알리는 Spring 주석입니다.
  • 우리는 EntityManager에 종속성 주입을 나타내는 JPA @PersistenceContext 주석을 사용합니다. Spring은 spring.xml 구성에 따라 적절한 EntityManager 인스턴스를 주입합니다.

Spring ORM 서비스 클래스

간단한 서비스 클래스에는 2개의 쓰기 메서드와 1개의 읽기 메서드(add, addAll 및 listAll)가 있습니다.

package hu.daniel.hari.learn.spring.orm.service;

import hu.daniel.hari.learn.spring.orm.dao.ProductDao;
import hu.daniel.hari.learn.spring.orm.model.Product;

import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class ProductService {

	@Autowired
	private ProductDao productDao;

	@Transactional
	public void add(Product product) {
		productDao.persist(product);
	}
	
	@Transactional
	public void addAll(Collection<Product> products) {
		for (Product product : products) {
			productDao.persist(product);
		}
	}

	@Transactional(readOnly = true)
	public List<Product> listAll() {
		return productDao.findAll();

	}

}

  • Spring @Autowired 주석을 사용하여 서비스 클래스에 ProductDao를 주입합니다.
  • 트랜잭션 관리를 사용하려고 하므로 메서드에 @Transactional Spring 주석이 추가됩니다. listAll 메서드는 데이터베이스를 읽기만 하므로 최적화를 위해 @Transactional 주석을 읽기 전용으로 설정합니다.

Spring ORM 예제 Bean 구성 XML

스프링 ORM 예제 프로젝트 자바 클래스가 준비되었습니다. 이제 스프링 빈 구성 파일을 살펴보겠습니다. <코드>spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans" 
	xmlns:p="https://www.springframework.org/schema/p"
	xmlns:context="https://www.springframework.org/schema/context" 
	xmlns:tx="https://www.springframework.org/schema/tx" 
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
		https://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		https://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context-3.0.xsd
		https://www.springframework.org/schema/tx
		https://www.springframework.org/schema/tx/spring-tx.xsd
		">
	
	<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
	<context:component-scan base-package="hu.daniel.hari.learn.spring" />
	<!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
	<context:annotation-config />

	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
		<property name="url" value="jdbc:hsqldb:mem://productDb" />
		<property name="username" value="sa" />
		<property name="password" value="" />
	</bean>
	
	<bean id="entityManagerFactory" 
			class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
			p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
            p:dataSource-ref="dataSource"
			>
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="generateDdl" value="true" />
				<property name="showSql" value="true" />
			</bean>
		</property>
	</bean>

	<!-- Transactions -->
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<!-- enable the configuration of transactional behavior based on annotations -->
	<tx:annotation-driven transaction-manager="transactionManager" />

</beans>

  1. 먼저 Spring xml에서 하나씩 정의하는 대신 Spring 컴포넌트(서비스, DAO)에 대한 클래스 경로 스캐닝을 사용하고 싶다고 Spring에 알립니다. Spring 주석 감지도 활성화했습니다.
  2. 현재 HSQLDB 메모리 내 데이터베이스인 데이터 소스를 추가합니다.
  3. 우리는 EntityManager를 얻기 위해 애플리케이션에서 사용할 JPA EntityManagerFactory를 설정합니다. Spring은 이를 수행하는 3가지 방법을 지원하며 전체 JPA 기능을 위해 LocalContainerEntityManagerFactoryBean을 사용했습니다. LocalContainerEntityManagerFactoryBean 속성을 다음과 같이 설정합니다.\n
    1. 모델 클래스 패키지를 가리키는 packagesToScan 속성
    2. 스프링 구성 파일에서 이전에 정의된 데이터 소스
    3. jpaVendorAdapter를 최대 절전 모드로 설정하고 일부 최대 절전 속성을 설정합니다.

    Spring ORM Hibernate JPA 예제 테스트 프로그램

    봄 ORM JPA Hibernate 예제 프로젝트가 준비되었으므로 응용 프로그램에 대한 테스트 프로그램을 작성해 보겠습니다.

    public class SpringOrmMain {
    	
    	public static void main(String[] args) {
    		
    		//Create Spring application context
    		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
    		
    		//Get service from context. (service's dependency (ProductDAO) is autowired in ProductService)
    		ProductService productService = ctx.getBean(ProductService.class);
    		
    		//Do some data operation
    		
    		productService.add(new Product(1, "Bulb"));
    		productService.add(new Product(2, "Dijone mustard"));
    		
    		System.out.println("listAll: " + productService.listAll());
    		
    		//Test transaction rollback (duplicated key)
    		
    		try {
    			productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
    		} catch (DataAccessException dataAccessException) {
    		}
    		
    		//Test element list after rollback
    		System.out.println("listAll: " + productService.listAll());
    		
    		ctx.close();
    		
    	}
    }
    

    메인 메서드에서 Spring 컨테이너를 얼마나 쉽게 시작할 수 있는지 알 수 있습니다. 첫 번째 종속성 주입 진입점인 서비스 클래스 인스턴스를 얻습니다. 스프링 컨텍스트가 초기화된 후 ProductService 클래스에 삽입된 ProductDao 클래스 참조. ProducService 인스턴스를 얻은 후 메서드를 테스트할 수 있습니다. 모든 메서드 호출은 Spring의 프록시 메커니즘으로 인해 트랜잭션이 됩니다. 이 예에서는 롤백도 테스트합니다. Spring ORM 예제 테스트 프로그램을 실행하면 아래와 같은 로그를 얻을 수 있습니다.

    Hibernate: insert into Product (name, id) values (?, ?)
    Hibernate: insert into Product (name, id) values (?, ?)
    Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
    listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
    Hibernate: insert into Product (name, id) values (?, ?)
    Hibernate: insert into Product (name, id) values (?, ?)
    Hibernate: insert into Product (name, id) values (?, ?)
    Hibernate: select product0_.id as id0_, product0_.name as name0_ from Product product0_
    listAll: [Product [id=1, name=Bulb], Product [id=2, name=Dijone mustard]]
    

    두 번째 트랜잭션이 롤백되므로 제품 목록이 변경되지 않은 이유입니다. 첨부된 소스의 log4j.properties 파일을 사용하면 내부에서 무슨 일이 일어나고 있는지 확인할 수 있습니다. 참조: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/orm.html 아래 링크에서 최종 Spring ORM JPA Hibernate 예제 프로젝트를 다운로드하고 이를 가지고 놀면서 학습할 수 있습니다. 더.

    트랜잭션 프로젝트와 함께 Spring ORM 다운로드