SlideShare a Scribd company logo
Spring Boot with MyBatis
KESTI 개발팀
Spring Boot with MyBatis
• mybatis-spring-boot-starter 소개
• MyBatis Tutorials
• setup
• mappers
• testing
• Bonus Tutorials
• Flyway
• QueryDSL
2016-10-12KESTI 개발팀 세미나 2
MyBatis for Spring Boot
• MyBatis 를 Spring Boot Application 에서 사용하기 위한
라이브러리
• 홈페이지 : http://www.mybatis.org/mybatis-spring-boot/
• 소스 : https://github.com/mybatis/mybatis-spring-boot
• 예제 :
• KESTI SpringBoot-MyBatis Tutorials
• Spring Boot 에서 Java Config를 통해 myBatis 연동하기
2016-10-12KESTI 개발팀 세미나 3
MyBatis Tutorials
2016-10-12KESTI 개발팀 세미나 4
Tutorial Overview
1. mybatis-spring-boot-starter 프로젝트 구성
2. Mapper – Annotation / XML 방식
3. MyBatis 설정 방법
4. Flyway 를 이용한 database migration 방법
5. MyBatis 단위 테스트
2016-10-12KESTI 개발팀 세미나 5
1. 프로젝트 구성
<dependencies>
<dependency>
<groupId>kr.kesti.kesti4j</groupId>
<artifactId>kesti4j-data-java6</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
2016-10-12KESTI 개발팀 세미나 6
Project Structures
configuration
data handling source
Spring Boot Application
Flyway database migration
MyBatis configuration & XML Mappers
환경설정 정보
Test 코드
2016-10-12KESTI 개발팀 세미나 7
XML 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="kr.kesti.mybatis.examples.domain.models">
<select id="selectActorByFirstname" parameterType="String" resultType="Actor">
SELECT * FROM Actors WHERE firstname = #{firstname} limit 1
</select>
<insert id="insertActor" useGeneratedKeys="true" keyProperty="id">
insert into Actors(firstname, lastname)
values( #{firstname}, #{lastname} )
</insert>
<!-- Oracle, PostgreSQL 은 SEQUENCE -->
<!--
<insert id="insertActorBySequence">
<selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
SELECT SEQ_ID.nextval FROM DUAL
</selectKey>
INSERT INTO Autors (id, firstname, lastname)
VALUES (#{id}, #{firstname}, #{lastname})
</insert>
-->
</mapper>
2016-10-12KESTI 개발팀 세미나 8
Annotated Mapper
/**
* Actor 를 위한 MyBatis Mapper 입니다.
*/
public interface ActorMapper {
@Select("SELECT * FROM Actors WHERE firstname = #{firstname}")
Actor findByFirstname(@Param("firstname") String firstname);
@Select("SELECT * FROM Actors")
List<Actor> findAll();
@Delete("DELETE FROM Actors WHERE id=#{id}")
void deleteById(@Param("id") Integer id);
}
/**
* MyBatis 를 Spring boot 에서 사용하기 위한 환경설정
*/
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = { ActorRepository.class })
@MapperScan(basePackageClasses = { ActorMapper.class })
public class MyBatisConfiguration extends AbstractFlywayMyBatisConfiguration {
2016-10-12KESTI 개발팀 세미나 9
XML Mapper
보다 좋은점은?
Repository / DAO
/**
* {@link Actor} 를 위한 Repository (DAO) 입니다.
*/
@Slf4j
@Repository
public class ActorRepository {
@Autowired ActorMapper mapper;
@Autowired SqlSessionTemplate session;
public Actor findByFirstname(@NonNull String firstname) {
return mapper.findByFirstname(firstname);
}
public Actor findByFirstnameWithXmlMapper(@NonNull String firstname) {
return session.selectOne("selectActorByFirstname", firstname);
}
public List<Actor> findAll() {
return mapper.findAll();
}
public int insertActor(@NonNull Actor actor) {
return session.insert("insertActor", actor);
}
public void deleteById(@NonNull Integer id) {
mapper.deleteById(id);
}
}
2016-10-12KESTI 개발팀 세미나 10
MyBatis JavaConfig
@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = { ActorRepository.class })
@MapperScan(basePackageClasses = { ActorMapper.class })
public class MyBatisConfiguration extends AbstractFlywayMyBatisConfiguration {
// datasource 용 properties (application.properties 에서 자동으로 정보를 읽어옴)
@Inject DataSourceProperties dataSourceProperties;
// mybatis 용 properties (application.properties 에서 자동으로 정보를 읽어옴)
@Inject MybatisProperties mybatisProperties;
@Override
protected DatabaseSetting getDatabaseSetting() {
return DatabaseSetting.builder()
.driverClass(dataSourceProperties.getDriverClassName())
.jdbcUrl(dataSourceProperties.getUrl())
.build();
}
@Override
protected String getMyBatisConfigPath() {
return mybatisProperties.getConfig();
}
@Override
protected String getMyBatisMapperPath() {
return mybatisProperties.getMapperLocations()[0];
}
}
2016-10-12KESTI 개발팀 세미나 11
XML Config
와 장단점
비교
Spring Boot application.properties
### DataSource
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.jdbc-url=jdbc:h2:mem
spring.datasource.username=sa
spring.datasource.password=
### MyBatis
mybatis.config=mybatis/mybatis-config.xml
mybatis.mapper-locations=mybatis/mappers/**/*Mapper.xml
mybatis.executor-type=simple
### Logging
logging.level.root=info
logging.level.kr.kesti.mybatis.examples=debug
2016-10-12KESTI 개발팀 세미나 12
Database Setup by Flyway
/**
* Flyway 를 이용한 DB Migration 을 수행하도록 합니다.
*
* @param dataSource DataSource
* @return {@link Flyway} 인스턴스
*/
@Bean(initMethod = "migrate")
protected Flyway flyway(DataSource dataSource) {
Flyway flyway = new Flyway();
flyway.setDataSource(dataSource);
if (cleanDatabaseForTest()) {
flyway.clean();
}
return flyway;
}
CREATE TABLE Actors (
id SERIAL PRIMARY KEY,
firstname VARCHAR(64),
lastname VARCHAR(64)
);
INSERT INTO Actors (firstname, lastname) VALUES
('Sunghyouk', 'Bae');
INSERT INTO Actors (firstname, lastname) VALUES ('Misook',
'Kwon');
INSERT INTO Actors (firstname, lastname) VALUES ('Jehyoung',
'Bae');
INSERT INTO Actors (firstname, lastname) VALUES ('Jinseok',
'Kwon');
INSERT INTO Actors (firstname, lastname) VALUES ('Kildong',
'Hong');
2016-10-12KESTI 개발팀 세미나 13
Configuration Test
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { MyBatisConfiguration.class })
public class MyBatisConfigurationTest {
@Inject ActorMapper actorMapper;
@Inject ActorRepository actorRepository;
@Inject private SqlSessionFactory sf;
@Test
public void testConfiguration() {
assertThat(actorMapper).isNotNull();
assertThat(actorRepository).isNotNull();
assertThat(sf).isNotNull();
}
}
2016-10-12KESTI 개발팀 세미나 14
Repository Test
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { MyBatisConfiguration.class })
public class ActorRepositoryTest {
@Inject ActorRepository actorRepo;
private static final String FIRST_NAME = "Sunghyouk";
@Test
public void testConfiguration() {
assertThat(actorRepo).isNotNull();
}
@Test
public void testFindByFirstname() {
Actor actor = actorRepo.findByFirstname(FIRST_NAME);
assertThat(actor).isNotNull();
}
@Test
public void testFindByFirstnameWithXmlMapper() {
Actor actor = actorRepo.findByFirstnameWithXmlMapper(FIRST_NAME);
assertThat(actor).isNotNull();
}
@Test
public void testFindAll() {
List<Actor> actors = actorRepo.findAll();
assertThat(actors.size()).isGreaterThan(0);
}
@Test
public void testInsertActor() {
String firstname = "mybatis";
Actor actor = Actor.of(null, firstname, "kesti");
int rowCount = actorRepo.insertActor(actor);
log.debug("rowCount={}", rowCount);
actor = actorRepo.findByFirstname(firstname);
log.debug("actor={}", actor);
assertThat(actor).isNotNull();
assertThat(actor.getFirstname()).isEqualTo(firstname);
actorRepo.deleteById(actor.getId());
}
}
2016-10-12KESTI 개발팀 세미나 15
Flyway
Evolve your Database Schema easily and
reliably across all your instances
2016-10-12KESTI 개발팀 세미나 16
Code 관리는 git
2016-10-12KESTI 개발팀 세미나 17
DB 형상 관리는?
DB Schema Control by Dev Step
2016-10-12KESTI 개발팀 세미나 18
DB Schema Version Control
2016-10-12KESTI 개발팀 세미나 19
QueryDSL
Unified Queries for Java with Typesafe
2016-10-12KESTI 개발팀 세미나 20
QueryDSL 개요
• 질의어를 문자열이 아닌 Java Code로 표현
• Query문 – type check 불가 / 실행 전에는 오류 검출 불가
• Java Code는
• Compile Error를 미리 검출
• Code assistant 활용 100%
• Refactoring 용이
• 다양한 저장소에 대한 일관된 질의어 제작 가능
• Collection, RDBMS, MongoDB, Lucene …
• .NET 의 LINQ 같은 목적 (Langunage-INtegrated Query)
• 참고 : 한글 매뉴얼 (단 3.4.0 기준임. 4.x 는 package 명이
달라졌음)
2016-10-12KESTI 개발팀 세미나 21
QueryDSL 적용 범위
JPA (Java Persistence API)
JDO (Java Data Object)
SQL
Lucence
MongoDB
Collections
QueryDSL
RDBMS
ORM
Lucene
(search
engine)
MongoD
B
List / Map
2016-10-12KESTI 개발팀 세미나 22
QueryDSL for SQL
• Select
• Join (innerJoin, join, leftJoin, rightJoin, fullJoin)
• group by / having
• order by
• limit / offset / restrict
• subquery
• Window functions
• Common Table Expression (CTE)
• Insert
• Update
• Delete
2016-10-12KESTI 개발팀 세미나 23
SELECT with Projections
2016-10-12KESTI 개발팀 세미나 24
QActors $ = QActors.actors;
List<Tuple> rows = query.select($.id, $.firstname, $.lastname)
.from($)
.fetch();
QActors $ = QActors.actors;
List<Actor> actors = query.select(Projections.constructor(Actor.class,
$.id,
$.firstname,
$.lastname))
.from($)
.fetch();
SELECT – Filter, GroupBy
2016-10-12KESTI 개발팀 세미나 25
Actor actor = query.select(Projections.constructor(Actor.class,
$.id,
$.firstname,
$.lastname))
.from($)
.where($.lastname.eq("Bae"))
.fetchFirst();
List<Tuple> rows = query.select($.lastname, $.lastname.count().as("cnt"))
.from($)
.groupBy($.lastname)
.fetch();
SELECT : Subquery
2016-10-12KESTI 개발팀 세미나 26
QActors $ = QActors.actors;
QActors $2 = new QActors("$2");
SQLQuery<Actor> sq = query.select(Projections.constructor(Actor.class,
$.id,
$.firstname,
$.lastname))
.from($)
.where($.id.eq(SQLExpressions.select($2.id.max()).from($2)));
List<Actor> actors = sq.fetch();
CUD
2016-10-12KESTI 개발팀 세미나 27
QActors $ = QActors.actors;
long inserted = query.insert($)
.columns($.firstname, $.lastname)
.values("querydsl", "examples")
.execute();
long updated = query.update($)
.set($.lastname, "updated examples")
.where($.firstname.eq("querydsl"))
.execute();
long deleted = query.delete($)
.where($.firstname.eq("querydsl"))
.execute();
CUD Batch Execution
2016-10-12KESTI 개발팀 세미나 28
QActors $ = QActors.actors;
int COUNT = 100;
// INSERT
SQLInsertClause insertClause = query.insert($);
for (int i = 0; i < COUNT; i++) {
insertClause.set($.firstname, "firstname-" + i)
.set($.lastname, "lastname-" + i)
.addBatch();
}
long insertedCount = insertClause.execute();
// UPDATE
SQLUpdateClause updateClause = query.update($);
for (int i = 0; i < COUNT; i++) {
updateClause.set($.firstname, "updated-firstname-" + i)
.where($.lastname.eq("lastname-" + i))
.addBatch();
}
long updatedCount = updateClause.execute();
// DELETE
SQLDeleteClause deleteClause = query.delete($);
for (int i = 0; i < COUNT; i++) {
deleteClause.where($.firstname.eq("updated-firstname-" + i))
.addBatch();
}
long deletedCount = deleteClause.execute();
QueryDSL Dependency
2016-10-12KESTI 개발팀 세미나 29
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql</artifactId>
<version>${com.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-spring</artifactId>
<version>${com.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-codegen</artifactId>
<version>${com.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-spatial</artifactId>
<version>${com.querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-spatial</artifactId>
<version>${com.querydsl.version}</version>
</dependency>
QueryDSL APT
2016-10-12KESTI 개발팀 세미나 30
<plugin>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-maven-plugin</artifactId>
<version>${com.querydsl.version}</version>
<executions>
<execution>
<goals>
<goal>export</goal>
</goals>
</execution>
</executions>
<configuration>
<jdbcDriver>org.postgresql.Driver</jdbcDriver>
<jdbcUrl>jdbc:postgresql://localhost/querydsl</jdbcUrl>
<jdbcUser>root</jdbcUser>
<jdbcPassword>root</jdbcPassword>
<packageName>kesti4j.data.querydsl.models</packageName>
<sourceFolder>${project.basedir}/target/generated-sources/java</sourceFolder>
<targetFolder>${project.basedir}/target/generated-sources/java</targetFolder>
<spatial>true</spatial>
</configuration>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1206-jdbc42</version>
</dependency>
</dependencies>
</plugin>
QueryDSL Generated Code
2016-10-12KESTI 개발팀 세미나 31
/**
* QActors is a Querydsl query type for QActors
*/
@Generated("com.querydsl.sql.codegen.MetaDataSerializer")
public class QActors extends RelationalPathSpatial<QActors> {
private static final long serialVersionUID = 822224394;
public static final QActors actors = new QActors("actors");
public final StringPath firstname = createString("firstname");
public final NumberPath<Integer> id = createNumber("id", Integer.class);
public final StringPath lastname = createString("lastname");
public final com.querydsl.sql.PrimaryKey<QActors> actorsPkey = createPrimaryKey(id);
public QActors(String variable) {
super(QActors.class, forVariable(variable), "public", "actors");
addMetadata();
}
public QActors(String variable, String schema, String table) {
super(QActors.class, forVariable(variable), schema, table);
addMetadata();
}
public QActors(Path<? extends QActors> path) {
super(path.getType(), path.getMetadata(), "public", "actors");
addMetadata();
}
public QActors(PathMetadata metadata) {
super(QActors.class, metadata, "public", "actors");
addMetadata();
}
public void addMetadata() {
addMetadata(firstname, ColumnMetadata.named("firstname").withIndex(2).ofType(Types.VARCHAR).withSize(64));
addMetadata(id, ColumnMetadata.named("id").withIndex(1).ofType(Types.INTEGER).withSize(10).notNull());
addMetadata(lastname, ColumnMetadata.named("lastname").withIndex(3).ofType(Types.VARCHAR).withSize(64));
}
}

More Related Content

PPTX
Spring Boot and REST API
PDF
Swagger UI
PDF
Spring boot introduction
PDF
JPA and Hibernate
PPTX
Its time to React.js
PDF
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
PPTX
Spring boot
PDF
DDD 구현기초 (거의 Final 버전)
Spring Boot and REST API
Swagger UI
Spring boot introduction
JPA and Hibernate
Its time to React.js
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
Spring boot
DDD 구현기초 (거의 Final 버전)

What's hot (20)

PPTX
Javascript best practices
PPTX
Spring Web MVC
PPTX
PPTX
React hooks
PDF
React JS - Introduction
PPTX
Spring Security 5
PPTX
Intro to React
PDF
What Is React | ReactJS Tutorial for Beginners | ReactJS Training | Edureka
PPTX
Introduction to React JS for beginners
PDF
A Separation of Concerns: Clean Architecture on Android
PDF
ReactJS presentation
PDF
PPTX
Spring data jpa
PPTX
Spring boot
PPTX
Spring Framework
PPTX
Reactjs
PDF
Testing Spring Boot Applications
PDF
WEB DEVELOPMENT USING REACT JS
PDF
React.js and Redux overview
PDF
스프링 시큐리티 구조 이해
Javascript best practices
Spring Web MVC
React hooks
React JS - Introduction
Spring Security 5
Intro to React
What Is React | ReactJS Tutorial for Beginners | ReactJS Training | Edureka
Introduction to React JS for beginners
A Separation of Concerns: Clean Architecture on Android
ReactJS presentation
Spring data jpa
Spring boot
Spring Framework
Reactjs
Testing Spring Boot Applications
WEB DEVELOPMENT USING REACT JS
React.js and Redux overview
스프링 시큐리티 구조 이해
Ad

Viewers also liked (20)

PPTX
MyBatis
PDF
Springboot Overview
PDF
MyBatis 개요와 Java+MyBatis+MySQL 예제
ODP
Springboot and camel
PPTX
SeaJUG May 2012 mybatis
PPTX
How to Choose an API Automation Tool for a Distributed Cloud-based App: To...
PPTX
Secure RESTful API Automation With JavaScript
PDF
OpenERP 6.1 Framework Changes
PPTX
Design Summit - RESTful API Overview - John Hardy
PPTX
Telephonic etiquettes
PDF
Crash Introduction to Modern Java Data Access: Understanding JPA, Hibernate, ...
PDF
좌충우돌 ORM 개발기 | Devon 2012
PDF
Светлана Исакова «Язык Kotlin»
PPTX
RESTful API Automation with JavaScript
PDF
Kotlin in action
PDF
Querydsl overview 2014
PDF
A brief introduction to Realm with Kotlin
PPTX
Frisby: Rest API Automation Framework
PDF
Web API Test Automation using Frisby & Node.js
PPTX
Flyway: The agile database migration framework for Java
MyBatis
Springboot Overview
MyBatis 개요와 Java+MyBatis+MySQL 예제
Springboot and camel
SeaJUG May 2012 mybatis
How to Choose an API Automation Tool for a Distributed Cloud-based App: To...
Secure RESTful API Automation With JavaScript
OpenERP 6.1 Framework Changes
Design Summit - RESTful API Overview - John Hardy
Telephonic etiquettes
Crash Introduction to Modern Java Data Access: Understanding JPA, Hibernate, ...
좌충우돌 ORM 개발기 | Devon 2012
Светлана Исакова «Язык Kotlin»
RESTful API Automation with JavaScript
Kotlin in action
Querydsl overview 2014
A brief introduction to Realm with Kotlin
Frisby: Rest API Automation Framework
Web API Test Automation using Frisby & Node.js
Flyway: The agile database migration framework for Java
Ad

Similar to SpringBoot with MyBatis, Flyway, QueryDSL (9)

PDF
#34.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자교육,국...
PDF
Spring java config
PDF
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
PPTX
SQL 쿼리를 AWS DynamoDB에서 (CLI)로 사용해 볼까요?
PDF
spring3.2 java config Servler3
PDF
MySQL Document Store
PDF
#29.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자국비지원...
PPTX
What is persistence in java
PPTX
Spring data jpa simple example_스프링학원/자바학원추천/구로IT학원/자바학원
#34.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자교육,국...
Spring java config
#36.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_재직자환급교육,실업자교육,국비지원교육, 자바교육,구...
SQL 쿼리를 AWS DynamoDB에서 (CLI)로 사용해 볼까요?
spring3.2 java config Servler3
MySQL Document Store
#29.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_스프링프레임워크 강좌, 재직자환급교육,실업자국비지원...
What is persistence in java
Spring data jpa simple example_스프링학원/자바학원추천/구로IT학원/자바학원

More from Sunghyouk Bae (16)

PDF
JUnit5 and TestContainers
PDF
Introduction of failsafe
PDF
Kotlin @ Coupang Backed - JetBrains Day seoul 2018
PDF
Spring data requery
PDF
Requery overview
PDF
Kotlin @ Coupang Backend 2017
PDF
measure metrics
PDF
Alternatives of JPA/Hibernate
PPTX
Kotlin coroutines and spring framework
PPTX
Java naming strategy (자바 명명 전략)
PPTX
테스트자동화와 TDD
PPTX
JUnit & AssertJ
PPTX
좋은 개발자 되기
PDF
Using AdoRepository
PDF
Multithread pattern 소개
PDF
Strategy Maps
JUnit5 and TestContainers
Introduction of failsafe
Kotlin @ Coupang Backed - JetBrains Day seoul 2018
Spring data requery
Requery overview
Kotlin @ Coupang Backend 2017
measure metrics
Alternatives of JPA/Hibernate
Kotlin coroutines and spring framework
Java naming strategy (자바 명명 전략)
테스트자동화와 TDD
JUnit & AssertJ
좋은 개발자 되기
Using AdoRepository
Multithread pattern 소개
Strategy Maps

Recently uploaded (20)

PDF
Design an Analysis of Algorithms I-SECS-1021-03
PDF
Digital Systems & Binary Numbers (comprehensive )
PDF
Understanding Forklifts - TECH EHS Solution
PDF
System and Network Administraation Chapter 3
PPTX
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
PPTX
history of c programming in notes for students .pptx
PPTX
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Digital Strategies for Manufacturing Companies
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PPT
Introduction Database Management System for Course Database
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PPTX
Reimagine Home Health with the Power of Agentic AI​
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PPTX
L1 - Introduction to python Backend.pptx
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PDF
2025 Textile ERP Trends: SAP, Odoo & Oracle
Design an Analysis of Algorithms I-SECS-1021-03
Digital Systems & Binary Numbers (comprehensive )
Understanding Forklifts - TECH EHS Solution
System and Network Administraation Chapter 3
Oracle E-Business Suite: A Comprehensive Guide for Modern Enterprises
history of c programming in notes for students .pptx
Agentic AI : A Practical Guide. Undersating, Implementing and Scaling Autono...
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Digital Strategies for Manufacturing Companies
VVF-Customer-Presentation2025-Ver1.9.pptx
Odoo Companies in India – Driving Business Transformation.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
Introduction Database Management System for Course Database
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Reimagine Home Health with the Power of Agentic AI​
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
L1 - Introduction to python Backend.pptx
How to Migrate SBCGlobal Email to Yahoo Easily
2025 Textile ERP Trends: SAP, Odoo & Oracle

SpringBoot with MyBatis, Flyway, QueryDSL

  • 1. Spring Boot with MyBatis KESTI 개발팀
  • 2. Spring Boot with MyBatis • mybatis-spring-boot-starter 소개 • MyBatis Tutorials • setup • mappers • testing • Bonus Tutorials • Flyway • QueryDSL 2016-10-12KESTI 개발팀 세미나 2
  • 3. MyBatis for Spring Boot • MyBatis 를 Spring Boot Application 에서 사용하기 위한 라이브러리 • 홈페이지 : http://www.mybatis.org/mybatis-spring-boot/ • 소스 : https://github.com/mybatis/mybatis-spring-boot • 예제 : • KESTI SpringBoot-MyBatis Tutorials • Spring Boot 에서 Java Config를 통해 myBatis 연동하기 2016-10-12KESTI 개발팀 세미나 3
  • 5. Tutorial Overview 1. mybatis-spring-boot-starter 프로젝트 구성 2. Mapper – Annotation / XML 방식 3. MyBatis 설정 방법 4. Flyway 를 이용한 database migration 방법 5. MyBatis 단위 테스트 2016-10-12KESTI 개발팀 세미나 5
  • 7. Project Structures configuration data handling source Spring Boot Application Flyway database migration MyBatis configuration & XML Mappers 환경설정 정보 Test 코드 2016-10-12KESTI 개발팀 세미나 7
  • 8. XML 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="kr.kesti.mybatis.examples.domain.models"> <select id="selectActorByFirstname" parameterType="String" resultType="Actor"> SELECT * FROM Actors WHERE firstname = #{firstname} limit 1 </select> <insert id="insertActor" useGeneratedKeys="true" keyProperty="id"> insert into Actors(firstname, lastname) values( #{firstname}, #{lastname} ) </insert> <!-- Oracle, PostgreSQL 은 SEQUENCE --> <!-- <insert id="insertActorBySequence"> <selectKey keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED"> SELECT SEQ_ID.nextval FROM DUAL </selectKey> INSERT INTO Autors (id, firstname, lastname) VALUES (#{id}, #{firstname}, #{lastname}) </insert> --> </mapper> 2016-10-12KESTI 개발팀 세미나 8
  • 9. Annotated Mapper /** * Actor 를 위한 MyBatis Mapper 입니다. */ public interface ActorMapper { @Select("SELECT * FROM Actors WHERE firstname = #{firstname}") Actor findByFirstname(@Param("firstname") String firstname); @Select("SELECT * FROM Actors") List<Actor> findAll(); @Delete("DELETE FROM Actors WHERE id=#{id}") void deleteById(@Param("id") Integer id); } /** * MyBatis 를 Spring boot 에서 사용하기 위한 환경설정 */ @Configuration @EnableAutoConfiguration @ComponentScan(basePackageClasses = { ActorRepository.class }) @MapperScan(basePackageClasses = { ActorMapper.class }) public class MyBatisConfiguration extends AbstractFlywayMyBatisConfiguration { 2016-10-12KESTI 개발팀 세미나 9 XML Mapper 보다 좋은점은?
  • 10. Repository / DAO /** * {@link Actor} 를 위한 Repository (DAO) 입니다. */ @Slf4j @Repository public class ActorRepository { @Autowired ActorMapper mapper; @Autowired SqlSessionTemplate session; public Actor findByFirstname(@NonNull String firstname) { return mapper.findByFirstname(firstname); } public Actor findByFirstnameWithXmlMapper(@NonNull String firstname) { return session.selectOne("selectActorByFirstname", firstname); } public List<Actor> findAll() { return mapper.findAll(); } public int insertActor(@NonNull Actor actor) { return session.insert("insertActor", actor); } public void deleteById(@NonNull Integer id) { mapper.deleteById(id); } } 2016-10-12KESTI 개발팀 세미나 10
  • 11. MyBatis JavaConfig @Configuration @EnableAutoConfiguration @ComponentScan(basePackageClasses = { ActorRepository.class }) @MapperScan(basePackageClasses = { ActorMapper.class }) public class MyBatisConfiguration extends AbstractFlywayMyBatisConfiguration { // datasource 용 properties (application.properties 에서 자동으로 정보를 읽어옴) @Inject DataSourceProperties dataSourceProperties; // mybatis 용 properties (application.properties 에서 자동으로 정보를 읽어옴) @Inject MybatisProperties mybatisProperties; @Override protected DatabaseSetting getDatabaseSetting() { return DatabaseSetting.builder() .driverClass(dataSourceProperties.getDriverClassName()) .jdbcUrl(dataSourceProperties.getUrl()) .build(); } @Override protected String getMyBatisConfigPath() { return mybatisProperties.getConfig(); } @Override protected String getMyBatisMapperPath() { return mybatisProperties.getMapperLocations()[0]; } } 2016-10-12KESTI 개발팀 세미나 11 XML Config 와 장단점 비교
  • 12. Spring Boot application.properties ### DataSource spring.datasource.driver-class-name=org.h2.Driver spring.datasource.jdbc-url=jdbc:h2:mem spring.datasource.username=sa spring.datasource.password= ### MyBatis mybatis.config=mybatis/mybatis-config.xml mybatis.mapper-locations=mybatis/mappers/**/*Mapper.xml mybatis.executor-type=simple ### Logging logging.level.root=info logging.level.kr.kesti.mybatis.examples=debug 2016-10-12KESTI 개발팀 세미나 12
  • 13. Database Setup by Flyway /** * Flyway 를 이용한 DB Migration 을 수행하도록 합니다. * * @param dataSource DataSource * @return {@link Flyway} 인스턴스 */ @Bean(initMethod = "migrate") protected Flyway flyway(DataSource dataSource) { Flyway flyway = new Flyway(); flyway.setDataSource(dataSource); if (cleanDatabaseForTest()) { flyway.clean(); } return flyway; } CREATE TABLE Actors ( id SERIAL PRIMARY KEY, firstname VARCHAR(64), lastname VARCHAR(64) ); INSERT INTO Actors (firstname, lastname) VALUES ('Sunghyouk', 'Bae'); INSERT INTO Actors (firstname, lastname) VALUES ('Misook', 'Kwon'); INSERT INTO Actors (firstname, lastname) VALUES ('Jehyoung', 'Bae'); INSERT INTO Actors (firstname, lastname) VALUES ('Jinseok', 'Kwon'); INSERT INTO Actors (firstname, lastname) VALUES ('Kildong', 'Hong'); 2016-10-12KESTI 개발팀 세미나 13
  • 14. Configuration Test @Slf4j @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = { MyBatisConfiguration.class }) public class MyBatisConfigurationTest { @Inject ActorMapper actorMapper; @Inject ActorRepository actorRepository; @Inject private SqlSessionFactory sf; @Test public void testConfiguration() { assertThat(actorMapper).isNotNull(); assertThat(actorRepository).isNotNull(); assertThat(sf).isNotNull(); } } 2016-10-12KESTI 개발팀 세미나 14
  • 15. Repository Test @Slf4j @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = { MyBatisConfiguration.class }) public class ActorRepositoryTest { @Inject ActorRepository actorRepo; private static final String FIRST_NAME = "Sunghyouk"; @Test public void testConfiguration() { assertThat(actorRepo).isNotNull(); } @Test public void testFindByFirstname() { Actor actor = actorRepo.findByFirstname(FIRST_NAME); assertThat(actor).isNotNull(); } @Test public void testFindByFirstnameWithXmlMapper() { Actor actor = actorRepo.findByFirstnameWithXmlMapper(FIRST_NAME); assertThat(actor).isNotNull(); } @Test public void testFindAll() { List<Actor> actors = actorRepo.findAll(); assertThat(actors.size()).isGreaterThan(0); } @Test public void testInsertActor() { String firstname = "mybatis"; Actor actor = Actor.of(null, firstname, "kesti"); int rowCount = actorRepo.insertActor(actor); log.debug("rowCount={}", rowCount); actor = actorRepo.findByFirstname(firstname); log.debug("actor={}", actor); assertThat(actor).isNotNull(); assertThat(actor.getFirstname()).isEqualTo(firstname); actorRepo.deleteById(actor.getId()); } } 2016-10-12KESTI 개발팀 세미나 15
  • 16. Flyway Evolve your Database Schema easily and reliably across all your instances 2016-10-12KESTI 개발팀 세미나 16
  • 17. Code 관리는 git 2016-10-12KESTI 개발팀 세미나 17 DB 형상 관리는?
  • 18. DB Schema Control by Dev Step 2016-10-12KESTI 개발팀 세미나 18
  • 19. DB Schema Version Control 2016-10-12KESTI 개발팀 세미나 19
  • 20. QueryDSL Unified Queries for Java with Typesafe 2016-10-12KESTI 개발팀 세미나 20
  • 21. QueryDSL 개요 • 질의어를 문자열이 아닌 Java Code로 표현 • Query문 – type check 불가 / 실행 전에는 오류 검출 불가 • Java Code는 • Compile Error를 미리 검출 • Code assistant 활용 100% • Refactoring 용이 • 다양한 저장소에 대한 일관된 질의어 제작 가능 • Collection, RDBMS, MongoDB, Lucene … • .NET 의 LINQ 같은 목적 (Langunage-INtegrated Query) • 참고 : 한글 매뉴얼 (단 3.4.0 기준임. 4.x 는 package 명이 달라졌음) 2016-10-12KESTI 개발팀 세미나 21
  • 22. QueryDSL 적용 범위 JPA (Java Persistence API) JDO (Java Data Object) SQL Lucence MongoDB Collections QueryDSL RDBMS ORM Lucene (search engine) MongoD B List / Map 2016-10-12KESTI 개발팀 세미나 22
  • 23. QueryDSL for SQL • Select • Join (innerJoin, join, leftJoin, rightJoin, fullJoin) • group by / having • order by • limit / offset / restrict • subquery • Window functions • Common Table Expression (CTE) • Insert • Update • Delete 2016-10-12KESTI 개발팀 세미나 23
  • 24. SELECT with Projections 2016-10-12KESTI 개발팀 세미나 24 QActors $ = QActors.actors; List<Tuple> rows = query.select($.id, $.firstname, $.lastname) .from($) .fetch(); QActors $ = QActors.actors; List<Actor> actors = query.select(Projections.constructor(Actor.class, $.id, $.firstname, $.lastname)) .from($) .fetch();
  • 25. SELECT – Filter, GroupBy 2016-10-12KESTI 개발팀 세미나 25 Actor actor = query.select(Projections.constructor(Actor.class, $.id, $.firstname, $.lastname)) .from($) .where($.lastname.eq("Bae")) .fetchFirst(); List<Tuple> rows = query.select($.lastname, $.lastname.count().as("cnt")) .from($) .groupBy($.lastname) .fetch();
  • 26. SELECT : Subquery 2016-10-12KESTI 개발팀 세미나 26 QActors $ = QActors.actors; QActors $2 = new QActors("$2"); SQLQuery<Actor> sq = query.select(Projections.constructor(Actor.class, $.id, $.firstname, $.lastname)) .from($) .where($.id.eq(SQLExpressions.select($2.id.max()).from($2))); List<Actor> actors = sq.fetch();
  • 27. CUD 2016-10-12KESTI 개발팀 세미나 27 QActors $ = QActors.actors; long inserted = query.insert($) .columns($.firstname, $.lastname) .values("querydsl", "examples") .execute(); long updated = query.update($) .set($.lastname, "updated examples") .where($.firstname.eq("querydsl")) .execute(); long deleted = query.delete($) .where($.firstname.eq("querydsl")) .execute();
  • 28. CUD Batch Execution 2016-10-12KESTI 개발팀 세미나 28 QActors $ = QActors.actors; int COUNT = 100; // INSERT SQLInsertClause insertClause = query.insert($); for (int i = 0; i < COUNT; i++) { insertClause.set($.firstname, "firstname-" + i) .set($.lastname, "lastname-" + i) .addBatch(); } long insertedCount = insertClause.execute(); // UPDATE SQLUpdateClause updateClause = query.update($); for (int i = 0; i < COUNT; i++) { updateClause.set($.firstname, "updated-firstname-" + i) .where($.lastname.eq("lastname-" + i)) .addBatch(); } long updatedCount = updateClause.execute(); // DELETE SQLDeleteClause deleteClause = query.delete($); for (int i = 0; i < COUNT; i++) { deleteClause.where($.firstname.eq("updated-firstname-" + i)) .addBatch(); } long deletedCount = deleteClause.execute();
  • 29. QueryDSL Dependency 2016-10-12KESTI 개발팀 세미나 29 <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql</artifactId> <version>${com.querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql-spring</artifactId> <version>${com.querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql-codegen</artifactId> <version>${com.querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-spatial</artifactId> <version>${com.querydsl.version}</version> </dependency> <dependency> <groupId>com.querydsl</groupId> <artifactId>querydsl-sql-spatial</artifactId> <version>${com.querydsl.version}</version> </dependency>
  • 30. QueryDSL APT 2016-10-12KESTI 개발팀 세미나 30 <plugin> <groupId>com.querydsl</groupId> <artifactId>querydsl-maven-plugin</artifactId> <version>${com.querydsl.version}</version> <executions> <execution> <goals> <goal>export</goal> </goals> </execution> </executions> <configuration> <jdbcDriver>org.postgresql.Driver</jdbcDriver> <jdbcUrl>jdbc:postgresql://localhost/querydsl</jdbcUrl> <jdbcUser>root</jdbcUser> <jdbcPassword>root</jdbcPassword> <packageName>kesti4j.data.querydsl.models</packageName> <sourceFolder>${project.basedir}/target/generated-sources/java</sourceFolder> <targetFolder>${project.basedir}/target/generated-sources/java</targetFolder> <spatial>true</spatial> </configuration> <dependencies> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>9.4-1206-jdbc42</version> </dependency> </dependencies> </plugin>
  • 31. QueryDSL Generated Code 2016-10-12KESTI 개발팀 세미나 31 /** * QActors is a Querydsl query type for QActors */ @Generated("com.querydsl.sql.codegen.MetaDataSerializer") public class QActors extends RelationalPathSpatial<QActors> { private static final long serialVersionUID = 822224394; public static final QActors actors = new QActors("actors"); public final StringPath firstname = createString("firstname"); public final NumberPath<Integer> id = createNumber("id", Integer.class); public final StringPath lastname = createString("lastname"); public final com.querydsl.sql.PrimaryKey<QActors> actorsPkey = createPrimaryKey(id); public QActors(String variable) { super(QActors.class, forVariable(variable), "public", "actors"); addMetadata(); } public QActors(String variable, String schema, String table) { super(QActors.class, forVariable(variable), schema, table); addMetadata(); } public QActors(Path<? extends QActors> path) { super(path.getType(), path.getMetadata(), "public", "actors"); addMetadata(); } public QActors(PathMetadata metadata) { super(QActors.class, metadata, "public", "actors"); addMetadata(); } public void addMetadata() { addMetadata(firstname, ColumnMetadata.named("firstname").withIndex(2).ofType(Types.VARCHAR).withSize(64)); addMetadata(id, ColumnMetadata.named("id").withIndex(1).ofType(Types.INTEGER).withSize(10).notNull()); addMetadata(lastname, ColumnMetadata.named("lastname").withIndex(3).ofType(Types.VARCHAR).withSize(64)); } }