본문 바로가기

수업 정리

61일차 수업정리(Spring - MyBatis)

**Oracle MyBatis 연동

1. 데이터베이스 준비

GOODS 테이블

    => code : number

    => name : varchar2

    => regdate : date

 

2. Simple Spring Maven 프로젝트 생성

    => 안만들어지면 Java Project를 생성하여 Maven Project로 변환

 

3. Oracle을 MyBatis를 이용하여 사용할 때 필요한 의존성을 설정 - pom.xml

    => Oracle, SpringJDBC, 

  1) 오라클 사용을 위해서 repository 생성

<!-- 중앙 저장소가 아닌 곳에서 라이브러리를 다운로드 받고자 하는 경우 설정 : Oracle 때문에 설정 -->
<repositories>
	<repository>
		<id>oracle</id>
		<name>ORACLE JDBC Repository</name>
		<url>http://maven.jahia.org/maven2</url>
	</repository>
</repositories>

 

  2) dependency 설정

<!-- 오라클 의존성 -->
<dependency>
	<groupId>com.oracle</groupId>
	<artifactId>ojdbc7</artifactId>
	<version>12.1.0.2</version>
</dependency>
<!-- 스프링에서 데이터베이스를 사용할 때 필요한 의존성 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${spring-framework.version}</version>
</dependency>
<!-- MyBatis 의존성 -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.6</version>
</dependency>
<!-- Spring에서 MyBatis를 사용할 때 필요한 의존성 -->
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.2</version>
</dependency>

 

4. 데이터베이스 접속 테스트

    => Spring에서는 데이터베이스 사용할 때 설정 파일에 DataSource를 만들어서 사용하는 것을 강제

    => src 또는 src/main/resources 디렉토리에 spring bean configuration 파일추가하고 작성 - applicationContext.xml 

<!-- DataSource 빈 생성 코드 -->
<bean 
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
	<!-- 데이터베이스 종류 : Oracle -->
	<property name="driverClassName" 
		value="oracle.jdbc.driver.OracleDriver"/>
	<!-- 연결할 데이터베이스 URL -->	
	<property name="url"			value="jdbc:oracle:thin:@192.168.0.200:1521:xe"/>
	<!-- 사용할 데이터베이스 계정 -->
	<property name="username" value="system"/>
	<!-- 사용할 계정의 비밀번호 -->
	<property name="password" value="********" />
</bean>

 

5. 위의 설정이 제대로 되었는지 테스트 클래스를 만들어서 테스트

    => src/test/java 또는 src디렉토리에 테스트용 클래스를 생성 - MyBatisTest

public static void main(String[] args) {
	try {
		GenericXmlApplicationContext context = 
			new GenericXmlApplicationContext(
				"applicationContext.xml");
		DataSource dataSource = 
			context.getBean(DataSource.class);
		//내용이 정상적으로 출력되면 데이터베이스 접속 성공
		Connection con = dataSource.getConnection();
		System.out.println(con);
	}catch(Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
}

 

6. 테이블과 매핑이 될 DTO 클래스를 생성

    => 자료형과 프로퍼티 명을 정확하게 매핑

import java.util.Date;

public class Good {
	private int code; 
	private String name;
	private Date regdate;
	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Date getRegdate() {
		return regdate;
	}
	public void setRegdate(Date regdate) {
		this.regdate = regdate;
	}
	
	@Override
	public String toString() {
		return "Good [code=" + code + ", name=" + name + ", regdate=" + regdate + "]";
	}
}

 

7. MyBatis 나 Hibernate 등의 데이터베이스 연동 프레임워크를 사용시 여기까지는 공통 작업

 

**MyBatis를 XML을 이용하여 사용

    => 환경 설정파일을 1개 생성 - 생략가능

        - 패키지 이름이 길 떄 패키지 이름을 생략하고 DTO를 사용하기 위해서 생성

        - resultMap이라고해서 DTO에 만든 프로퍼티 명과 이름과 테이블의 컬럼명이 다를때 매핑시켜주는 설정을 작성

    => SQL을 작성할 매퍼 파일을 n개 생성 - 필수

    => Spring 설정 파일에 위의 정보를 이용하여 SqlSessionFactoryBean과 SqlSessionTemplate 클래스의 bean을 생성

        하는 코드를 작성

    => DAO 클래스에서 SqlSession을 주입받아서 사용

 

1. 매퍼 파일 작성 요령

    => namespace를 생성 - 이 파일의 이름을 설정

    - namespace std{ int count = 7; }

  1) select

<select id="구별할 이름" 
	parameterType="파라미터 전체를 하나로 묶어서 전달할 자료형"
	resultType="select 절의 컬럼들을 하나로 묶어서 전달할 자료형">
	select 구문
</select>

    => 외부로부터 대입받는 데이터는 ?대신에 #{이름}의 형태로 작성

 

    - code, name, regdate로 구성된 Goods 테이블의 모든 데이터를 가져오는 SQL

<select id="allgood" resultType="mybatis.domain.Good">
	select code, name, regdate
	from goods
</select>

 

    - code를 입력받아서 code에 해당하는 데이터를 가져오는 SQL

<select id="getgood" 
	parameterType="java.lang.Integer"
	resultType="mybatis.domain.Good">
	select code, name, regdate
	from goods
	where code = #{code}
</select>

 

    - user 테이블에서 email에 해당하는 데이터를 찾아오는 SQL(User클래스가 없는 경우 Map사용)

<select id="getuser" parameterType="java.util.Map"
	resultType="java.util.Map">
	select email, password, nickname, image
	from user
	where email = #{email} and password = #{password}
</select>

 

    => 부등호는 직접 사용할 수 없으므로 &lt;(<), &gt;(>)

    => like 사용

        - Oracle : '% 나 _' || #{keyword} || '% 나 _')

        - MySQL : concat('% 나 _', #{keyword} || '% 나 _')

 

  2) select 이외 구문

    - insert, update, delete 태그로 만드는데 사실 아무태그나 써도 결과는 같음

<insert id="구별할이름" parameterType="파라미터를 묶어서 전달할 자료형">
	삽입할 SQL
</insert>

    - Good 테이블에 데이터를 삽입하는 SQL

<insert id="insertgood" parameterType="mybatis.domain.Good">
	insert into goods(code, name, regdate)
	values(#{code}, #{name}, #{regdate})
</insert>

 

2. src에 mappers 디렉토리를 생성하고 good.xml 파일을 만들어서 SQL 작성

<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD: XML로 작성한 내용을 해석하는 위치를 설정 -->
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="good">
	<!-- 파라미터없이 전체 데이터를 읽어오는 SQL : 전체보기 -->
	<select id="allgood" resultType="mybatis.domain.Good">
		select code, name, regdate
		from goods
	</select>
	<!-- code를 가지고 하나의 데이터를 읽어오는 SQL : 상세보기 -->
	<select id="getgood" 
		parameterType="java.lang.Integer"
		resultType="mybatis.domain.Good">
		select code, name, regdate
		from goods
		where code = #{code}
	</select>
	
	<!-- 데이터를 삽입하는 SQL -->
	<insert id="insertgood" 
		parameterType="mybatis.domain.Good">
		insert into goods(code, name, regdate)
		values(#{code}, #{name}, #{regdate})
	</insert>
	
	<!-- 데이터를 수정하는 SQL 
	기본키(code)를 가지고 데이터를 가져와서 나머지 컬럼의 값을 수정-->
	<update id="updategood"
		 parameterType="mybatis.domain.Good">
		update goods
		set	name=#{name}, regdate=#{regdate}
		where code = #{code}
	</update>
	
	<!-- 데이터를 삭제하는 SQL 
	기본키를 가지고 데이터를 삭제하는 것이 일반적-->
	<delete id="deletegood" parameterType="java.lang.Integer">
		delete from goods
		where code = #{code}
	</delete>

</mapper> 

    => 없는 자료형 사용시 애플리케이션 시작될때 에러가 발생

    => id가 틀리게 호출되거나 parameter가 잘못되면 메소드를 호출할때 에러가 발생

 

3. Spring Bean Configuration 파일에 xml을 이용하는 MyBatis Bean을 생성하는 코드를 작성

    => SqlSessionFactoryBean, SqlSessionTemplate

<!-- MyBatis 사용을 위한 bean 설정 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
	<!-- 연결할 데이터베이스 설정 -->
	<property name="dataSource" ref="dataSource"/>
	<!-- 매퍼 파일의 위치 설정 -->
	<property name="mapperLocations"
		value="mappers/good.xml" />
</bean>
<bean id="sqlSessionTemplate"
class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg name="sqlSessionFactory"
	ref ="sqlSessionFactory"/>
</bean>

 

4. 설정확인

    => SqlSessionTemplate을 주입받아 보면 됨

    => 이부분에서 에러가 발생하는 것은 대부분 매퍼 파일의 경로를 틀려서 발생

GenericXmlApplicationContext context = 
	new GenericXmlApplicationContext(
		"applicationContext.xml");
			
//SQLSession 주입받아보기
//출력이 되면 MyBatis 설정에는 문제가 없음
SqlSession sqlSession = context.getBean(SqlSession.class);
System.out.println(sqlSession);

    

5. XML을 이용한 MyBatis 연동 DAO 작성방법

    => SqlSession을 주입받아서 사용

    => SQL 호출

    => 파라미터가 없는 경우 파라미터 생략 가능

  1) selectList("아이디", 파라미터) : resultType의 List를 리턴

  2) selectOne("아이디", 파라미터) : resultType으로 리턴

    => select의 결과가 0개이면 null이 리턴되고 2개 이상이면 예외가 발생

    => primary key, unique 제약 조건이 설정된 컬럼으로 조회하거나 count등의 집계 함수를 이용하여 데이터를 1개만

       조회하는 경우 사용

  3) insert("아이디", 파라미터)

  4) update("아이디", 파라미터)

  5) delete("아이디", 파라미터)

    => 3개의 메소드는 정수를 리턴

        - 정수는 영향받은 행의 개수

        - insert : 0 리턴시 삽입 실패

        - update, delete : 0 리턴시 조건에 맞는 데이터가 없어 작업을 수행하지 않은 것

  6) 많이 발생시키는 예외

    => 아이디를 잘못 입력해야 하는 경우

    => 파라미터를 대입해야 하는데 파라미터를 생략한 경우

    => sql을 잘못 입력해서 SqlException이 발생하기도 함

 

6. 앞에서 작성한 xml을 호출할 DAO를 생성

    => 여기까지는 프로젝트 종류가 달라지더라도 코드를 수정할 필요가 없음

    => Service나 Controller는 URL 통신인지, Socket통신인지에 따라서 달라질수 있음

 

7. GoodDAO 클래스의 bean을 자동으로 생성해주는 설정을 SpringBeanConfinguration 파일에 추가

    => Spring MVC Project면 할 필요가 없는 작업

  1) applicationContext.xml 파일에 context 네임스페이스를 추가

 

  2) applicationContext 파일에 어노테이션을 사용할 수 있도록 하고 bean을 자동생성할 패키지를 등록

<!-- 어노테이션을 사용할 수 있도록 해주는 설정 -->
<context:annotation-config />
<!-- bean을 자동 생성할 패키지 등록 -->
<context:component-scan base-package="mybatis" />

 

8. main 메소드에서 DAO를 가져와서 사용

### java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for good.allgoeod

=> 위의 예외는 sql이름을 잘못 부른 것, mapper 파일에 작성한 id와 DAO에서 호출한 이름을 확인

### 예외 메시지

=> 예외메시지에 SQLException이 보이면 SQL 문장을 잘못 입력한 것이므로 mapper 파일에 가서 SQL을 수정

### 부적합한 열 유형

=> 파라미터를 대입하지 않았거나 DTO를 만들때 자료형을 잘못설계한 것

 

**데이터 넣기

1. 데이터 삽입

    => 삽입작업시 primary key, unique 설정된 컬럼이 있다면 이 컬럼의 값을 어떤 식으로 만들 것인지 생각해보아야 함

  1) Sequence나 Auto_Increment를 이용하여 자동으로 값을 설정

    => Sequence나 Auto_Increment는 증가만 하고 감소, 삭제는 불가, 초기화는 가능

    => 입력후 바로 삭제하는 경우 번호를 재사용 할 수 없음

    => 사용하는 관계형 데이터베이스마다 생성방법이 다름

  2) Primary key나 unique한 데이터가 정수라면 가장 큰 정수 값을 찾아서 +1 해주는 방법

    => 별도의 SQL을 한번 수행

  3) 직접 입력 받는 방법

    => 중복 검사를 수행해야 함

  4) 1번, 2번으로 생성한 데이터는 순번 이외의 아무런 기능도 없음

 

2. 데이터 수정

    => 데이터 수정은 데이터 삽입과 동일한 형태

    => 기본키를 가지고 데이터를 찾아와서 데이터를 화면에 출력한 후 수정 할 수 있도록 해주고 수정 작업을 처리   

    => 정수가 리턴되는데 0이 아닌 양수면 조건에 맞는 데이터를 수정, 0이면 데이터가 없어 수정하지 않았다는 의미

 

3. 데이터 삭제

    => 삭제는 구현시 비밀번호를 입력받거나 정말로 삭제할 것인지 묻는 작업을 수행하고 삭제 진행

    => 실 업무에서는 삭제보다는 대부분 특정 컬럼값을 변경하여 삭제된 것처럼 하는 경우가 많음

    => 메소드 구현시는 기본키를 매개변수로 받아서 삭제

GenericXmlApplicationContext context = 
	new GenericXmlApplicationContext(
		"applicationContext.xml");
			
GoodDAO goodDAO = context.getBean(GoodDAO.class);
//전체 데이터 가져오기
//System.out.println(goodDAO.allGood());
			
//코드를 이용해서 데이터 1개 찾아오기
//System.out.println(goodDAO.getGood(1));
			
//삽입할 데이터를 생성
/*
Good good = new Good();
good.setCode(8);
good.setName("수박");
good.setRegdate(new Date());
			
System.out.println(goodDAO.insertGood(good));
*/
			
//데이터 수정 확인
/*
Good good = new Good();
good.setCode(1);
good.setName("무화과");
good.setRegdate(new Date());
			
System.out.println(goodDAO.updateGood(good));
*/
			
System.out.println(goodDAO.deleteGood(8));

 

**새로운 데이터베이스 작업이 필요한 경우

1. mapper 파일에 필요한 SQL을 작성

<select id="searchname"
	parameterType="java.lang.String"
	resultType="mybatis.domain.Good">
		select code, name, regdate
		from goods
		where name like '%' || #{name} || '%'
			
</select>

    => MySQL에서는 where name like concat('%', #{name}, '%')

 

2. DAO에 적절한 메소드를 만들어서 호출

//name의 일부분을 가지고 name이 포함된 데이터를 찾아오는 메소드
public List<Good> searchName(String name){
	return sqlSession.selectList("good.searchname", name);
}

 

3. main 메소드에서 테스트

System.out.println(goodDAO.searchName("감"));

 

**procedure 실행

1. procedure 

    => 자주 사용하는 SQL문을 하나의 이름으로 만들어두고 이름을 호출하는 형태로 실행

    => 프로그래밍 언어의 메소드처럼 사용

    => 프로시저의 장점

        - 실행속도가 빠름 : 한번 호출되면 메모리에 적재된 상태에서 다음 호출에 이용됨

        - 보안 향상 : SQL 테이블의 구성을 숨길 수 있음

    => 프로시저는 데이터베이스 종류마다 다른 문법으로 작성

    => Oracle에서 프로시저 만드는 문법을 PL/SQL이라고 하고, MS-SQL Server에서는 T-SQL이라고 함

 

2. MyBatis에서 프로시저 호출

{
	call procedure명(파라미터)
}

    => 태그에는 statementType 속성에 CALLABLE을 설정해서 CallableStatement가 호출하도록 해주어야 함

    => SqlSession이 호출 할 때는 이전처럼 selectList, selectOne, insert, delete, update 메소드로 호출

 

3. MySQL의 프로시저 생성

  1) 프로시저 생성

-- MySQL 프로시저
create procedure insertgood(
	in vcode int,
	in vname varchar(20),
	in vregdate date
)
begin
	insert into goods(code, name, regdate)
	values(vcode, vname, vregdate);
end;

 

  2) 매퍼 파일에서 데이터를 삽입하는 SQL을 수정

 <!-- 프로시저를 이용한 작업 -->
<insert id="insertgood" 
	parameterType="mybatis.domain.Good" 
	statementType="CALLABLE">
	{call insertgood(#{code}, #{name}, #{regdate})
</insert>

 

  3) main메소드에서 테스트

 

 

**인터페이스 이용

    => xml 파일 대신에 인터페이스를 이용하여 MyBatis 연동 가능

    => 이 방식을 이용하면 별도의 DAO 클래스가 필요 없음

1. 인터페이스에서 SQL 생성

@Select("sql 구문")

메소드 선언

    => 검색이면 select, 삽입이면 insert, 갱신이면 update, 삭제면 delete를 사용

 

2. MyBatis 사용을 위한 Bean 생성 코드

    => SqlSessionFactoryBean에는 dataSource만 설정

<bean id="아이디" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<property name="mapperInterface" value="매퍼인터페이스 경로" />
    <property name="sqlSessionFactory" ref="앞에서 만든 bean의 id" />
</bean>

 

3. 인터페이스 주입 받아서 메소드 호출

 

**XML로 작업했던 내용을 인터페이스로 수정

1. pom.xml 파일에 데이터베이스, mybatis 관련 의존성을 설정

    => Oracle, Spring-JDBC, MyBatis, MyBatis-Spring

 

2. Spring Bean Configuration 파일에 data Source Bean 생성 코드를 작성하고 데이터베이스 연결이 되는지 확인

  1) applicationContext 파일에 데이터베이스 접속 정보 작성

 

  2) 테스트할 메소드에서 윈의 bean을 가져와서 테스트하는 코드를 작성하고 실행

public static void main(String[] args) {
	try {
		//스프링 설정 파일의 경로를 설정
		GenericXmlApplicationContext context = 
				new GenericXmlApplicationContext("applicationContext.xml");
		
		//데이터베이스 접속 정보 가져오기
		DataSource ds = context.getBean(DataSource.class);
		Connection con = ds.getConnection();
		System.out.println(con);
			
		context.close();
	}catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
}

 

  3) 예외가 발생하지 않아야 하고 콘솔에 Connection관련 텍스트가 출력되어야 함

 

3. DTO 클래스 작성

 

4. SQL을 저장하고 실행할 인터페이스를 생성하고 필요한 SQL을 작성

package mybatis.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import mybatis.domain.Good;

//MyBatis Mapper로 사용할 인터페이스
public interface GoodMapper {
	//전체 데이터를 가져오는 메소드
	@Select("select code, name, regdate from goods")
	public List<Good> allGood();
	
	//code를 가지고 하나의 데이터를 찾아오는 메소드
	@Select("select code, name, regdate "
			+ "from goods where code=#{code}")
	public Good getGood(int code);
	
	//데이터를 삽입하는 메소드
	@Insert("insert into goods(code, name, regdate) "
			+ "values(#{code}, #{name}, #{regdate})")
	public int insertGood(Good good);
	
	//데이터를 수정하는 메소드
	@Update("update goods "
			+ "set name=#{name}, regdate=#{regdate} "
			+ "where code = #{code}")
	public int updateGood(Good good);
	
	//데이터를 삭제하는 메소드
	@Delete("delete from goods where code = #{code}")
	public int deleteGood(int code);
	
}

 

5. 스프링 설정 파일에 매퍼 인터페이스를 사용하는 MyBatis Bean을 생성

 <!-- Mapper 인터페이스를 사용하는 MyBatis bean -->
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
</bean>
	
<bean id="goodMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<property name="mapperInterface" value="mybatis.dao.GoodMapper" />
	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

 

6. GoodMapper를 주입받아 사용하면 됨

public static void main(String[] args) {
	try {
		//스프링 설정 파일의 경로를 설정
		GenericXmlApplicationContext context = 
				new GenericXmlApplicationContext("applicationContext.xml");
			
//		//데이터베이스 접속 정보 가져오기
//		DataSource ds = context.getBean(DataSource.class);
//		Connection con = ds.getConnection();
//		System.out.println(con);

		GoodMapper goodMapper = context.getBean(GoodMapper.class);
		System.out.println(goodMapper.allGood());


		context.close();
	}catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
}

    => XML을 이용하는 방식은 XML파일에 SQL을 작성하고 DAO 클래스를 만들어서 SQL을 호출하는 형태

    => Mapper 인터페이스를 이용하면 하나의 인터페이스에 SQL과 메소드를 같이 선언하여 사용

        - 복잡한 동적인 SQL을 작성할 때는 불편

 

**MyBatis 로그 확인

    => MyBatis 사용시 로그를 더 자세히 출력하고자 하면 log4jdbc-log4j2-jdbc4 라이브러리를 사용할 수 있음

1. pom.xml 파일에 의존성을 설정

<!-- MyBatis Log 기록 라이브러리 -->
<dependency>
	<groupId>org.bgee.log4jdbc-log4j2</groupId>
	<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
	<version>1.16</version>
</dependency>

 

2. 루트 디렉토리(src, src/main/java, src/main/resources 등)에 프로퍼티 설정파일을 추가

    => 파일명 : log4jdbc.log4j2.propertise

    => 내용 : log4jdbc.spylogdelgator.name = net.sf.log4jdbc.log.slf4j.Slf4SpyLogDelegator

 

3. 데이터베이스 접속부분에 log4jdbc 를 추가

    => url의 oracle 앞에 추가

    - <property name="url" value="jdbc:log4jdbc:mysql://localhost:3306/myproject

 

**Transaction

    => 논리적 작업단위

    => 한번에 이루어져야 하는 작업의 단위

1. ACID 성질

    => Atomicity : 트랜잭션은 All or Nothing

    => Consistency : 일관성이 있어야 함

    => Isolation : 다른 트랜잭션과 격리되어야 함

    => Durability : 한번 완료된 트랜잭션은 계속되어야 함

 

2. 트랜잭션 작업

    => Commit : 작업이 완료되어 데이터베이스 원본에 반영

    => RollBack : 작업을 취소하여 데이터베이스 원본에 반영하지 않는 것

    => SavePoint : rollback 할 지점을 만드는 것

 

3. Auto Commit

    => SQL이 정상적으로 수행되면 자동으로 Commit되는 경우

    => DDL(Create, Alter, Drop, Truncate, Rename등)을 성공적으로 수행한 경우

    => DCL(Grant, Revoke - 권한 부여및 회수)을 성공적으로 수행한 경우

    => 데이터베이스 접속 프로그램을 정상적으로 종료하는 경우

    => 데이터베이스가 정상적으로 종료되는 경우

 

4. Auto Rollback

    => 접속 프로그램이나 데이터베이스가 비정상적으로 종료된 경우

 

5. select는 트랜잭션과 아무런 상관이 없음

 

6. MyBatis는 트랜잭션 사용이 옵션이지만 Hibernate는 트랜잭션 사용이 필수