반응형

📝 Mybatis 연결하기

<bean id="dataSource"
    class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName"
        value="com.mysql.cj.jdbc.Driver" />
    <property name="url"
        value="jdbc:mysql://localhost:3306/customer?characterEncoding=UTF-8" />
    <property name="username" value="root" />
    <property name="password" value="1234" />

</bean>

<bean id="sqlSessionFactory"
    class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation"
        value="classpath:mybatis/mybatis-config.xml" />
    <property name="mapperLocations"
        value="classpath:mybatis/mappers/**/*mapper.xml" />
</bean>
<!-- classpath란 src/main/java를 의미한다. classpath:com.mycompany.myapp -->



<bean id="sqlSession"
    class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory" />
</bean>

root-context.xml

<!-- Mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

<!-- Mybatis-Spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.2</version>
</dependency>

pom.xml

package com.company.macro;

import java.util.List;

import com.company.macro.VO.CustomerVO;

public interface MybatisImpl {

	public String selectCount();
	public List<CustomerVO> selectAll();
	public void insertAll();
	
//	@Select("SELECT * FROM customer")
//	public String selectAll();
}

MybatisImpl.java

 

query문의 이름과 반환 타입을 선언합니다.

public class CustomerVO {
	private String customer_no;
	private String customer_nm;
	// DB에 있는 컬럼명과 VO와 동일하게 가야한다.

	
	public String getCustomer_no() {
		return customer_no;
	}
	public void setCustomer_no(String customer_no) {
		this.customer_no = customer_no;
	}
	public String getCustomer_nm() {
		return customer_nm;
	}
	public void setCustomer_nm(String customer_nm) {
		this.customer_nm = customer_nm;
	}
	
	
}

CustomerVO.java

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- 상기 내용은 반드시 필요한 내용 -->
<configuration>
   <properties>
        <property name="mybatis 안에서 사용될 column의 이름" value="디비 컬럼명" />
        <property name="tblCustomer" value="customer" />
        <property name="customer_no" value="customer_no" />
        <property name="customer_nm" value="customer_nm" />
        
        <property name="tblMember" value="tbl_member"/>
        <property name="colUserid" value="userid"/>
        <property name="colPassword" value="password"/>
        <property name="colEmail" value="email"/>
    </properties>

    <typeAliases>
        <typeAlias alias="customerVO" type="com.company.macro.VO.CustomerVO" />
    </typeAliases>
    <!-- VO가 들어있는 패키지명  -->
</configuration>

webapps/resources/mybatis-config.xml

 

property는 mapper.xml에서 쓰이는

typeAliases는 mapper.xml의 select해서 나온 데이터를 VO에 바로 담을수 있게 사용하겠다는 의미입니다.

<?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">
<!-- 상기 내용은 반드시 필요한 내용 -->

<!-- interface 명 -->
<mapper namespace="com.company.macro.mybatisImpl">

	<select id="selectCount" resultType="string">
		SELECT count(*) FROM customer
	</select>

	<select id="selectAll" resultType="customerVO">
		SELECT * FROM ${tblCustomer}
	</select>

</mapper>

webapps/resources/mappers/mapper.xml

 

resultType에는 typeAliases에 적은 VO를 사용할 수 있습니다.

${property name명} 으로 name에 해당하는 value를 가져올 수 있습니다.

 

 

 

📝 Mybatis 동작 구조

 

 

 

Mybatis 사용시 데이터 흐름

사용자 → 컨트롤러 → Servie(interface)[DI용 + 규격화] → ServiceImpl(class) → Dao(interface)[DI용 + 규격화] → mapper.xml(xml이지만 Dao에 설정된 interface를 따라감 implements한 class라고 생각하면 됨) → DB → 실행결과 → ServiceImpl → Controller → 사용자

 

Service → 비즈니스 로직 (DaoImpl을 통해 여러가지 쿼리문(Dao를 1개 이상 실행)을 가져오고 Java를 이용해 데이터를 만들어 반환 가능)

Dao       → DB Query 실행문이 들어감 (1개의 Query문)

mapper.xml → Query문에 대한 내용

Interface 사용 이유 → DI 하기 위함과 규격화를 위해 사용 → DI를 제대로 활용할 줄 몰라서 아직 와닿지 않음

 

* 참고 : 보통은 어떻게 처리하는지 모르겠지만 Service에 로직이 길어졌는데 해당 부분에 공통 로직이 있어서 (로그인 세션 체크 등...) 공통 API로 뺄 때 그 안에 Mapper를 이용해야하는 경우(DB Connection이 들어가는 로직)일 경우 해당 Util(공통 API)도 또한 Mapper를 Injection해서(Autowired) 사용할텐데 해당 Mapper를 이용하기 위해서는 @Component 또는 @Service 등 해당 클래스를 IoC컨테이너에 담아야한다.

 

LoginUtil util = new Util
LoginVo vo = util.getSessionInfo()
----------- (X)

@Autowired
LoginUtil loginUtil;

....

LoginVo vo = loginUtil.getSessionInfo();
----------- (O)

 

<!-- spring version과 spring-jdbc version은 동일해야한다 -->
<!-- mybatis, mybatis-spring, spring-version 서로 호환되는 버전으로해야한다. -->
<!-- 만약 호환 안 되는 버전 사용시 sqlSession 객체 만들 때 에러 나오는데 버전이 맞지 않습니다라 이런 에러도 안 나와서 에러 체킹이 힘듬 -->
<!-- root-context.xml에서 설정후 실행 시 sqlSession 객체 만들 때 정상 호환 버전들이면 정상 작동하거나 잘못 된 부분에 대한 정확한 에러가 나옴 -->


<properties>
    <java-version>11</java-version>
    <org.springframework-version>5.0.5.RELEASE</org.springframework-version>
    <org.aspectj-version>1.6.10</org.aspectj-version>
    <org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
....

<!-- jdbc -->
<dependency>
  <groupId>org.mariadb.jdbc</groupId>
  <artifactId>mariadb-java-client</artifactId>
  <version>2.0.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<!-- Mybatis -->	
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!-- Mybatis + Spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	<bean id="dataSource"
	    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	    <property name="driverClassName"
	        value="org.mariadb.jdbc.Driver" />
	    <property name="url"
	        value="jdbc:mariadb://192.168.0.40:7001/ocr_search" />
	    <property name="username" value="testid" />
	    <property name="password" value="testpassword" />
	</bean>

	<bean id="sqlSessionFactory"
	    class="org.mybatis.spring.SqlSessionFactoryBean">
	    <property name="dataSource" ref="dataSource" />
        <property name="configLocation"
        value="classpath:mybatis-config.xml" />
    	<property name="mapperLocations"
     	   value="classpath:mappers/*Mapper.xml" />
	</bean>
	
	<!-- classpath란 src/main/java 또는 src/main/resources를 의미한다. -->
	<!-- classpath는 build path - source에 등록된 경로들이다.  -->
	<!-- classpath:mybatis-config.xml → Alias등 설정 하는 설정 파일 -->
	<!-- classpath:mappers/*Mapper.xml → Query를 적는 파일이다 -->
	<!-- src/main/java/mappers 폴더에 관리하겠다는 의미이고 파일명이 Mapper.xml로 끝나는 파일들을 등록하겠다는 의미이다. -->
	
	<bean id="sqlSession"
	    class="org.mybatis.spring.SqlSessionTemplate">
	    <constructor-arg ref="sqlSessionFactory" />
	</bean>
	
	<!-- mapper 어노테이션을 인식 시키기위해 필요 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	    <property name="basePackage" value="com.company.ocrsearch" />
	</bean>
		
</beans>

root-context.xml

 

SqlSession : DB 연결하기 위한 객체 및 Query 수행 역할 (SqlSessionFactory가 주입 됨)

SqlSessionFactory : mapper.xml에 대한 설정 및 mapper.xml(쿼리문) 읽을 객체

 

 

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

public class EngineDto {

	private String uuid = "";
	private String id = "";
	private String password = "";
	private String salt = "";
	private String fail_cnt ="";
	
	public String getUuid() {
		return uuid;
	}
	public void setUuid(String uuid) {
		this.uuid = uuid;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getSalt() {
		return salt;
	}
	public void setSalt(String salt) {
		this.salt = salt;
	}
	public String getFail_cnt() {
		return fail_cnt;
	}
	public void setFail_cnt(String fail_cnt) {
		this.fail_cnt = fail_cnt;
	}
	
	public String toStringShortPrefix() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
	
	
}

EngineDto.java

→ 데이터가 담기는 부분이다.

 

 

---- String 단일 매개변수 동적 쿼리 ----


import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

import global.configuration.Config;
import global.dto.DirectoryPathDto;
import global.dto.ServerInfoDto;


@Controller
public class EngineAPI {
	
	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/myabtis-test", method = RequestMethod.GET)
	public void testMybatis(String uuid) throws Exception{
		
		EngineDto engineDto = engineService.getUserInfo(uuid);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

→ Controller로 사용자의 요청이 들어오는 부분이다.

 


import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(String uuid) throws Exception;
}

EngineSerivce.java

→ 비즈니스 로직이 들어가며 규격화 및 DI 역할

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(String uuid) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(uuid);
		return engineDto;
	}

}

EngineServiceImpl.java

→ 실제 비즈니스 로직 구현한 것으로 DB 쿼리문을 사용할 경우 1개 이상의 Dao로 구성되어 있다.

받아온 쿼리문을 가지고 여러 동작을 하거나 여러개의 Dao를 호출해서 결과를 조합해서 Dto에 담아서 내려주는 형태

 

 

 

 

package com.company.ocrsearch.engine.dao;

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(String uuid) throws Exception;
	
}

EngineDao.java

→ root-context.xml에서 설정한 org.mybatis.spring.mapper.MapperScannerConfigurer에 포함된 패키지일 경우 @mapper 어노테이션을 찾아서 *mapper.xml과 연결해준다. interface이기 때문에 규격화및 DI 역할을 한다.

 

 

 

<?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.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="String"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			 FROM   admin_user	
			 WHERE 
			        uuid = #{UUID}
	</select>
</mapper>

EngineInfoMapper.xml 

→  쿼리문이 여기에 작성되며 연결할 interface명을 mapper namespace에 적는다.

xml을 EngineDao inteface를 implements한 class라고 생각하면 된다. 

 

id → Dao Interface에서 구현해야할 Funciton Name을 의미한다.

parameterType → 동적 쿼리를 위해 넘겨주는 파라미터를 의미한다. #{UUID}에 들어가는 파라미터는 String이 된다.

[ String 뿐만 아니라 HashMap 등이 있기 때문에 상황에 맞게 쓰면 된다 java.util.HashMap 등...)

String의 경우 HashMap의 경우 키 값으로 들어가기 때문에

resultType → 결과가 담길 곳을 의미한다. 일반적으로 여러개의 결과이고 데이터의 이동은 Dto에 담아서 하기 때문에 Dto를 적어줬다.

너무 당연한 이야기지만 내려받을 필드명과 Dto 필드는 같아야한다. (Alias 걸어서 Dto 수정 없이 Dto에 맞출 수도 있음)

[ String 뿐만 아니라 HashMap 등이 있기 때문에 상황에 맞게 쓰면 된다 java.util.HashMap 등...)

 

* 상속받는 놈(mapper.xml)이 ParameterType을 받기로 했기 때문에 Dao (interface)에도 그렇게 설정해줘야한다.

→ 매개변수가 1개의 String 이여야 함 (String의 경우 이름(#{UUID})을 정할 수 없기 때문에 들어온 그대로 들어가게 된다.)

     여러개의 매개변수를 받으려면 HashMap이나 Dto가 적당하다.

타입은 신경 안 쓰는 것 같다. → DB 필드가 문자 타입이여도 parameterType=int로 보내줘도 인식함

 

 

 

 

---- HashMap 매개변수 동적 쿼리 ----

 

pom.xml, root-context.xml, DTO는 동일합니다.

 

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;


@Controller
public class EngineAPI {
	
	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/mybatis-test", method = RequestMethod.GET)
	public void testMybatis(String uuid, String userId) throws Exception{
		
		HashMap<String, String> data = new HashMap<>();
		data.put("UUID", uuid); // mapper에서 대소문자 구분합니다.
		data.put("userId", userId);
		
		
		EngineDto engineDto = engineService.getUserInfo(data);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

 

import java.util.HashMap;

import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(HashMap<String, String> uuid) throws Exception;
}

EngineService.java

 

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(HashMap<String, String> uuid) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(uuid);
		return engineDto;
	}
}

EngineServiceImpl.java

 

import java.util.HashMap;

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(HashMap<String, String> uuid) throws Exception;
	
}

EngineDao.java

 

 

<?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.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="java.util.HashMap"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			 FROM   admin_user	
			 WHERE  uuid = #{UUID}
			   AND  id   = #{userId}
	</select>
</mapper>

EngineInfoMapper.xml

달라진 점은 HashMap을 파라미터로 넘긴다는 점입니다.

키의 값을 기준으로 동적쿼리가 생성되며 대소문자를 구분하니 주의하시길 바랍니다.

 

 

 

---- Dto 매개변수 동적 쿼리 ----

 

pom.xml, root-context.xml, DTO는 동일합니다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;


@Controller
public class EngineAPI {

	@Autowired
	@Qualifier("EngineSerive")
	EngineService engineService;
	
	@RequestMapping(value = "/mybatis-test", method = RequestMethod.GET)
	public void testMybatis(EngineDto requestEngineDto) throws Exception{

		EngineDto engineDto = engineService.getUserInfo(requestEngineDto);
		System.out.println(engineDto.toStringShortPrefix());
		
	}
	
}

EngineAPI.java

 

 

import com.company.ocrsearch.engine.dto.EngineDto;

public interface EngineService {
	
	public EngineDto getUserInfo(EngineDto requestEngineDto) throws Exception;
}

EngineService.java

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.company.ocrsearch.engine.dao.EngineDao;
import com.company.ocrsearch.engine.dto.EngineDto;
import com.company.ocrsearch.engine.service.EngineService;

@Service("EngineSerive")
public class EngineServiceImpl implements EngineService{

	@Autowired
	private EngineDao engineDao;
	
	@Override
	public EngineDto getUserInfo(EngineDto requestEngineDto) throws Exception {
		
		EngineDto engineDto = engineDao.selectUser(requestEngineDto);
		return engineDto;
	}
}

EngineServiceImpl.java

 

import org.apache.ibatis.annotations.Mapper;

import com.company.ocrsearch.engine.dto.EngineDto;

@Mapper
public interface EngineDao {
	public EngineDto selectUser(EngineDto requestEngineDto) throws Exception;
}

EngineDao.java

 

<?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.company.ocrsearch.engine.dao.EngineDao">

	<select id="selectUser" parameterType="com.company.ocrsearch.engine.dto.EngineDto"
		resultType="com.company.ocrsearch.engine.dto.EngineDto">
			SELECT
			        ID          -- 아이디
			      , FAIL_CNT    -- 로그인 실패 횟수
			  FROM  admin_user	
			 WHERE  uuid = #{uuid}
			   AND  id   = #{id}
	</select>
</mapper>

mapper.xml

 

 

 

 

 

https://wildeveloperetrain.tistory.com/26

 

@Component 와 @Bean, @Autowired 어노테이션 알아보기

자주 사용하면서도 정확하게 무슨 용도로 사용되는지, 어떤 동작 원리를 가지는지, 어떤 차이가 있는지 잘 몰랐던 부분에 대해서 공부합니다. 먼저 Annotation에 대해서 간단하게 알아보겠습니다.

wildeveloperetrain.tistory.com

 

반응형