본문 바로가기

Spring boot/Bank App 만들기(deployment)

Bank App 만들기 ( deployment ) - MyBatis 설정 ( DB 접근 기술이란? )

공식 문서 확인

https://mybatis.org/mybatis-3/

 

mybatis – MyBatis 3 | Introduction

What is MyBatis? MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates almost all of the JDBC code and manual setting of parameters and retrieval of results. MyBatis can use

mybatis.org

 

 

 

1. MyBatis 란?

MyBatis는 자바 언어를 위한 데이터베이스 연동 프레임워크로, SQL 쿼리와 자바 코드를 매핑하고 관리하기 위한 도구를 제공하는 프레임워크이다. MyBatis는 SQL을 직접 작성하고 실행하는 대신 SQL 쿼리를 XML 파일이나 어노테이션을 사용하여 정의하고, 데이터베이스와 자바 객체 간의 매핑을 설정할 수 있도록 도와준다.

 

MyBatis는 자바 언어를 위한 데이터베이스 연동 프레임워크로, SQL 쿼리와 자바 코드를 매핑하고 관리하기 위한 도구를 제공하는 프레임워크이다. MyBatis는 SQL을 직접 작성하고 실행하는 대신 SQL 쿼리를 XML 파일이나 어노테이션을 사용하여 정의하고, 데이터베이스와 자바 객체 간의 매핑을 설정할 수 있도록 도와준다.

 

 

순수 자바 예시 코드
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCExample {
    public static void main(String[] args) {
        // 데이터베이스 연결 정보
        String url = "jdbc:mysql://localhost:3306/mydatabase";
        String username = "root";
        String password = "password";

        // 데이터베이스 연결
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            System.out.println("데이터베이스 연결 성공!");

            // SQL 쿼리 작성
            String sql = "SELECT * FROM users WHERE id = ?";

            // SQL 쿼리 실행을 위한 PreparedStatement 생성
            try (PreparedStatement statement = connection.prepareStatement(sql)) {
                // 파라미터 설정
                int userId = 1;
                statement.setInt(1, userId);

                // SQL 쿼리 실행 및 결과 가져오기
                try (ResultSet resultSet = statement.executeQuery()) {
                    // 결과 처리
                    if (resultSet.next()) {
                        int id = resultSet.getInt("id");
                        String name = resultSet.getString("name");
                        System.out.println("사용자 ID: " + id);
                        System.out.println("사용자 이름: " + name);
                    } else {
                        System.out.println("사용자를 찾을 수 없습니다.");
                    }
                }
            }
        } catch (SQLException e) {
            System.out.println("데이터베이스 연결 오류: " + e.getMessage());
        }
    }
}

 

 

 

 

MyBatis 주요 개념

  1. 매퍼(Mapper): 매퍼는 SQL 쿼리와 자바 메서드를 연결하는 인터페이스 또는 XML 파일이다. 매퍼는 데이터베이스와 상호 작용하기 위한 모든 SQL 작업을 정의한다.
  2. SQL 쿼리 매핑: MyBatis는 SQL 쿼리를 자바 코드에서 분리하여 XML 파일이나 어노테이션을 사용하여 정의한다. 이렇게 정의된 SQL 쿼리는 매퍼에서 호출되며, MyBatis가 데이터베이스에 전달하여 실행한다.
  3. 파라미터 매핑: MyBatis는 SQL 쿼리의 파라미터를 자바 객체와 매핑한다. 이를 통해 SQL 쿼리 내에서 자바 객체의 속성을 사용할 수 있다.
  4. 결과 매핑: MyBatis는 SQL 쿼리의 결과를 자바 객체로 변환하여 반환한다. 이를 통해 데이터베이스 결과를 자바 객체로 쉽게 사용할 수 있다.
  5. 세션(Session): MyBatis에서는 세션을 사용하여 데이터베이스와의 연결을 관리한다. 세션은 데이터베이스 연결을 열고 닫는 데 사용되며, SQL 쿼리를 실행할 때마다 세션을 생성하고 사용한다.

 

 

MyBatis에서 가장 자주 사용되는 XML 태그

  • <mapper> : 이 태그는 전체 XML 문서가 하나의 매퍼와 연관된다는 것을 나타낸다. namespace 속성은 MyBatis 매퍼 인터페이스의 fully qualified name을 사용하여, SQL 문과 매퍼 인터페이스 메서드 간의 연결을 명확히 한다.
  • <select> : SELECT 쿼리를 정의한다. id 속성은 매퍼 인터페이스의 메서드 이름과 일치해야 하며, resultType 속성은 쿼리 결과를 매핑할 자바 클래스를 지정한다. resultMap을 사용하면 더 복잡한 결과 매핑도 가능하다.
  • <insert>, <update>, <delete>: 이 태그들은 각각 INSERT, UPDATE, DELETE 쿼리를 정의합니다. parameterType 속성은 메서드로 전달될 파라미터의 타입을 정의한다. insert 태그에서는 useGeneratedKeyskeyProperty 속성을 사용하여 데이터베이스에서 생성된 키 값을 객체에 매핑할 수 있다.
  • <resultMap> : 쿼리 결과의 컬럼과 자바 객체의 프로퍼티 간의 매핑 규칙을 정의한다. 복잡한 조인이나 상속 관계 등을 표현할 때 유용하다.
  • <sql> : 재사용 가능한 SQL 조각을 정의한다. include 태그와 함께 사용하여 SQL 쿼리의 중복을 줄일 수 있다.
  • <if>, <choose>, <when>, <otherwise> : 이 태그들은 SQL 쿼리를 조건에 따라 동적으로 생성할 수 있게 해준다. 예를 들어, 특정 파라미터가 null이 아닐 경우에만 특정 조건을 포함시키는 등의 동적 쿼리를 작성할 때 사용된다.
  • <foreach> : 컬렉션의 각 항목에 대해 반복되는 SQL 조각을 생성할 때 사용된다. 주로 IN 절에서 여러 파라미터를 전달할 때 활용된다.

태그 사용 시 주의해야 할 점은, 동적 SQL을 사용할 때 쿼리의 가독성과 유지보수성을 위해 가능한 간결하게 유지하는 것이 좋다. 너무 복잡한 동적 쿼리는 디버깅과 유지보수를 어렵게 할 수 있으므로, 필요한 경우에만 사용하는 것이 바람직하다.

 

 

 

 

2. MyBatis 의존성 설정 확인

https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter/3.0.3

implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'

 

 

 

 

3. yml 파일 매퍼 설정 확인

#mybatis 설정 부분 입니다.   
mybatis: 
  mapper-locations:
    - classpath:mapper/**/*.xml  # MyBatis 매퍼 파일 위치를 설정합니다. **는 모든 디렉토리, *.xml은 모든 XML 파일을 의미합니다.
  configuration:
    map-underscore-to-camel-case: true  # 데이터베이스의 언더스코어 네이밍(예: column_name)을 자바의 카멜케이스 네이밍(예: columnName)으로 매핑합니다.
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # SQL 로깅 구현체를 설정합니다. 이 설정을 통해 콘솔에 SQL 로그를 출력합니다.

logging:
  level:
    org.apache.ibatis: DEBUG  # MyBatis 로깅 레벨을 DEBUG로 설정하여, 실행되는 SQL 쿼리와 내부 로깅 정보를 콘솔에 출력합니다.

classpath:mapper/**/*.xml은 mapper 디렉토리와 그 하위의 모든 디렉토리에 있는 XML 파일을 클래스 경로에서 찾겠다는 의미이다. 즉, MyBatis 매퍼 파일들이 프로젝트의 클래스 경로 아래 mapper 디렉토리에 위치해야 한다.

 

💡 클래스 경로(classpath)란?
자바 애플리케이션에서 클래스 경로는 클래스 로더(class loader)가 클래스를 로드할 때 검색하는 위치를 나타낸다. 좀 더 쉽게 설명하면 상위 경로에 대한 명시는 필요하지 않으며, classpath:를 기준으로 mapper 디렉토리부터 시작하여 그 하위 디렉토리에 있는 모든 XML 파일을 탐색한다.

 

 

 

 

4. mapper 패키지 및 xml 파일 생성

인터페이스 설계 - 역할을 설계하고 구현은 (xml 파일에서 처리)

 

 

UserRepository.java 파일 생성
package com.tenco.bank.repository.interfaces;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.tenco.bank.repository.model.User;

//interface 와 user.xml 매칭 - (메서드 명 기준)  
@Mapper // 반드시 선언해주어 한다. 
public interface UserRepository {
	public int insert(User user);
	public int updateById(User user);
	public int deleteById(Integer id);
	public User findById(Integer id);
	public List<User> findAll();
}

 

 

 

src/main/resources/user.xml 파일 생성 (UserRepository 구현체)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tenco.bank.repository.interfaces.UserRepository">
	
	<!-- 반드시 세미콜론을 제거 해야 한다.   -->
	<!-- id는 매칭되어 있는 인터페이스에 메서드 명과 같아야 한다.  -->	
	<insert id="insert">
		insert into user_tb(username, password, fullname, created_at) 
		values( #{username}, #{password}, #{fullname}, #{createdAt})
	</insert>
	
	<update id="updateById">
		update user_tb set username = #{username}, 
		                   password = #{password}, 
		                   fullname = #{fullname},
		                   where id = #{id} 
	</update>
	
	<delete id="deleteById">
		delete from user_tb where id = #{id}
	</delete>
	
	<select id="findById" resultType="com.tenco.bank.repository.model.User">
		select * from user_tb where id = #{id}
	</select>
	
	<select id="findAll" resultType="com.tenco.bank.repository.model.User">
		select * from user_tb
	</select>
</mapper>

: 쿼리는 항상 DB 에서 테스트 후에 복사 붙여 넣기 하자 (H2에 문법과 RDBMS 에서 적용되는 문법이 다를 수 있다.)

 

 

 

 

AccountRepository + account.xml 정의

AccountRepository.java 파일 생성
package com.tenco.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.tenco.bank.repository.model.Account;

//AccountRepository 인터페이스와 account.xml 파일을 매칭 시킨다. 
@Mapper 
public interface AccountRepository {
	public int insert(Account account);
	public int updateById(Account account);
	public int deleteById(Integer id, String name);
	
	// 고민! - 계좌 조회 
	// --> 한사람에 유저는 여러개의 계좌 번호를 가질 수 있다. 
	public List<Account> findByUserId(@Param("userId") Integer principalId);
	// --> account id 값으로 계좌 정보 조회 
	public Account findByNumber(@Param("number") String id);
	
	// 코드 추가 예정
}

 

 

 

 

account.xml 파일 생성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tenco.bank.repository.interfaces.AccountRepository">
	
	<!-- 반드시 세미콜론을 제거 해야 한다.   -->
	<!-- id는 매칭되어 있는 인터페이스에 메서드 명과 같아야 한다.  -->	
	
	<insert id="insert">
		insert into account_tb(number, password, balance, user_id, created_at)
		values(#{number}, #{password}, #{balance}, #{userId}, now())
	</insert>	
	
	<update id="updateById">
		update account_tb set number = #{number}, password = #{password},
			balance = #{balance}, user_id = #{userId} where id = #{id}
	</update>
	
	<delete id="deleteById">
		delete from account_tb where id = #{id}
	</delete>

	<select id="findByUserId" resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where user_id = #{userId} 
	</select>
	
	<select id="findByNumber"  resultType="com.tenco.bank.repository.model.Account">
		select * from account_tb where number = #{number}
	</select>
	
</mapper>

 

 

 

HistoryRepository + history.xml 정의

HistoryRepository.java 파일 생성
package com.tenco.bank.repository.interfaces;

import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.tenco.bank.repository.model.History;

//HistoryRepository, history.xml 파일을 매칭 시킨다. 
@Mapper 
public interface HistoryRepository {

	public int insert(History history);
	public int updateById(History history);
	public int deleteById(Integer id);
	
	// 거래내역 조회 
	public History findById(Integer id);
	public List<History> findAll();
	
	//코드 추가 예정 - 모델을 반드시  1:1 엔터티에 매핑을 시킬 필요는 없다. 
	// 조인 쿼리, 서브쿼리 
	
}

 

 

history.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tenco.bank.repository.interfaces.HistoryRepository">
	
	<insert id="insert">
		insert into history_tb(amount, w_balance, d_balance, w_account_id, d_account_id)
		                values(#{amount}, #{wBalance}, #{dBalance}, #{wAccountId}, #{dAccountId} )	
	</insert>
	
	<update id="updateById">
		update history_tb
		set amount = #{amount},
			w_balance = #{wBalance},
			d_balance = #{dBalance},
			w_account_id = #{wAccountId},
			d_account_id = #{dAccountId}
			where id = #{id}			
	</update>
	
	<delete id="deleteById">
		delete from history_tb where id = #{id}
	</delete>
	
	<select id="findById" resultType="com.tenco.bank.repository.model.History">
		select * from history_tb where id = #{id}
	</select>
	
	<select id="findAll" resultType="com.tenco.bank.repository.model.History">
		select * from history_tb
	</select>
	
</mapper>
728x90