티스토리 뷰

Programming/JOOQ

[JOOQ-01] JOOQ 맛보기

zepinos 2019. 2. 28. 17:45
반응형

2019/02/28 - [Programming/JOOQ] - [프롤로그] JOOQ 을 사용하게 된 계기


JOOQ 을 어떻게 사용하는건지 아주 간단한 맛보기 프로그램을 만들어보겠습니다.


PC 에 docker 을 사용할 수 있는 환경을 구축한 후 MySQL 8 을 설치하였습니다. jooq 라는 계정을 만들고 jooq@1234 라는 패스워드를 생성하고 jooq 라는 schema(DB) 을 생성하였습니다. 이 모든 설정은 MySQL 와 관련된 것이기 때문에 생략하겠습니다.

생성된 DB 에 다음과 같이 테스트용 테이블 하나와 두 개의 데이터를 입력하였습니다.


CREATE TABLE `jooq`.`jooq_board` (
`seq` INT NOT NULL AUTO_INCREMENT,
`author` VARCHAR(100) NOT NULL,
`content` TEXT NOT NULL,
`read_cnt` VARCHAR(45) NULL,
`add_date` DATETIME NOT NULL DEFAULT now(),
`mod_date` DATETIME NOT NULL DEFAULT now(),
PRIMARY KEY (`seq`));


INSERT INTO `jooq`.`jooq_board` (`author`, `content`, `read_cnt`) VALUES ('zepinos', '테스트', '0');
INSERT INTO `jooq`.`jooq_board` (`author`, `content`, `read_cnt`) VALUES ('okky', '오키사이트\nhttps://okky.kr', '0');


그리고, IDE 에서 Spring Boot 프로젝트를 하나 생성했습니다. 만약 IDE 을 사용하지 않는다면 웹사이트에서 직접 생성해도 되고, maven 구조에 맞게 직접 생성해도 됩니다.

생성할 때 Java 11, maven, war 환경에 DevTools, Lombok, Web, MySQL, JOOQ 을 선택하였습니다. 그리고, 이를 바탕으로 만들어진 pom.xml 에 tomcat 을 제외시키고 undertow 을 추가하였습니다.

아래와 같은 pom.xml 이 생성되었습니다.


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.zepinos.blog</groupId>
	<artifactId>jooq</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>jooq</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jooq</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<artifactId>spring-boot-starter-tomcat</artifactId>
					<groupId>org.springframework.boot</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-undertow</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


그리고 DB 접속 정보 설정을 위해 src/main/resources 의 application.properties 파일을 application.yml 로 확장자를 변경하고 아래와 같이 내용을 추가하였습니다. 개인적으로 YAML 을 Properties 보다 선호합니다.


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jooq?useSSL=false
    username: jooq
    password: jooq@1234
  jooq:
    sql-dialect: mysql_8_0


그리고 src/main/java 에 MVC 환경에 맞게 controller, service, dao 패키지(Package)를 추가하였습니다. 각 패키지에는 다음과 같은 내용을 추가하였습니다.


controller/JooqBoardController.java

package com.zepinos.blog.jooq.controller;

import com.zepinos.blog.jooq.service.JooqBoardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

@RestController
@RequestMapping("/jooq_board")
public class JooqBoardController {

	private final JooqBoardService jooqBoardService;

	@Autowired
	public JooqBoardController(JooqBoardService jooqBoardService) {
		this.jooqBoardService = jooqBoardService;
	}

	@GetMapping("/list")
	public Callable<Map<String, Object>> list() {

		Map<String, Object> result = new HashMap<>();

		result.put("list", jooqBoardService.list());

		return () -> result;

	}

}


service/JooqBoardService.java

package com.zepinos.blog.jooq.service;

import com.zepinos.blog.jooq.dao.JooqBoardDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class JooqBoardService {

	private final JooqBoardDao jooqBoardDao;

	@Autowired
	public JooqBoardService(JooqBoardDao jooqBoardDao) {
		this.jooqBoardDao = jooqBoardDao;
	}

	public List<Map<String, Object>> list() {

		return jooqBoardDao.list();

	}

}


dao/JooqBoardDao.java

package com.zepinos.blog.jooq.dao;

import org.jooq.DSLContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.table;

@Repository
public class JooqBoardDao {

	private final DSLContext dslContext;
	private final JdbcTemplate jdbcTemplate;

	@Autowired
	public JooqBoardDao(DSLContext dslContext, JdbcTemplate jdbcTemplate) {
		this.dslContext = dslContext;
		this.jdbcTemplate = jdbcTemplate;
	}

	public List<Map<String, Object>> list() {

		String sql = dslContext.select(field("jooq_board.seq"), field("jooq_board.author"), field("jooq_board.content"), field("jooq_board.read_cnt"), field("jooq_board.add_date"), field("jooq_board.mod_date"))
				.from(table("jooq_board"))
				.where(field("jooq_board.seq").greaterThan(0))
				.limit(2)
				.offset(1)
				.getSQL();

		System.out.println("sql : " + sql);

		return jdbcTemplate.queryForList(sql, 0, 2, 1);

	}

}


설명이 필요한 코드는 역시 dao/JooqBoardDao.java 파일입니다.

프롤로그에서 설명했듯이, JOOQ 는 DB 에 접속하여 DB Object 을 분석하여 Java Code 을 생성합니다. 그런데, 위에서는 그런 작업을 전혀 진행하지 않았습니다. JOOQ 는 DSL 형태로 질의를 작성하면 sql-dialect (JPA 써보신 분들은 아시겠지만, dialect 을 방언이라고 직역해서 쓰시더군요) 설정에 따라 DBMS 문법에 맞게 결과를 만들어내는 라이브러리입니다. 그래서, 위와 같이 field() 나 table() 을 이용해 생성된 코드 없이도 직접 질의를 만들어낼 수도 있게 되어 있습니다. 그 결과를 String 으로 저장할 수 있으며, where() 에서 greaterThan() 와 같이 parameter 로 입력할 수 있는 부분은 PreparedStatement 에 맞게 ? 로 변환되어 제공됩니다.

그래서, 이렇게 생성된 질의는 기존의 방식을 이용해서(여기서는 JdbcTemplate) 질의를 실행할 수 있습니다. 위에서는 greaterThan, limit, offset 세 개의 paramter 을 이용하고 있기 때문에 질의와 함께 세 개의 매개변수를 추가로 지정해서 실행할 수 있도록 했습니다.


이렇게 작성된 프로그램을 실행하고 웹브라우져로 http://localhost:8080/jooq_board/list 에 접속하면 웹브라우져와 콘솔에서 다음과 같은 결과를 확인할 수 있습니다.


물론 logging 을 활성화하지 않아서 소스에 추가한 출력만 표시됩니다만, MySQL 8 에 적합한 쿼리를 JOOQ 가 생성한다는 것을 알 수 있습니다.


그렇다면, 이 프로그램을 Cubrid 에서 실행한다면 어떻게 될까요? 제가 Cubrid 을 써본 적도 없고, 굳이 Docker 로 내려 받아 확인할 필요도 없이, sql-dialect 을 cubrid 로만 변경해도 원하는 바를 확인할 수 있습니다.


spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jooq?useSSL=false
    username: jooq
    password: jooq@1234
  jooq:
    sql-dialect: cubrid


실행한 뒤 브라우져로 접근하면 오류가 발생하지만, 콘솔에서 다음과 같이 Cubrid 에 맞는 쿼리가 자동 생성됨을 확인할 수 있습니다.


실제 JOOQ 가 만들어낸 질의는 아래와 같습니다.


select jooq_board.seq, jooq_board.author, jooq_board.content, jooq_board.read_cnt, jooq_board.add_date, jooq_board.mod_date from jooq_board where jooq_board.seq > cast(? as int) limit ?, ?


limit 가 아닌 다른 부분에서 더 극적인 차이를 느낄 수 있겠지만, 어쨌든 이러한 질의 생성은 이미 JPA 을 통해서 경험해보신 분들도 많으실 겁니다. 그렇기 때문에 이 정도 선에서 설명을 줄이겠습니다.


다음 글부터는 JOOQ 공식문서의 순서대로 JOOQ 을 소개해나갈 계획입니다. 예제 DB 도 공식문서의 것을 이용할 것이구요. 많은 관심 부탁드립니다.


2019/03/01 - [Programming/JOOQ] - [JOOQ-02] JOOQ 에 대해서 좀 더 알아보기

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함