[Spring Boot 실습 #13] MyBatis CRUD - H2 Database
1. Spring Boot Mybatis 설정 및 기본 CRUD (H2 Database)
2. 개발환경
2.1. 개발환경
2.2. 라이브러리
3. 구현
3.1. 프로젝트 구조
3.2. 구현 - 소스코드
4. 결과
4.1. 테이블 생성
4.2. save
4.3. findAll, findById
4.4. update
4.5. delete
1. 임의의 http status code 반환
http 호출로 에러 발생시 정상 범위로 판단하여 임의의 http status code 반환
2. 개발환경
2.1. 개발환경
MacOS M1 - macOS Monterey 12.0.1
IntelliJ IDEA 2021.2 (Community Edition)
2.2. 라이브러리
JDK 11
spring-boot-2.6.6
3. 구현
3.1. 프로젝트 구조
3.2. 구현 - 소스코드
[ pom.xml ]
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
<?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 https://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.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.home.test</groupId>
<artifactId>spring-boot-mybatis-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-mybatis-starter</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-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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>
|
[ mybatis-config.xml ]
1
2
3
4
5
6
7
8
9
10
|
<?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>
<typeAliases>
<package name="io.home.test.app.product.domain"/>
</typeAliases>
</configuration>
|
[ productMapper.xml ]
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<?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="io.home.test.app.product.mapper.ProductMapper">
<select id="findAll" resultType="io.home.test.app.product.domain.Product">
select * from TB_PRODUCT;
</select>
<select id="findById" resultType="io.home.test.app.product.domain.Product">
select * from TB_PRODUCT where id = #{id};
</select>
<insert id="save" parameterType="io.home.test.app.product.domain.Product">
INSERT INTO TB_PRODUCT (
name,
description,
price,
record_state,
status,
created_date,
updated_date
)
VALUES (
#{name},
#{description},
#{price},
#{recordState},
#{status},
#{createdDate},
#{updatedDate}
)
</insert>
<update id="update" parameterType="io.home.test.app.product.domain.Product">
UPDATE
TB_PRODUCT
SET
<if test="name!=null">name=#{name}</if>
<if test="description!=null">,description=#{description}</if>
<if test="price!=null">,price=#{price}</if>
<if test="recordState!=null">,record_state=#{recordState}</if>
<if test="status!=null">,status=#{status}</if>
<if test="createdDate!=null">,created_date=#{createdDate}</if>
<if test="updatedDate!=null">,updated_date=#{updatedDate}</if>
WHERE
id=#{id};
</update>
<delete id="deleteById">
delete from TB_PRODUCT where id = #{id};
</delete>
</mapper>
|
[ init_ddl.sql ] 테이블 생성용 sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
-- tb_product
CREATE TABLE IF NOT EXISTS `TB_PRODUCT`(
id bigint auto_increment primary key comment 'id',
-- product_token varchar(36) not null comment 'product token',
name varchar(255) not null comment 'product name',
description varchar(255) null comment 'product description',
status varchar(30) not null default 'PREPARED' comment 'status',
record_state varchar(30) not null default 'ACTIVE' comment 'record_state',
price bigint default 0 comment 'product price',
created_date datetime(6) null comment 'created date',
updated_date datetime(6) null comment 'updated date'
);
-- create
-- index tb_product_idx01 on tb_product (product_token);
create
index tb_product_idx02 on tb_product (created_date);
create
index tb_product_idx03 on tb_product (updated_date);
|
[ application.properties ]
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
31
32
33
34
35
36
37
38
39
40
41
|
server.port=8080
# ===============================
# = SPRING JPA
# ===============================
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none
#spring.jpa.generate-ddl=true
#spring.jpa.hibernate.ddl-auto=update
# ===============================
# = SPRING H2
# ===============================
spring.h2.console.enabled=true
spring.h2.console.settings.web-allow-others=true
# ===============================
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
spring.datasource.platform=h2
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:h2:file:./target/h2db/db/app;DB_CLOSE_DELAY=-1
spring.datasource.username=tester
spring.datasource.password=
# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle=true
spring.datasource.validationQuery=SELECT 1
# jndi
#spring.datasource.jndi-name=jdbc/test
# ===============================
# = MYBATIS
# ===============================
# MyBatis
# mapper.xml
mybatis.mapper-locations=mappers/**/*.xml
# model camel case
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.config-location=classpath:mybatis-config.xml
|
[ io.home.test.app.product.domain.Product.java ] Product 모델
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package io.home.test.app.product.domain;
import io.home.test.base.entity.RecordState;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.relational.core.mapping.Table;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
@Entity
//@Table(name = "tb_product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal price;
@CreationTimestamp
@Column(name = "created_date", updatable = false)
private ZonedDateTime createdDate = ZonedDateTime.now();
@UpdateTimestamp
@Column(name = "updated_date", updatable = true)
private ZonedDateTime updatedDate;
@Enumerated
@Column(name = "record_state")
private RecordState recordState = RecordState.ACTIVE;
@Enumerated
private Status status = Status.PREPARE;
public enum Status {
PREPARE("prepare"),
ON_SALE("on sale"),
OUT_OF_STOCK("out of stock"),
END_OF_SALE("end of sale");
private final String desc;
Status(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
public Product() {
}
public Product(Long id, String name, BigDecimal price) {
this.id = id;
this.name = name;
this.price = price;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public ZonedDateTime getCreatedDate() {
return createdDate;
}
public void setCreatedDate(ZonedDateTime createdDate) {
this.createdDate = createdDate;
}
public ZonedDateTime getUpdatedDate() {
return updatedDate;
}
public void setUpdatedDate(ZonedDateTime updatedDate) {
this.updatedDate = updatedDate;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public RecordState getRecordState() {
return recordState;
}
public void setRecordState(RecordState recordState) {
this.recordState = recordState;
}
}
|
[ io.home.test.app.product.mapper.ProductMapper.java ] mybatis의 productMapper.xml과 연결
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
|
package io.home.test.app.product.mapper;
import io.home.test.app.product.domain.Product;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductMapper {
List<Product> findAll();
Product findById(Long id);
void save(Product product);
void update(Product product);
void deleteById(Long id);
// @Select("SELECT CURRENT_TIMESTAMP result FROM sysibm.sysdummy1") // db2
@Select("SELECT CURRENT_TIMESTAMP FROM DUAL") // H2
String now() throws Exception;
}
|
[ io.home.test.app.product.service.ProductService.java ]
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
31
32
33
34
35
36
37
38
39
40
41
42
|
package io.home.test.app.product.service;
import io.home.test.app.product.domain.Product;
import io.home.test.app.product.mapper.ProductMapper;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ProductService {
private final ProductMapper productMapper;
public ProductService(ProductMapper productMapper) {
this.productMapper = productMapper;
}
public List<Product> findAll() {
return productMapper.findAll();
}
public Product findById(Long id) {
return productMapper.findById(id);
}
public void save(Product product) {
productMapper.save(product);
}
public void update(Product product) {
productMapper.update(product);
}
public void deleteById(Long id) {
productMapper.deleteById(id);
}
public String now() throws Exception {
return productMapper.now();
}
}
|
[ io.home.test.app.product.web.ProductResource.java ]
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package io.home.test.app.product.web;
import io.home.test.app.product.domain.Product;
import io.home.test.app.product.service.ProductService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.List;
@RequestMapping(value = "/api/products")
@RestController
public class ProductResource {
private final ProductService productService;
public ProductResource(ProductService productService) {
this.productService = productService;
}
@GetMapping(value = "")
public ResponseEntity<List<Product>> findAllProducts() {
List<Product> products = productService.findAll();
return ResponseEntity.ok(products);
}
@GetMapping(value = "/{id}")
public ResponseEntity<Product> findById(@PathVariable(value = "id") Long id) {
Product product = productService.findById(id);
return ResponseEntity.ok(product);
}
@GetMapping(value = "/test/save")
public ResponseEntity<?> testSave() {
Product product = new Product();
product.setName("test-product");
product.setPrice(BigDecimal.valueOf(15000L));
productService.save(product);
return ResponseEntity.ok("saved");
}
@GetMapping(value = "/test/delete")
public ResponseEntity<?> testDelete(@RequestParam(value = "id") long id) {
productService.deleteById(id);
return ResponseEntity.ok("deleted");
}
@GetMapping(value = "/test/update")
public ResponseEntity<?> testUpdate(@RequestParam(value = "id") long id) {
Product oldProduct = productService.findById(id);
oldProduct.setName("new Name");
oldProduct.setDescription("new Description");
oldProduct.setUpdatedDate(ZonedDateTime.now());
productService.update(oldProduct);
return ResponseEntity.ok("updated");
}
@GetMapping(value = "/now")
public ResponseEntity<?> now() throws Exception {
String now = productService.now();
return ResponseEntity.ok(now);
}
}
|
[ io.home.test.base.entity.RecordState.java ]
1
2
3
4
5
6
7
|
package io.home.test.base.entity;
public enum RecordState {
ACTIVE,
UPDATING,
DELETED
}
|
[ io.home.test.SpringBootMybatisStarterApplication.java ] @MapperScan 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package io.home.test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan(basePackageClasses = SpringBootMybatisStarterApplication.class)
@SpringBootApplication
public class SpringBootMybatisStarterApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatisStarterApplication.class, args);
}
}
|
4. 결과
4.1. 테이블 생성
H2 console 접근
테이블 생성
4.2. save
save
http://localhost:8080/api/products/test/save
4.3. findAll, findById
findAll
http://localhost:8080/api/products
findById
http://localhost:8080/api/products/{id}
4.4. update
update
http://localhost:8080/api/products/test/update?id={id}
findById
http://localhost:8080/api/products/{id}
4.5. delete
'스프링 (Spring) > Spring Boot' 카테고리의 다른 글
[Spring Boot 실습 #15] MySQL 5.7 설정 - HikariCP 설정 (application.properties) (0) | 2022.07.04 |
---|---|
[Spring Boot 실습 #14] 백그라운드 실행 (0) | 2022.06.22 |
[Spring Boot 실습 #12] 에러 처리 (Exception Handling) - 임의의 http status code 반환 (0) | 2022.04.20 |
[Spring Boot 실습 #11] 에러 처리 (Exception Handling) - 정상 범위의 http status code로 반환 (0) | 2022.04.19 |
[Spring Boot 실습 #10] Log4j2 설정 및 적용 - level 별 appender 분리 (0) | 2022.03.18 |