수업내용/Spring

[2023.1.2.월] jdbc, mybatis를 이용한 스프링 데이터베이스 액세스

주니어주니 2023. 1. 2. 17:24

 

스프링과 데이터베이스 액세스 

 

  • 스프링의 데이터베이스 액세스 작업은 jdbc 연동, ibatis/mybatis 연동, JPA/hibernate 연동 등 다양한 데이터베이스 액세스 기술과 연동해서 데이터베이스 액세스 작업을 구현할 수 있다. 

  • 스프링의 데이터베이스 액세스 작업 특징
    • 다양한 데이터베이스 액세스 라이브러리와의 연동을 지원한다. 
      (jdbc, ibatis/mybatis, JPA/hibernate 등)

    • 일관된 데이터베이스 액세스 예외처리를 지원한다.
      • 모든 데이터베이스 액세스 오류에 대해서 DataAccessException 예외를 발생시킨다.
      • DataAccessException 클래스는 RuntimeException의 하위클래스여서 예외처리를 강제하지 않는다.
      • 데이터베이스의 종류와 상관없이 오류 상황에 맞는 적절한 예외 객체를 발생시킨다.
      • 예외 클래스의 이름도 예외상황에 맞는 이름이기 때문에 오류 상황을 더 쉽게 파악할 수 있다.

    • 데이터베이스와 연결을 유지하는 Connection 객체는 Connecton Pool을 이용해서 획득하고, 사용 후 반납한다.
      • Connection 객체는 데이터베이스와 미리 연결되어 있다.
      • Connection Pool은 데이터베이스와 연결을 유지하는 Connection 객체를 관리하는 객체다. 
      • Connection Pool 객체를 스프링 컨테이너가 생성하게 하거나, 생성된 Connection Pool을 스프링 컨테이너에 등록한다.

    • 선언적 트랜잭션 처리를 지원한다.
      • 프로그램 코딩없이 간단한 설정만으로 트랜잭션처리(커밋,롤백) 기능을 지원받을 수 있다. 

 

 

 


 

spring-jdbc를 이용한 데이터베이스 액세스

(얘는 원래 안쓰는데 관계를 보여주기 위한 예시) 

 

* spring-jdbc는 데이터베이스 액세스를 지원하는 JdbcTemplate 객체를 제공한다.
* JdbcTemplate는 spring-jdbc의 핵심 객체다.
* JdbcTemplate는 INSERT, UPDATE, DELETE, SELECT를 실행하는 메소드를 제공한다. 
* 사용자 정의 DAO 클래스는 JdbcTemplate 객체를 의존성 주입받아서 데이터베이스 액세스 작업을 수행한다.

 

 

1. 필요한 라이브러리 의존성 추가 (pom.xml)

 

  • jdbc 드라이버 라이브러리 의존성 추가

 

  • sping-jdbc 라이브러리 의존성 추가

 

- pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<!-- pom.xml 파일에서 사용되는 프로퍼티값(상수값)을 설정한다. -->
	<properties>
		<!-- 프로젝트의 자바버전을 정의한다. -->
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
		<!-- 로그출력을 지원하는 slf4j 라이브러리의 버전 정보를 상수로 정의한다. <version>${slf4j.version}</version>와 
			같이 ${} 형식으로 값을 사용할 수 있다. -->
		<slf4j.version>1.7.25</slf4j.version>
	</properties>
	
		
	<!-- 프로젝트의 의존성 라이브러리를 정의한다. -->
	<dependencies>

		<!-- 오라클 데이터베이스 연동을 지원하는 jdbc 드라이버 라이브러리 의존성 추가 -->
		<dependency>
		    <groupId>com.oracle.database.jdbc</groupId>
		    <artifactId>ojdbc11</artifactId>
		    <version>21.8.0.0</version>
		</dependency>
		<!-- spring-jdbc 라이브러리 의존성 정의
				* spring-jdbc 라이브러리 의존성의 spring-jdbc, spring-tx 라이브러리 의존성도 포함한다.
				* jdbc 기반의 데이터베이스 액세스 작업을 쉽게 구현하도록 지원하는 라이브러리다. 
		 -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.3.24</version>
		</dependency>
		
		<!-- spring-context 라이브러리 의존성 정의 
			spring-context 라이브러리 의존성은 spring-core, spring-beans, spring-aop, spring-expression 라이브러리도 포함한다. 
			* 스프링을 이용해서 객체 생성, 의존성 주입, 관점지향 프로그래밍 실습이 가능하다. -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.3.24</version>
		</dependency>
		<!-- 로그 출력을 지원하는 라이브러리 의존성 정의 -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.2.3</version>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jul-to-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>log4j-over-slf4j</artifactId>
			<version>${slf4j.version}</version>
		</dependency>

	</dependencies>
</project>

 

 

2. 스프링 컨테이너의 bean 설정 파일에 Connection Pool 객체 등록하기 (필수) 

 

- db.properties (데이터베이스 연결 정보 설정)

 

 

- context.xml

 

 

 

3. spring-jdbc 라이브러리가 지원하는 JdbcTemplate 객체를 스프링 컨테이너에 등록하기

 

- context.xml

 

 

4. DAO 클래스와  Service 클래스를 스캔에서 스프링 컨테이너가 객체 생성하고, 의존하는 객체를 자동 의존성 주입시키기

 

- context.xml

 

 



5. 데이터베이스 엑세스 작업을 구현하는 DAO에 JdbcTemplate 객체를 의존성 주입받을 수 있도록 구현하기

 

- UserDao

 

 

 

 

6. 핵심 업무로직을 구현하는 Service에 DAO 객체를 의존성 주입받을 수 있도록 구현하기

 

 

 

 

 

* service는 핵심기능을 구현하기 위해 dao 주입 필요

  dao는 데이터베이스 액세스를 해야 하니까 jdbcTemplate 주입 필요

 

 


 

 

* mybitis 참고링크 

https://mybatis.org/mybatis-3/ko/index.html

 

 

mybatis

 

  • SQL Mapping Framework
    • SQL 실행에 필요한 정보를 포함하고 있는 객체
    • SQL 실행 결과를 저장하는 객체를 SQL과 매핑하면 해당 SQL를 실행시키고, 결과값을 반환하는 프레임워크

  • ibatis의 후속 버전
    • ibatis와 많은 부분이 유사함

 

mybatis의 주요 구성요소

 

  • Mapper 인터페이스
    • 데이터베이스 액세스 작업이 정의된 인터페이스
    • 개발자가 작성 (구현객체를 mybatis에서 알아서 만들어줌) 
    • 예시

 

  • Mapper 파일
    • SQL과 객체 매핑 정보가 정의된 XML 파일이다. (테이블당 1개씩 작성)
    • Mapper 인터페이스의 전체이름을 namespace로 설정한다. 
    • 개발자가 작성
    • ibatis와 mybatis
         - parameterClass, resultClass -> parameterType, resultType
         - #파라미터명#  -> #{파라미터명}
         - dynamic 쿼리에서 <if>, <choose>, <when>, <otherwise>, <foreach> 태그를 사용해서 조건식처리, 반복처리 가능하다.
         - dynamic 쿼리에서 <where>, <set> 태그를 사용해서 불필요한 and와 ,를 제거할 수 있다. 
    • 예시

 

  • Mapper 인스턴스 (인터페이스 구현객체) 
    • Mapper 인터페이스를 구현한 객체다. 
    • 개발자가 구현하지 않는다.
    • mybatis와 mybatis-spring 라이브러리에서 Mapper 인터페이스를 구현한 객체를 자동으로 생성하고, 스프링 컨테이너의 빈으로 등록시킨다. 
      (그러기 위해서 해야할 일 : Mapper 인터페이스와 Mapper 파일의 namespace 일치시키기) 
      -> Mapper 인터페이스의 메소드를 재정의 함 

 

 

  • mybatis 매퍼파일의 <where> 태그

    ==================================================================
    <where> 태그를 사용하지 않는 경우
    ==================================================================
    select
                *
    from 
               web_products
    where
              <if test="deleted != null">
                     product_deleted = #{deleted}
              </if>
              <if test="minPrice > 0">
                     and product_price >= #{minPrice}
              </if>
              <if test="maxPrice > 0">
                     and product_price <= #{maxPrice}
              </if>
              <if test="categoryNo != null">
                     and product_category_no = #{categoryNo}
              </if>


    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();
    param.put("deleted", 'N');
    param.put("minPrice", 10000);

    select
              *
    from 
              web_products
    where
              product_deleted = #{deleted}
              and product_price >= #{minPrice}
    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();

    select
             *
    from 
             web_products
    where                                                               -- 문법 오류가 발생한다. (map에 아무것도 안담겨서 where만 남음)
    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();
    param.put("minPrice", 10000);
    param.put("maxPrice", 50000);

    select
              *
    from 
             web_products
    where
             and product_price >= #{minPrice}           -- 문법 오류가 발생한다. (deleted가 null이라서 and부터 시작) 
             and product_price <= #{maxPrice}
    --------------------------------------------------------


    ==================================================================
    <where> 태그를 사용하는 경우
    ==================================================================
    select
              *
    from 
             web_products
    <where>
            <if test="deleted != null">
                   product_deleted = #{deleted}
            </if>
            <if test="minPrice > 0">
                   and product_price >= #{minPrice}
            </if>
            <if test="maxPrice > 0">
                  and product_price <= #{maxPrice}
            </if>
            <if test="categoryNo != null">
                  and product_category_no = #{categoryNo}
            </if>
    </where>

    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();
    param.put("deleted", 'N');
    param.put("minPrice", 10000);

    select
              *
    from 
             web_products
    where
             product_deleted = #{deleted}
             and product_price >= #{minPrice}

    * <where>과 </where>사이에 동적으로 추가되는 조건이 하나라도 있으면
       where 키워드가 추가된다.
    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();

    select
              *
    from 
             web_products

    * <where>과 </where>사이에 동적으로 추가되는 조건이 하나도 없으면 
       where 키워드는 추가되지 않는다.
    --------------------------------------------------------
    Map<String, Object> param = new HashMap<>();
    param.put("minPrice", 10000);
    param.put("maxPrice", 50000);

    select
              *
    from 
              web_products
    where
              product_price >= #{minPrice}
              and product_price <= #{maxPrice}

    * <where>과 </where>사이에 동적으로 추가되는 조건이 있고,
       첫번째 조건에 and 키워드가 있으면, and를 제거한다.
    --------------------------------------------------------

 

 

mybatis의 주요 API

* 개발자가 직접 mybatis의 주요 API를 사용해서 코딩하는 경우는 절대 없다. 

  • SqlSessionFactoryBuilder
    • mybatis 환경설정 파일을 읽어서 SqlSessionFactory 객체를 생성한다
  • SqlSessionFactory
    • SqlSession 객체를 제공한다.
    • 애플리케이션이 실행되는 동안 싱글턴 객체로 관리되어야 한다. 
    • 주요 메소드
      SqlSession openSession() : 새로운 SqlSession 객체를 반환한다.
  • SqlSession
    • SQL을 실행하는 메소드를 제공하는 객체
    • Sql을 실행할 때마다 SqlSessionFactory의 openSession() 메소드를 실행해서 새로운 SqlSession 객체를 획득해서 사용하고, Sql 실행이 완료되면 close() 메소드를 호출해서 폐기한다.
    • 주요 메소드
      <T> T selectOne(String id, Object parameter)
                : 결과값이 하나 조회되는 SELECT 문을 실행시키고, 값을 반환한다.
      <E> List<E> selectList(String id, Object parameter)
                : 결과값이 여러개 조회되는 SELECT 문을 실행시키고, 값을 반환한다.
      int insert(String id, Object parameter)
                : INSERT 문을 실행시키고, 추가된 행의 개수를 반환한다.
      int update(String id, Object parameter)
                : UPDATE 문을 실행시키고, 변경된 행의 개수를 반환한다.
      int delete(String id, Object parameter)
                : DELETE 문을 실행시키고, 삭제된 행의 개수를 반환한다.
      void commit()
                : INSERT, UPDATE, DELETE 문의 실행결과를 테이블에 영구적으로 반영시킨다. 
      void rollback()
                : INSERT, UPDATE, DELETE 문의 실행의 테이블 반영을 취소시킨다. 
      void close()
                : SqlSession 객체를 폐기한다.
      <T> T getMapper(Class<T> type)
                : type에서 지정한 Mapper 인터페이스를 구현한 Mapper 인스턴스를 반환한다.
                  예시)
                          Mapper 인터페이스 정의
                                    public interface UserMapper {
                                            void insertUser(User user)
                                    }
                          Mapper 파일 정의 
                                    <mapper namespace="com.sample.mapper.UserMapper">
                                         <insert id="insertUser" parameterType="com.sample.vo.User"> 
                                                  insert into web_users (user_id, user_password, user_name, user_email)
                                                  values (#{id}, #{password}, #{name}, #{email})
                                        </insert>
                                  </mapper>
                         Mapper 인스턴스 획득하기
                                    SqlSession session = sqlSessionFactory.openSession();
                                    UserMapper instance = session.getMapper(UserMapper.class);
                                    * instance변수에는 UserMapper 인터페이스를 구현한 객체가 대입된다.
                                    * mybatis-spring 라이브러리는 모든 Mapper인터페이스를 스캔해서 Mapper 인스턴스를 생성하고, 스프링 컨테이너의 빈으로 등록시킨다