MyBatis, DB연동 및 CRUD

Doridam·2023년 6월 9일
1

MyBatis 🕊

목록 보기
2/2

MyBatis DB 연동

이전 게시글에서 MyBatis의 간단한 사용법 및 환경설정에 대해 알아보았다. 이번에는 MyBatis의 DB 연동과 CRUD 연산 수행에 대해 알아본다.
SpringBoot에서 MyBatis를 이용한 DB 연동 방법에는 두가지가 있다.

1. MyBatis config.xml을 사용한 연동

특징

  • 재사용성 : 파일로써 독립적으로 관리되기 때문에 프로젝트나 모듈에 따라 재사용 할 수 있다.
  • 설정과의 분리 : 다른 설정들과 분리함으로써 설정과의 의존성 관리에 용이하다.
  • 업데이트시 재시작 : 파일 수정 후 어플리케이션을 재시작해야 한다.
  • 경로 설정 : Application.properties에 config.xml에 대한 경로를 지정해야 한다.
  • 작성 방식 : XML 형식에 따라 작성한다.

설정 방법

<!-- xml 선언-->
<?xml version="1.0" encoding="UTF-8" ?>
<!-- mybatis config 파일임을 명시함 -->
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 설정에 대한 선언 -->
<configuration><!-- 1. DB를 사용할 환경을 정의한다. -->
	<environments default="development">
		<!-- 2. environment id를 구분하여 연결할 DB를 여러개 구성할 수 도 있음 -->
		<environment id="development">
            <!-- 3. 트랜잭션 관리자를 지정한다. -->
			<transactionManager type="JDBC"/>
				<!-- 4. 데이터 소스 타입을 정의한다. -->
				<dataSource type="POOLED">
                  	<!-- 5. DB 드라이버 -->
					<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
					<!-- DB URL -->
					<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
                  	<!-- DB user명 -->
					<property name="username" value="mybatis"/>
                  	<!-- DB user password -->
					<property name="password" value="mybatis"/>
				</dataSource>
		</environment>
  </environments><!-- mapper 파일 경로지정 영역 -->
	<mappers>
      	<!-- 6. mapper 파일에 대한 경로 지정 -->
		<mapper resource="mybatis/mappers/TestMapper.xml"/> 
	</mappers></configuration>

config.xml을 생성한 후 xml과 config 파일에 대한 선언 후 작성한다.

1. DB를 사용할 환경을 정의한다. 개발, 운영, 테스트에 따라 다른 DB를 사용해야한다면 해당 설정을 통해 여러개의 설정을 지정할 수 있다.

2. DB 환경을 구체적으로 정의한다. id를 구분하여 여러개의 DB 연결을 구성할 수도 있다.

3. 트랜잭션 관리자를 지정한다. 트랜잭션 관리 방식에 따라 type을 통해 관리자를 다르게 지정할 수 있다.

	JDBC : JDBC 드라이버를 통해 트랜잭션을 관리한다. 주로 사용됨.
	MANAGED : 어플리케이션을 통해 트랜잭션을 관리한다. Java의 경우 Java EE 컨테이너 
              혹은 서블릿 컨테이너에서 관리된다.
	XA : 분산 트랜잭션 관리를 위해 사용한다. 여러개의 DB의 트랜잭션을 처리해야할 때 사용한다.

4. 데이터 소스의 타입을 정의한다. 리소스의 접근 방식을 type을 통해 지정할 수 있다.

	POOLED : 커넥션 풀을 사용하는 방식. 일정한 수의 Connection 객체를 미리 생성하여 트랙픽 관리에 
    		유연하게 대처할 수 있다.
 	UNPOOLED : 커넥션 풀을 사용하지 않고, 직접 데이터 소스를 설정하는 방식. Connection 객체를 
    		매번 생성하고 종료하는 방식으로 오버헤드의 가능성이 높아 단일 스레드 또는 테스트 
            어플리케이션에서 주로 사용됨

5. DB에 대한 연결정보를 작성한다.

<!-- 5. DB 드라이버 -->
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<!-- DB URL -->
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/>
<!-- DB user명 -->
<property name="username" value="C##APITEST"/>
<!-- DB user password -->
<property name="password" value="APITEST"/>

작성은 OracleDB 기준으로 작성되었으며, 유저명 : APITEST PW : APITEST 이다. DB마다 드라이버 및 URL이 다르므로 작성시 유의해야 한다.

6. mapper에 대한 경로를 작성.

	<mappers>
      	<!-- 6. mapper 파일에 대한 경로 지정 -->
		<mapper resource="mybatis/mappers/TestMapper.xml"/> 
	</mappers>

기본 경로인 resources/mytbatis/mappers 폴더에 있는 TestMapper.xml을 mapper로 등록시킨다.

7. config.xml에 대한 경로 지정

mybatis.config-location=mybatis/config.xml

Application.properties에 config.xml에 대한 경로를 지정하여 config 파일임을 인식시킨다.

2. Application.properties를 사용한 연동

Application.properties에서도 Mybatis의 설정이 가능하다. 하지만 MyBatis의 모든 설정이 가능한것은 아니기때문에 세부 설정이 필요하다면 config.xml 파일과 같이 설정 해야한다.

특징

  • 중앙 집중화 설정 : 다른 의존성의 설정도 가능해서 하나의 파일에서 관리할 수 있는 장점이 있다.
  • Spring Boot의 자동 설정 : 설정을 추가시 자동으로 SpringBoot의 자동설정 기능이 작동하여 Bean을 등록하기 때문에 설정의 번거로움을 줄여준다.
  • 유연한 설정 변경 : 어플리케이션을 재시작하지 않고도 설정 변경이 가능하기 때문에 변경사항을 즉시 반영할 수 있다.
  • 제한된 옵션 : MyBatis의 모든 설정이 가능한것은 아니므로 세부설정이 필요하다면 config 파일이 필요하다.
  • 가독성 : 다른 의존성 설정으로 인한 가독성이 안좋아질 수 있다.
  • 작성 방식 : 키-값 형식에 따라 작성한다.

설정 방법

# URL 설정
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
# 유저명 설정
spring.datasource.username=C##APITEST
# 유저 PW 설정
spring.datasource.password=APITEST
# 유저 URL 설정
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# mapper 파일에 대한 경로 지정
mybatis.mapper-locations=mybatis/mappers/*.xml
# config.xml 을 사용할 경우 경로 지정
mybatis.config-location=mybatis/config.xml

먼저 DB 정보를 추가한다. OracleDB 기준으로 작성된 APITEST 유저의 정보이다.
위에서부터 순서대로 URL, 유저 이름, 비밀번호, 드라이버 명, mapper에 대한 경로를 작성한다.
기본적인 DB연동의 경우 config.xml에 비해서 간단한 편이지만, 세세한 설정이 필요하다면 config.xml으로 작성해야 한다.

※ 테스트를 위해 추가한 설정

원활한 테스트를 위해 Application.properties에 몇가지 설정을 추가했다.

# 카멜 케이스와 스네이크 케이스 자동 mapping 설정
mybatis.configuration.map-underscore-to-camel-case=true
# 지정 패키지의 하위 클래스들을 클래스명으로 별칭을 정함.
mybatis.type-aliases-package=com.apiTest

CRUD 수행

CRUD란 Create, Read, Update, Delete의 약자로서 데이터베이스의 기본적인 조작 기능을 의미한다.

환경 설정

※ 테스트 환경 : SpringBoot 2.7.12 / OracleDB 21c / IntelliJ IDE / Postman / Windows 10

1. DB Table 생성 및 더미 데이터 삽입

CREATE TABLE CRUD_TEST(
TEST_NO NUMBER PRIMARY KEY,
TEST_VALUE VARCHAR2(20) NOT NULL
);
INSERT INTO CRUD_TEST VALUES(1, 'ONE');
INSERT INTO CRUD_TEST VALUES(2, 'TWO');
INSERT INTO CRUD_TEST VALUES(3, 'THREE');
INSERT INTO CRUD_TEST VALUES(4, 'FOUR');
INSERT INTO CRUD_TEST VALUES(5, 'FIVE');
COMMIT;

테스트용 테이블 생성 후 간단한 더미 데이터를 삽입했다.

2. Dependency 추가

	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.3.1'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.oracle.database.jdbc:ojdbc8'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

Dependency 추가 목록

  • Spring Web
  • Lombok
  • OJDBC (Oracle JDBC)
  • MyBatis

3. application.properties 설정

# 오라클 db url 지정
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
# 오라클 드라이버 설정
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# 유저명
spring.datasource.username=C##APITEST
# 유저 비밀번호
spring.datasource.password=APITEST
#
# mapper 파일에 대한 경로 지정
mybatis.mapper-locations=mybatis/mappers/*.xml
# 카멜 케이스와 스네이크 케이스 자동 mapping 설정
mybatis.configuration.map-underscore-to-camel-case=true
# 지정 패키지의 하위 클래스들을 클래스명으로 별칭을 정함.
mybatis.type-aliases-package=com.apiTest

4. 패키지 구조

MVC 패턴에 따른 패키지 구조로 생성했다.

Controllermodel
Service
Repository

INSERT 수행

1. Controller의 PostMapping 메소드 작성

	// insertTest url의 post로 mapping
    @PostMapping("/insertTest")
    public String insertTest(
    		// CrudTest 객체로 값을 전달받음
            @ModelAttribute CrudTest crudTest
    ) {		
    	// null 값 방지를 위한 조건식
        if (crudTest.getTestNo() != null && crudTest.getTestValue() != null) {
            /*
            	param : CrudTest crudTest
                return : String "성공" 또는 "실패"
                crudTest 객체를 service > repository로 전달하여 insert한다.
                성공 여부에 따라 문자열이 리턴된다.
            */
            return ctService.crudTestByInsert(crudTest);
        }else {
        	// crudTest객체가 null로 인한 예외 리턴
            return "값이 없거나 누락됨";
        }
    }

2. Service interface 및 구현체 메소드 작성

// CrudTestService
    String crudTestByInsert(CrudTest crudTest);
// CrudTestSerivceLogic
    @Override
    public String crudTestByInsert(CrudTest crudTest) {
    	// crudTest를 repository로 전달 후 결과값을 리턴받는다.
        int result = ctRepository.crudTestByInsertData(crudTest);
        // 결과에 따라 성공, 실패 에 대한 문자열을 Controller로 리턴한다.
        if (result > 0) {
            return "데이터 삽입 성공";
        } else {
            return "데이터 삽입 실패";
        }
    }

3. Repository interface 및 구현체 메소드 작성

// CrudTestRepository interface
	int crudTestByInsertData(CrudTest crudTest);
// CrudTestRepositoryLogic
    @Override
    public int crudTestByInsertData(CrudTest crudTest) {
    	// DI 받은 session을 이용해 CrudMapper의 insert id=crudByInsertTest로 접근하여 insert를 수행한다.
        return session.insert("CrudTestMapper.crudByInsertTest",crudTest);
    }

4. mapper 작성

    <insert id="crudByInsertTest">
        INSERT INTO CRUD_TEST VALUES(#{testNo}, #{testValue})
    </insert>

CRUD_TEST 테이블에 INSERT 를 수행한다. controller > service > repository로 전달받은 crudTest 객체의 변수를 파라미터로 사용한다.

5. 실행결과

삽입 전 삽입 후

Postman을 사용해서 Body에 CrudTest에 따른 값을 전송 한 결과 데이터 삽입 성공 메시지와 DB의 데이터 변화를 확인할 수 있었다.

SELECT 수행

1. Controller의 PostMapping 메소드 작성

	// Get method로 selectTest 요청이 들어올 경우
    @GetMapping("/selectTest")
    public List<CrudTest> selectTest() {
    	// Service로부터 받아온 List<CrudTest>객체를 ResponseBody를 통한 json 형태로 반환한다.
        return ctService.crudTestBySelect();
    }
}

2. Service interface 및 구현체 메소드 작성

// CrudTestService
    List<CrudTest> crudTestBySelect();
// CrudTestSerivceLogic
    @Override
    public List<CrudTest> crudTestBySelect() {
    	// repository로부터 받아온 데이터를 controller로 반환한다.
        return ctRepository.crudTestBySelectList();
    }

3. Repository interface 및 구현체 메소드 작성

// CrudTestRepository interface
	List<CrudTest> crudTestBySelectList();
// CrudTestRepositoryLogic
    @Override
    public List<CrudTest> crudTestBySelectList() {
    	// selectList를 통해 resultSet을 여러값을 받는다.
        // CrudeTestMapper로부터 select id=crudBySelectTest 를 수행후 리턴값을 service로 반환한다.
        return session.selectList("CrudTestMapper.crudBySelectTest");
    }

4. mapper 작성

    <select id="crudBySelectTest" resultType="CrudTest">
        SELECT * FROM CRUD_TEST
    </select>

SELECT 하고 반환값은 List로 반환된다.

5. 실행결과


결과값을 JSON 형태로 잘 받아온것을 확인했다.

UPDATE 수행

1. Controller의 PatchMapping 메소드 작성

  // 일부 데이터 업데이트에 따른 patch Mapping
  @PatchMapping("/updateTest")
    public String updateTest(
  			// crud객체로 데이터를 전달 받는다.
            @ModelAttribute CrudTest crudTest
    ){
  		// null 값 방지를 위한 조건식
        if (crudTest.getTestNo() != null && crudTest.getTestValue() != null) {
  			/*
            	param : CrudTest crudTest
                return : String "성공" 또는 "실패"
                crudTest 객체를 service > repository로 전달하여 update 작업을 수행한다.
                성공 여부에 따라 문자열이 리턴된다.
            */
            return ctService.crudTestByUpdate(crudTest);
        }else {
  			// crudTest객체가 null로 인한 예외 리턴
            return "값이 없거나 누락됨";
        }
    }

2. Service interface 및 구현체 메소드 작성

// CrudTestService
    String crudTestByUpdate(CrudTest crudTest);
// CrudTestSerivceLogic
      @Override
    public String crudTestByUpdate(CrudTest crudTest) {
  		// crudTest를 repository로 전달 후 결과값을 리턴받는다.
        int result = ctRepository.crudTestByUpdateData(crudTest);
  		// 결과에 따라 성공, 실패 에 대한 문자열을 Controller로 리턴한다.
        if (result > 0) {
            return "데이터 업데이트 성공";
        } else {
            return "데이터 업데이트 실패";
        }
    }

3. Repository interface 및 구현체 메소드 작성

// CrudTestRepository interface
    int crudTestByUpdateData(CrudTest crudTest);
// CrudTestRepositoryLogic
    @Override
    public int crudTestByUpdateData(CrudTest crudTest) {
  		// DI 받은 session을 이용해 CrudMapper의 update id=crudByUpdateTest로 접근하여 update 를 수행한다.
        return session.update("CrudTestMapper.crudByUpdateTest",crudTest);
    }

4. mapper 작성

    <update id="crudByUpdateTest">
        UPDATE CRUD_TEST SET TEST_VALUE = #{testValue} WHERE TEST_NO = #{testNo}
    </update>

CRUD_TEST 테이블에 UPDATE 를 수행한다. controller > service > repository로 전달받은 crudTest 객체의 변수를 파라미터로 사용한다.

5. 실행결과

삽입 전 삽입 후

Postman을 사용해서 Body에 CrudTest에 따른 값을 전송 한 결과 데이터 업데이트 성공 메시지와 DB의 데이터 변화를 확인할 수 있었다.

DELETE 수행

1. Controller의 delete 메소드 작성

  // 일부 데이터 업데이트에 따른 patch Mapping
  	@DeleteMapping("/deleteTest")
    public String deleteTest(
  			// crud객체로 데이터를 전달 받는다.
            @ModelAttribute CrudTest crudTest
    ){
  		// null 값 방지를 위한 조건식
        if (crudTest.getTestNo() != null) {
  			/*
            	param : CrudTest crudTest
                return : String "성공" 또는 "실패"
                crudTest 객체를 service > repository로 전달하여 delete 작업을 수행한다.
                성공 여부에 따라 문자열이 리턴된다.
            */
            return ctService.crudTestByDelete(crudTest);
        }else {
  			// crudTest객체가 null로 인한 예외 리턴
            return "값이 없거나 누락됨";
        }
    }

2. Service interface 및 구현체 메소드 작성

// CrudTestService
    String crudTestByDelete(CrudTest crudTest);
// CrudTestSerivceLogic
      @Override
    public String crudTestByDelete(CrudTest crudTest) {
  		// crudTest를 repository로 전달 후 결과값을 리턴받는다.
        int result = ctRepository.crudTestByDeleteData(crudTest);
  		// 결과에 따라 성공, 실패 에 대한 문자열을 Controller로 리턴한다.
        if (result > 0) {
            return "데이터 삭제 성공";
        } else {
            return "데이터 삭제 실패";
        }
    }

3. Repository interface 및 구현체 메소드 작성

// CrudTestRepository interface
    int crudTestByDeleteData(CrudTest crudTest);
// CrudTestRepositoryLogic
    @Override
    public int crudTestByDeleteData(CrudTest crudTest) {
  		// DI 받은 session을 이용해 CrudMapper의 delete id=crudByDeleteTest 접근하여 delete 를 수행한다.
        return session.update("CrudTestMapper.crudByDeleteTest",crudTest);
    }

4. mapper 작성

    <delete id="crudByDeleteTest">
        DELETE FROM CRUD_TEST WHERE TEST_NO = #{testNo}
    </delete>

CRUD_TEST 테이블에 DELETE 를 수행한다. controller > service > repository로 전달받은 crudTest 객체의 변수를 파라미터로 사용한다.

5. 실행결과

삽입 전 삽입 후

Postman을 사용해서 Body에 CrudTest에 따른 값을 전송 한 결과 데이터 삭제 성공 메시지와 DB의 데이터 변화를 확인할 수 있었다.


마무리

MyBatis DB 연동과 CRUD 연산작업에 대한 부분을 알아보았다. CRUD를 수행하고 그것에 대한 결과가 제대로 나오는것을 확인했다.

profile
도리도리담담 🐤🐥🐤🐥🐣🐣

0개의 댓글