oracle (3)
스프링 부트(Spring boot)에서 mybatis(oracle) 적용하기

2016/09/19 - [개발/JAVA] - 스프링 부트(Spring boot)에서 logback 적용하기


이어서.. 스프링 부트에서 마이바티스를 연동해 보도록 하겠습니다.

실무에서 주로 사용되는 mapper xml을 이용한 방법을 살펴보겠습니다.

DB는 오라클을 사용하지만 드라이버만 바꾸면 다른 종류의 DB도 사용이 가능합니다.


pom.xml

오라클 드라이버는 메이븐 중앙 저장소에 없기 때문에 오라클 저장소를 추가해 줍니다.

parent와 동일 레벨로 저장소를 추가합니다.


<repositories>

        <repository>

            <id>oracle</id>

            <name>ORACLE JDBC Repository</name>

            <url>http://maven.jahia.org/maven2</url>

        </repository>

    </repositories>

그리고 오라클 드라이버와 mybatis spring goot starter 종속성을 추가해줍니다.

마이바티스에서 스프링 대응하여 나온 것으로 기존 마이바티스는 필요없습니다.

(http://www.mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure)

<dependency>

        <groupId>org.mybatis.spring.boot</groupId>

        <artifactId>mybatis-spring-boot-starter</artifactId>

        <version>1.1.1</version>

    </dependency>

         

    <dependency>

        <groupId>com.oracle</groupId>

        <artifactId>ojdbc6</artifactId>

        <version>11.1.0.7.0</version>

    </dependency>


application.yml

spring.datasource 설정 부분이 추가되었으며, mybatis 설정 부분이 추가되었습니다.

mybatis-config.xml형식으로 설정을 하고 싶으면 mybatis.config-location설정을 쓰시면 됩니다.

다양한 설정 항목들이 있으니 스프링, 마이바티스 문서를 참고해 주세요.

datasource항목은 대부분 개발과 상용이 다를테니 샘플보다는 프로파일별로 옮겨서 관리를 하시는게 좋습니다.

spring:
  profiles: 
    active: dev
  timerName: exampleTimer
  datasource:
    driver-class-name: oracle.jdbc.driver.OracleDriver
    url: jdbc:oracle:thin:@111.222.111.222:1521:SIDHERE
    username: donnert
    password: password
---
mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    lazyLoadingEnabled=true
    aggressiveLazyLoading=false
    mapUnderscoreToCamelCase=true
---
spring:
  profiles: local
  task:
    fixedDelay: 1000
    name: localTask
---
spring:
  profiles: dev
  task:
    fixedDelay: 5000
    name: devTask

Application.java

기존에 사용하시던대로 MapperScan어노케이션으로 매퍼 패키지 위치를 정의해 줍니다.

package net.donnert.spring.boot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@MapperScan("net.donnert.spring.boot")
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

TestMapper.java

쿼리 xml과의 인터페이스를 제공해줍니다.

package net.donnert.spring.boot;

public interface TestMapper {
	public String getValueFromDatabase();
}


testMapper.xml

src/main/resources하위에 mapper폴더 생성 후 testMapper.xml을 생성해 줍니다.

내용은 위에서 정의한 인터페이스 쿼리를 작성해줍니다.




    

Timer.java

매퍼 빈을 주입 받아서 그냥 호출하면 됩니다.

package net.donnert.spring.boot;

import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
public class Timer {
	Logger logger = LoggerFactory.getLogger(this.getClass());
	private AtomicInteger loopCounter = new AtomicInteger();
	
	@Autowired
	private StopWatch watch;
	
	@Autowired
	private TestMapper testMapper;
	
	@Value("${spring.task.name}")
	private String taskNamePrefix;
	
	@Value("${spring.timerName}")
	private String timerName;
	
	@PostConstruct
	public void init() {
		logger.info("{} init", timerName);
		watch.start();
	}

	@Scheduled(fixedDelayString = "${spring.task.fixedDelay}")
	public void tick() throws InterruptedException{
		watch.stop();
		logger.info(testMapper.getValueFromDatabase());
		String taskName = taskNamePrefix + "-" + String.valueOf(loopCounter.getAndIncrement());
		watch.start(taskName);
	}

	@Bean
	public StopWatch watch() {
		return new StopWatch();
	}
}

테스트

실행해 보면 아래처럼 쿼리 로그와 결과가 정상적으로 찍히는 모습을 볼 수 있습니다.

2016-09-20 13:56:08.904  INFO(12012)[main] [net.donnert.spring.boot.Timer:33] exampleTimer init
2016-09-20 13:56:09.286  INFO(12012)[main] [o.s.j.e.a.AnnotationMBeanExporter:431] Registering beans for JMX exposure on startup
2016-09-20 13:56:09.349  INFO(12012)[main] [o.s.s.a.ScheduledAnnotationBeanPostProcessor:244] No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2016-09-20 13:56:09.372  INFO(12012)[main] [n.d.spring.boot.Application:57] Started Application in 3.229 seconds (JVM running for 3.793)
2016-09-20 13:56:10.812 DEBUG(12012)[pool-2-thread-1] [n.d.s.b.T.getValueFromDatabase:145] ==>  Preparing:  SELECT NAME FROM TEST WHERE ROWNUM=1
2016-09-20 13:56:10.902 DEBUG(12012)[pool-2-thread-1] [n.d.s.b.T.getValueFromDatabase:145] ==> Parameters: 
2016-09-20 13:56:10.930 DEBUG(12012)[pool-2-thread-1] [n.d.s.b.T.getValueFromDatabase:145] <==      Total: 1
2016-09-20 13:56:10.932  INFO(12012)[pool-2-thread-1] [net.donnert.spring.boot.Timer:40] 테스트이름

간단한 예제같지만 프로파일, 로깅, DB연결 등 필요한 부분들은 모두 들어가 있기 떄문에 실무에서도 

배치나 데몬 프로세스로 충분히 사용하실 수 있습니다.

스프링 부트를 이용하면 복잡한 설정도 필요없고

이미 구현된 것들을 설정만 해서 쓰는 방식이기 때문에 개발 시간이 많이 단축됩니다.


완성소스

https://github.com/donnert/spring-boot/tree/master/spring-boot-batch


2  Comments,   0  Trackbacks
  • 크로노
    정말 정말 감사합니다^^
    계속 해맷는데 이제야 되는 소스를 찾았네요^^
  • jepark3452
    pom.xml에 오라클 추가시 에러나시는분들은

    <!-- dependencies 태그 위쪽에 추가 하세요.-->
    <repositories>
    <repository>
    <id>oracle</id>
    <name>ORACLE JDBC Repository</name>
    <url>https://maven.atlassian.com/3rdparty/</url>
    </repository>
    </repositories>

    <!-- dependencies 태그 안쪽에 추가하세요. -->
    <dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>12.1.0.1-atlassian-hosted</version>
    </dependency>


    참고하세요~

    ※ 참고 : https://github.com/regenea8/GroupWare/blob/master/GroupWare/pom.xml
댓글 쓰기
Mybatis #과 $의 차이

PrepareStatment(#)와 Statment($)를 정확히 모르고 사용하시는 분들이 있어 정리해봤습니다.

 - 간혹 Statment를 숫자일때 사용한다.. 라고 생각하시는 분들고 계시고 정확한 의미를 모르는 분들이 계십니다.  정리된 내용 보시기 바랍니다.


1. #의 사용 (PrepareStatment)

 - #을 사용할 경우 오라클의 PreparedStatment를 사용하게 된다.


  예제(name=John)

   a. mybatis mapper

      SELECT NAME FROM TEST WHERE NAME=#{name}

   b. 오라클에서 받은 쿼리

      SELECT NAME FROM TEST WHERE NAME = ?


실제 수행 쿼리

SELECT NAME FROM TEST WHERE NAME='John'


2. $의 사용(Statment)

 - $는 간단히 스트링 자체를 변환(REPLACE)해 버린다


  예제(score=99)

   a. mybatis mapper 

      SELECT NAME FROM TEST WHERE SCORE=${score}

   b. 오라클에서 받은 쿼리 

      SELECT NAME FROM TEST WHERE SCORE=99


실제 수행 쿼리

SELECT NAME FROM TEST WHERE NAME=99


3. 차이

두 가지가 똑같아 보이지만 붉게 처리된 부분을 볼 경우 1번은 오라클에서 변수를 바인드 처리 하기 때문에

NAME 값이 달라질 경우 같은 쿼리로 인식하게 됩니다.

(반드시는 아니지만 일반적으로 오라클의 쿼리 파싱, 캐시 등의 이점으로 CPU 및 수행 속도 차이가 나게 됩니다)


2번의 경우 b부분을 봤을때 쿼리 자체가 변경되어 들어가기 때문에 SCORE가 다른값이 올 경우

오라클은 이를 완전 다른 쿼리로 인식하기 때문에 새로 파싱 등의 작업을 하게 되고 속도가 느려질 우려가 있습니다.


그럼 Statment의 경우 어떻때 쓰는게 좋을까요?


SELECT ${column_name} FROM SCORE


위 쿼리같이 Bind를 할수 없는 경우 Statment를 사용하게 됩니다.

하지만 문자열 자체가 변형되기 때문에 보안상 취약할 수 있습니다.


예를 들면 아래와 같은 경우입니다.

user를 'admin'--로 세팅, password='aaa'

SELECT * FROM USER WHERE ID=${user} AND PASSWORD=${password}

실제 수행 쿼리

SELECT * FROM USER WHERE ID='admin'-- AND PASSWORD='aaa'


녹색 부분이 주석처리되어 admin으로 접속이 가능하게 되었습니다.


'개발 > DB' 카테고리의 다른 글

Mybatis #과 $의 차이  (0) 2014.11.10
몽고DB 설치  (0) 2014.01.22
테이블 정보(컬럼 정보, 코멘트 등등)  (0) 2013.11.11
테이블 사이즈 구하기(컬럼 길이, 컬럼 길이 평균)  (0) 2013.03.12
[ORACLE] 달력  (0) 2012.09.14
[ORACLE] 디비 락 해제  (0) 2012.01.09
0  Comments,   0  Trackbacks
댓글 쓰기
테이블 정보(컬럼 정보, 코멘트 등등)

컬럼이 많은 테이블의 경우 코멘트로 컬럼명을 찾고 싶을떄가 있다

아래 쿼리를 돌리자.


SELECT * FROM (

SELECT 

  DECODE(column_id,0,TABLE_NAME,NULL) AS TABLE_NAME,

  TO_NUMBER(DECODE(column_id,0,NULL,column_id))  AS COLUMN_ID,

  COLUMN_NAME, NULLABLE, DATA_TYPE, COMMENTS

FROM (

  SELECT cc.TABLE_NAME,

    tc.COLUMN_ID, tc.COLUMN_NAME,

    tc.NULLABLE, tc.DATA_TYPE || 

      CASE WHEN tc.DATA_SCALE IS NOT NULL THEN '(' || tc.DATA_PRECISION || ',' || tc.DATA_SCALE || ')'

        WHEN tc.DATA_PRECISION IS NOT NULL THEN '(' || tc.DATA_PRECISION || ')'

        WHEN tc.DATA_LENGTH IS NOT NULL AND tc.DATA_TYPE LIKE '%CHAR%' THEN '(' || tc.DATA_LENGTH || ')'

      END DATA_TYPE,

    cc.COMMENTS

  FROM user_col_comments cc

  INNER JOIN user_tab_cols tc ON (cc.TABLE_NAME = tc.TABLE_NAME AND cc.TABLE_NAME = tc.TABLE_NAME AND cc.COLUMN_NAME = tc.COLUMN_NAME) 

  UNION

  SELECT tab.table_name AS TABLE_NAME, 

         0 AS COLUMN_ID, '' AS COLUMN_NAME,

         '' AS NULLABLE, '' AS DATA_TYPE,

         tab.comments AS COMMENTS

  FROM user_tab_comments tab

WHERE table_name = UPPER(:MY_TABLE)

ORDER BY table_name, column_id

)

WHERE comments LIKE '%일련번호%'


'개발 > DB' 카테고리의 다른 글

Mybatis #과 $의 차이  (0) 2014.11.10
몽고DB 설치  (0) 2014.01.22
테이블 정보(컬럼 정보, 코멘트 등등)  (0) 2013.11.11
테이블 사이즈 구하기(컬럼 길이, 컬럼 길이 평균)  (0) 2013.03.12
[ORACLE] 달력  (0) 2012.09.14
[ORACLE] 디비 락 해제  (0) 2012.01.09
0  Comments,   0  Trackbacks
댓글 쓰기