오늘도 배우자!

귀찮음에 배움을 멀리하지 않기를...

Develop/Server

SpringBoot Controller, Service, DAO 이해 - Service(2) + MyBatis

리다양 2019. 11. 12. 16:43

(저에겐)많은 분들이 방명록에도 댓글에도 찾아주셔서 감사한 마음으로 돌아왔습니다!!!!(짝짝짝)

 

이전 시간에는 Service가 어떤 역할이고 왜 사용해야하는지 알아보았는데요

 

사실 이 글을 읽는 분들에게 가장 필요한건 DB에서 정보를 가져와서 뿌려주는거라고 생각해요.

 

각설하고 딱 필요한 부분만 뙇 짚어보도록 하겠습니다.

 

우선 DB로부터 데이터를 가져오기 위해서 필요한 부분을 정리해보겠습니다.

 

1. DB 연동 시 생각해야 할 것들

1) DB가 세팅되어 있어야한다.(이 글에서는 MySQL을 사용해요!)

2) Spring Boot에서 DB와 연결을 하기 위해 일종의 장치(?)가 필요하다.

MySQL을 사용할 것이고 Spring은 자바 기반이니 mysql과 java를 연결해주는 장치가 필요하겠죠?

=> mysql + java + connect => (mysql-connector-java)

3) Java 코드에서 SQL을 호출해서 DB로부터 정보를 읽어오는데 도움을 주는 녀석(MyBatis)

 

요 세가지를 생각해보면서 시작해볼게요!!

DB 세팅하기위해 Database 강의를 시작하죠!! 는 뻥이에요 로컬에서 workbench를 사용하거나 직접 mysql을 다운받아서 로컬에서 스스로 세팅해주세요!!(나중에 DB 블로그는 새로운 메뉴를 만들어 따로 만들어보도록 노력할게요 ㅠㅠ)

 

2. Spring Boot에 MySQL 연동하기

Spring Boot에서는 직접 라이브러리를 다운받아서 넣는 레거시(?)한 방법을 되도록이면 사용하지 않고있어요

(Spring 에서도 마찬가지)

 

이말은 라이브러리를 개발자가 직접 다운받지 않고도 어디선가 알아서 받아와서 등록까지 해주는 착한 녀석이 있는데요 대표적으로 Maven과 Gradle이 있습니다. 이녀석들은 단순히 라이브러리를 가져오는 것을 넘어서 서비스를 배포할 환경에 따라 아티팩트(바로 실행이 가능하도록 빌드된 산출물)를 만드는데도 도움을 주는데요 지금 중요한건 이게 아니니까 넘어가도록 하겠습니다!!

 

설명도 안할꺼면서 왜 주저리주저리 쓰느냐 단순히 사용하기보다는 이런 키워드를 검색해서 어떤 역할을 하는지

스스로 검색해보고 찾아보면서 지식을 습득하는 과정이 중요하다고 생각했기 때문이에요!!

 

다시 본론으로 가서 이 글에서는 maven을 사용하고 있기때문에 maven 기준으로 설명하도록 하겠습니다.

 

1) pom.xml에 아래와 같은 녀석을 추가해줍니다.

 

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

2) src/main/resources/application.properties에 다음과 같은 내용을 추가해줍니다.

spring.datasource.hikari.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.hikari.jdbc-url=jdbc:log4jdbc:mysql://localhost:3306/database스키마이름?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
spring.datasource.hikari.username=root
spring.datasource.hikari.password=mysql비밀번호
spring.datasource.hikari.connection-test-query=SELECT 1
mybatis.configuration.map-underscore-to-camel-case=true

spring.datasource.hikari.driver-class-name : mysql을 사용할때 java와 mysql을 연동해줄 드라이버녀석

spring.datasource.hikari.jdbc-url : mysql을 실행한 주소 + 내가만든 database이름

spring.datasource.hikari.username = mysql 접속할 id

spring.datasource.hikari.password = mysql 접속할 id의 비밀번호

spring.datasource.hikari.connection-test-query = mysql이랑 연동 되었는지 확인할 쿼리

mybatis.configuration.map-underscore-to-camel-case = under_score로 작성된 컬럼명을 camelCase로 바꿔줄지 여부

 

이러면 Maven 아~ mysql이란 녀석을 사용할꺼구나 준비해야지 하고 라이브러리 다운받고 Spring Boot에서

MySQL을 사용할 준비를 끝마칩니다.

 

3. Spring Boot + MyBatis 연결

MySQL 사용할 준비가 끝났으니 MyBatis를 연동할 차례인데요 갑분마... 왜 마이바티스가 나타났냐구요??

이녀석을 사용하는 이유는 DB 커넥션을 맺고 끝는것을 자동화하고 쿼리를 실행하고 쿼리 수행 결과를

미리 준비해둔 VO(DTO)에 '알아서' 세팅해주기 때문이에요!!

 

자세한건 밑에서 설명하고 필요한 라이브러리를 다운받아서 넣어보죠!! 물론 Maven보고 알아서 해달라고

요청하기위해 다시 pom.xml에 다음과 같은 내용을 추가해줄게요!!

 

<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>2.0.0</version>
</dependency>

 

여기까지 정리 MySQL 사용할 준비 + MyBatis 사용할 준비 끝났습니다.

 

그럼 이제부터는 사용하도록 해보겠습니다.

 

4. DAO 만들기

우선 저는 위와 같은 디렉토리구조를 만들어보았는데요.

dao 패키지 밑에 UserDAO라는 인터페이스를 만들고, src/main/resources 하위에 mapper라는 이름의 폴더를 만든 후

userMapper라는 이름을 가진 xml 파일을 만들었습니다.

 

왜왜왜!!! 이렇게 만들었냐!! MyBatis를 사용해서 Service에서 UserDAO 인터페이스에 정의된 메소드를 실행시키고

userMapper.xml 파일에 정의된 DAO 인터페이스에 정의된 메소드와 동일한 이름을 가진 쿼리를 수행할 것이기 때문입니다.

 

복잡해지기 시작했죠 정리한번 할게요!!

1) Service : 야 UserDAO야 어떤 쿼리 수행한 결과 필요해

2) UserDAO : 예압! 잠만 너가 원하는 쿼리 정의 되어있나 확인해 봄 -> userMapper.xml 녀석아 쿼리 있니?

3) userMapper.mxl : 오오 있네 실행해서 결과 가져옴 기달쿠 (없으면 에러뱉고 뻗어버릴꺼야)-> DB에서 쿼리 수행

4) userMapper.mxl : UserDAO야 결과나왔다 MyBatis쓰니까 원하는 객체로 값 세팅해서 보내줄게

5) userDAO : 야야 Service야 결과 옛다

 

 

5. MyBatis 환경설정하기

위와 같은 티키타카를 하기 위해선 몇가지 설정이 필요해요 잘따라와 주세요!!!

설정을 위해 아래와 같은 라이브러리를 pom.xml에 추가해주세요!!

 

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
	<groupId>org.bgee.log4jdbc-log4j2</groupId>
	<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
	<version>1.16</version>
</dependency>

<!-- RestContoller 어노테이션 없어서 눈물날것 같은 분들 이거 추가-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>

 

자세한 내용은 이글에서 다루기 너무 방대해서 설정 방법 위주로 하겠습니다.

 

1) DatabaseConfiguration 구현

import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

//이거슨 환경 설정을 하기위한 파일이에요
@Configuration

//application.properties라는 파일에 key : value로 정의한 값 가져다 쓸꺼에요
@PropertySource("classpath:/application.properties")

//com.example.demo.dao 는 DB를 다룰 Mapper 입니다
@MapperScan(value = { "com.example.demo.dao" })

//트랜잭션 관리를 활성화 할게요
@EnableTransactionManagement
public class DatabaseConfiguration {

	@Autowired
	private ApplicationContext applicationContext;

	//히카리라는 DB 커넥션 풀 라이브러리를 사용해요
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.hikari")
	public HikariConfig hikariConfig() {
		return new HikariConfig();
	}

	//Database 기본 설정은 이제 히카리가 관리합니다
	@Bean
	public DataSource dataSorce() throws Exception {
		DataSource dataSource = new HikariDataSource(hikariConfig());
		return dataSource;
	}

	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
		sessionFactory.setDataSource(dataSource);
		//SQL을 정리해둔곳은 src/main/resource/mapper 밑에 디렉토리 하나이상 더 있을수도있는데
		//아무쪼록 이름이 Mapper로 끝나는 xml파일에 쿼리가 있으니 앞으로 DAO가 쿼리 요청하면 여길 뒤져요
		sessionFactory.setMapperLocations(applicationContext.getResources("classpath:mapper/**/*Mapper.xml"));
		sessionFactory.setConfiguration(mybatisConfig());
		return sessionFactory.getObject();
	}

	@Bean
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

	/**
	 * application.properties 내 mybatis 설정 값 가져 옴
	 * 
	 * @return org.apache.ibatis.session.Configuration
	 */
	@Bean
	@ConfigurationProperties(prefix = "mybatis.configuration")
	public org.apache.ibatis.session.Configuration mybatisConfig() {
		return new org.apache.ibatis.session.Configuration();
	}

 

2) 트랜잭션 설정

import java.util.Collections;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

@Configuration
public class TransactionAspect {

	private static final String AOP_TRANSATION_METHOD_NAME = "*";
	private static final String AOP_TRANSACTION_EXPRESSION = "execution(* com.example.demo.service..*(..))";

	@Autowired
	private PlatformTransactionManager transactionManager;

	@Bean
	public TransactionInterceptor transactionAdvice() {
		MatchAlwaysTransactionAttributeSource source = new MatchAlwaysTransactionAttributeSource();
		RuleBasedTransactionAttribute transactionAttribute = new RuleBasedTransactionAttribute();

		transactionAttribute.setName(AOP_TRANSATION_METHOD_NAME);
		transactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
		source.setTransactionAttribute(transactionAttribute);

		return new TransactionInterceptor(transactionManager, source);
	}

	@Bean
	public Advisor transactionAdviceAdvisor() {
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		pointcut.setExpression(AOP_TRANSACTION_EXPRESSION);
		return new DefaultPointcutAdvisor(pointcut, transactionAdvice());
	}
}

 

3) DAO 인터페이스 구현

public interface UserDAO {
	public User selectUser(User user) throws Exception;
}

selectUser는 추후 Mapper.xml 에서 쿼리 id를 판별하는데 사용됩니다.

 

4) Mapper.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.example.demo.dao.UserDAO">

	<select id="selectUser"
		parameterType="com.example.demo.vo.User"
		resultType="com.example.demo.vo.User">
		SELECT
		USER_NAME
		FROM user
		WHERE USER_ID = #{userId}
			AND USER_PASSWORD = #{userPassword}
	</select>
</mapper>

여기서 namespace="com.example.demo.dao.UserDAO"는 이 매퍼는 UserDAO에서 사용한다는 의미이며

id="selectUser"는 UserDAO에서 selectUser라는 이름으로 된 메소드와 매핑되는 쿼리라는 의미입니다!!

 

6. Contoller -> Service -> DAO -> Mapper.xml 쿼리 불러와보기

1) Controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.service.UserService;
import com.example.demo.vo.User;

@RequestMapping(value="/user")
@RestController
public class UserController {
	
	@Autowired
	private UserService userService;
	
	@RequestMapping(value = "/userInfo.do", method = RequestMethod.GET)
	public User userInfo(User user) throws Exception{
		return userService.getUserInfo(user);
	}
}

1-1) User

public class User {
	
	private int id;
	private String userId;
	private String userPassword;
	private String userName;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getUserPassword() {
		return userPassword;
	}
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}

}

2) Service

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.dao.UserDAO;
import com.example.demo.service.UserService;
import com.example.demo.vo.User;

@Service
public class UserServiceImpl implements UserService{
	
	@Autowired
	private UserDAO userDAO;

	@Override
	public User getUserInfo(User user) throws Exception {
		// TODO Auto-generated method stub
		return userDAO.selectUser(user);
	}

}

3) DAO

public interface UserDAO {
	public User selectUser(User user) throws Exception;
}

4) Mapper

<?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.example.demo.dao.UserDAO">

	<select id="selectUser"
		parameterType="com.example.demo.vo.User"
		resultType="com.example.demo.vo.User">
		SELECT
		USER_NAME
		FROM user
		WHERE USER_ID = #{userId}
			AND USER_PASSWORD = #{userPassword}
	</select>
</mapper>

5) Result

헥헥... import 까지 들어가 있는 코드는 간혹 같은 이름이 있어서 어떤걸 import 해야하는지 헷갈리실까봐 같이 올려봤어요..

 

7. 정리

오늘은 거의 사용법 위주로 글을 작성했습니다. 사실 서비스에서 DB 연동해서 값을 가져오고 그걸 객체로 매핑해서 결과값을 보여주는것을 모두 설명하기에는 제 역량이 부족한 탓인지 한번에 설명하기엔 글이 너무 길어질 것 같더라구요..ㅠㅠㅠ 그래서 정리를 한번 해볼게요!!

 

1. Maven이나 Gradle로 DB관련 라이브러리를 받아온다.(MySQL, MyBatis)

2. 트랜잭션과 xml에 작성한 쿼리를 인식하기 위한 설정을 어노테이션으로 한다

(DatabaseConfiguration.java, TransactionAspect.java)

3. DAO, *Mapper.xml을 작성한다.

4. Controller -> Service -> DAO -> *Mapper.xml 순으로 실행되고 결과값을 가져온다.

 

끝으로 저는 김인우 개발자님이 지으신 "스프링 부트 시작하기" 서적을 기반으로 공부하여 글을 작성하고 있어요!! 혹시나 빠르게 지식을 익혀야 한다면 해당 서적을 추천드려요!! 저는 이 서적을 참고하고 공부해서 되도록 더 쉽고 짧게 작성하려하는데 도움이 되는지는 잘모르겠습니다 ㅠㅠㅠ

 

다음글은 이 설정들을 하나하나 뜯어보면서 왜 사용되는지 알아보는 내용을 다루려고 해요!!!

모두 열씨미 개발공부해봐요!! 화이팅!!