CRUD操作を行うREST APIを作成する
とりあえずバリデーションはなしでCURD操作に注力する
build.gradle
plugins { id 'org.springframework.boot' version '2.5.0' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { compileOnly 'org.projectlombok:lombok' implementation 'org.slf4j:slf4j-api' implementation 'ch.qos.logback:logback-core' implementation 'ch.qos.logback:logback-classic' implementation 'com.google.guava:guava:30.1.1-jre' implementation 'org.apache.commons:commons-lang3' implementation 'commons-beanutils:commons-beanutils:1.9.2' implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4' runtimeOnly 'mysql:mysql-connector-java' testImplementation 'org.springframework.boot:spring-boot-starter-test' } test { useJUnitPlatform() }
/src/main/resources/application.yml
server: servlet: context-path: /sample spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:33060/sample username: mysql-app password: mysql-app mybatis: configuration: map-underscore-to-camel-case: true logging: level: org: springframework: WARN com: example: demo: domain: mapper: DEBUG
/src/main/java/com/example/demo/DemoApplication.java
package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // https://spring.pleiades.io/spring-boot/docs/current/reference/html/features.html @SpringBootApplication @MapperScan("com.example.demo.domain.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
/src/main/java/com/example/demo/web/controller/UsersController.java
package com.example.demo.web.controller; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.example.demo.service.UsersService; import com.example.demo.service.dto.UserDto; import com.example.demo.web.controller.helper.UsersHelper; import com.example.demo.web.dto.request.UsersCreateRequest; import com.example.demo.web.dto.request.UsersUpdateRequest; import com.example.demo.web.dto.response.UsersCreateResponse; import com.example.demo.web.dto.response.UsersGetListResponse; import com.example.demo.web.dto.response.UsersGetResponse; import lombok.RequiredArgsConstructor; @CrossOrigin @RestController @RequestMapping(value = "/users", produces = "application/json;charset=UTF-8") @RequiredArgsConstructor public class UsersController { private static final Logger LOGGER = LoggerFactory.getLogger(UsersController.class); private final UsersService usersService; @RequestMapping(method = RequestMethod.GET) public UsersGetListResponse getList() { List<UserDto> resultList = usersService.findAll(); return UsersHelper.convertToUsersGetListResponse(resultList); } @RequestMapping(method = RequestMethod.GET, value = "{id}") public UsersGetResponse get(@PathVariable("id") Long id) { LOGGER.info("#id : {}", id); UserDto result = usersService.findById(id); return UsersHelper.convertToUsersGetResponse(result); } @RequestMapping(method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) public UsersCreateResponse create(@Validated @RequestBody UsersCreateRequest request) { LOGGER.info("#UsersCreateRequest : {}", request); UserDto userDto = UsersHelper.convertFromUsersCreateRequest(request); UserDto result = usersService.create(userDto); return UsersHelper.convertToUsersCreateResponse(result); } @RequestMapping(method = RequestMethod.PUT, value = "{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void update(@PathVariable("id") Long id, @Validated @RequestBody UsersUpdateRequest request) { LOGGER.info("#id : {}", id); LOGGER.info("#UsersUpdateRequest : {}", request); UserDto userDto = UsersHelper.convertFromUsersUpdateRequest(id, request); usersService.update(userDto); } @RequestMapping(method = RequestMethod.DELETE, value = "{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable("id") Long id) { LOGGER.info("#id : {}", id); usersService.delete(id); } }
/src/main/java/com/example/demo/web/controller/helper/UsersHelper.java
package com.example.demo.web.controller.helper; import java.util.List; import java.util.stream.Collectors; import com.example.demo.common.utils.ConvertUtils; import com.example.demo.service.dto.UserDto; import com.example.demo.web.dto.request.UsersCreateRequest; import com.example.demo.web.dto.request.UsersUpdateRequest; import com.example.demo.web.dto.response.UsersCreateResponse; import com.example.demo.web.dto.response.UsersGetListResponse; import com.example.demo.web.dto.response.UsersGetListResponse.User; import com.example.demo.web.dto.response.UsersGetResponse; public class UsersHelper { public static UsersGetListResponse convertToUsersGetListResponse(List<UserDto> list) { final UsersGetListResponse response = new UsersGetListResponse(); List<User> users = list.stream().map((dto) -> { final UsersGetListResponse.User user = new UsersGetListResponse.User(); ConvertUtils.copyProperties(dto, user); return user; }).collect(Collectors.toList()); response.setUsers(users); return response; } public static UsersGetResponse convertToUsersGetResponse(UserDto dto) { final UsersGetResponse response = new UsersGetResponse(); ConvertUtils.copyProperties(dto, response); return response; } public static UserDto convertFromUsersCreateRequest(UsersCreateRequest request) { final UserDto userDto = new UserDto(); ConvertUtils.copyProperties(request, userDto); return userDto; } public static UsersCreateResponse convertToUsersCreateResponse(UserDto dto) { final UsersCreateResponse response = new UsersCreateResponse(); ConvertUtils.copyProperties(dto, response); return response; } public static UserDto convertFromUsersUpdateRequest(Long id, UsersUpdateRequest request) { final UserDto userDto = new UserDto(); ConvertUtils.copyProperties(request, userDto); userDto.setUserId(id); return userDto; } }
/src/main/java/com/example/demo/web/dto/request/UsersCreateRequest.java
package com.example.demo.web.dto.request; import lombok.Data; @Data public class UsersCreateRequest { private String userName; private String mailAddress; private String password; private String confirmPassword; }
/src/main/java/com/example/demo/web/dto/request/UsersUpdateRequest.java
package com.example.demo.web.dto.request; import lombok.Data; @Data public class UsersUpdateRequest { private String userName; private String mailAddress; private String password; private String confirmPassword; private String deleted; }
/src/main/java/com/example/demo/web/dto/response/UsersGetListResponse.java
package com.example.demo.web.dto.response; import java.util.List; import com.fasterxml.jackson.annotation.JsonInclude; import com.google.common.collect.Lists; import lombok.Data; @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class UsersGetListResponse { private List<User> users = Lists.newArrayList(); @Data public static class User { private String userId; private String userName; private String mailAddress; } }
/src/main/java/com/example/demo/web/dto/response/UsersGetResponse.java
package com.example.demo.web.dto.response; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class UsersGetResponse { private String userId; private String userName; private String mailAddress; }
/src/main/java/com/example/demo/web/dto/response/UsersCreateResponse.java
package com.example.demo.web.dto.response; import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Data; @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class UsersCreateResponse { private String userId; private String userName; private String mailAddress; private String registTime; private String updateTime; }
/src/main/java/com/example/demo/service/UsersService.java
package com.example.demo.service; import java.util.List; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.example.demo.domain.model.Users; import com.example.demo.domain.repository.UsersRepository; import com.example.demo.service.dto.UserDto; import com.example.demo.utils.DateUtils; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class UsersService { private static final Logger LOGGER = LoggerFactory.getLogger(UsersService.class); private final UsersRepository usersRepository; @Transactional(readOnly = true) public List<UserDto> findAll() { return usersRepository.findAll().stream().map((users) -> { return convert(users); }).collect(Collectors.toList()); } @Transactional(readOnly = true) public UserDto findById(Long id) { Users users = usersRepository.findById(id); if (users == null) { return null; } return convert(users); } @Transactional(readOnly = false) public UserDto create(UserDto dto) { final Users users = Users.builder() .mailAddress(dto.getMailAddress()) .userName(dto.getUserName()) .password(dto.getPassword()) .registTime(DateUtils.getThreadDateTime()) .updateTime(DateUtils.getThreadDateTime()) .deleted(0) .build(); usersRepository.create(users); LOGGER.info("#users : {}", users); return convert(users); } @Transactional(readOnly = false) public void update(UserDto dto) { final Users users = Users.builder() .id(dto.getUserId()) .mailAddress(dto.getMailAddress()) .userName(dto.getUserName()) .password(dto.getPassword()) .updateTime(DateUtils.getThreadDateTime()) .deleted(dto.getDeleted()) .build(); usersRepository.update(users); } @Transactional(readOnly = false) public void delete(Long id) { usersRepository.delete(id); } private UserDto convert(Users users) { UserDto dto = new UserDto(); dto.setUserId(users.getId()); dto.setMailAddress(users.getMailAddress()); dto.setUserName(users.getUserName()); dto.setRegistTime(users.getRegistTime()); dto.setUpdateTime(users.getUpdateTime()); dto.setDeleted(users.getDeleted()); return dto; } }
/src/main/java/com/example/demo/service/dto/UserDto.java
package com.example.demo.service.dto; import java.util.Date; import lombok.Data; @Data public class UserDto { private Long userId; private String userName; private String mailAddress; private String password; private Date registTime; private Date updateTime; private Integer deleted; }
/src/main/java/com/example/demo/domain/repository/UsersRepository.java
package com.example.demo.domain.repository; import java.util.List; import org.springframework.stereotype.Repository; import com.example.demo.domain.mapper.UsersMapper; import com.example.demo.domain.model.Users; import lombok.RequiredArgsConstructor; @Repository @RequiredArgsConstructor public class UsersRepository { private final UsersMapper usersMapper; public List<Users> findAll() { return usersMapper.findAll(); } public Users findById(Long id) { return usersMapper.findById(id); } public int create(Users users) { return usersMapper.insert(users); } public int update(Users users) { return usersMapper.update(users); } public int delete(Long id) { return usersMapper.delete(id); } }
/src/main/java/com/example/demo/domain/mapper/UsersMapper.java
package com.example.demo.domain.mapper; import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import com.example.demo.domain.model.Users; @Mapper public interface UsersMapper { List<Users> findAll(); Users findById(@Param("id") Long id); Integer insert(@Param("users") Users users); Integer update(@Param("users") Users users); Integer delete(@Param("id") Long id); }
/src/main/resources/com/example/demo/domain/mapper/UsersMapper.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.domain.mapper.UsersMapper"> <select id="findAll" resultType="com.example.demo.domain.model.Users"> SELECT ID, MAIL_ADDRESS, USER_NAME, PASSWORD, LAST_LOGIN_TIME, REGIST_TIME, UPDATE_TIME, DELETED FROM t_users WHERE DELETED = 0 ORDER BY ID </select> <select id="findById" resultType="com.example.demo.domain.model.Users"> SELECT ID, MAIL_ADDRESS, USER_NAME, PASSWORD, LAST_LOGIN_TIME, REGIST_TIME, UPDATE_TIME, DELETED FROM t_users WHERE ID = #{id} </select> <insert id="insert" useGeneratedKeys="true" keyProperty="id"> INSERT INTO t_users ( MAIL_ADDRESS, USER_NAME, PASSWORD, LAST_LOGIN_TIME, REGIST_TIME, UPDATE_TIME, DELETED ) VALUES ( #{users.mailAddress}, #{users.userName}, SHA2(#{users.password}, 256), NULL, #{users.registTime}, #{users.updateTime}, 0 ) </insert> <update id="update"> UPDATE t_users SET UPDATE_TIME = #{users.updateTime} <if test="users.mailAddress != null"> ,MAIL_ADDRESS = #{users.mailAddress} </if> <if test="users.userName != null"> ,USER_NAME = #{users.userName} </if> <if test="users.password != null"> ,PASSWORD = SHA2(#{users.password}, 256) </if> <if test="users.deleted != null"> ,DELETED = #{users.deleted} </if> WHERE ID = #{users.id} </update> <delete id="delete"> DELETE FROM t_users WHERE ID = #{id} </delete> </mapper>
/src/main/java/com/example/demo/common/utils/DateUtils.java
package com.example.demo.common.utils; import java.util.Date; import java.util.regex.Pattern; import org.apache.commons.lang3.time.DateFormatUtils; public class DateUtils { private static final String THREAD_LOCAL_KEY = "___THREAD_LOCAL_DATE___"; // ISO8601拡張形式 private static final Pattern DEFAULT_DATE_TIME_FORMAT_PATTERN = Pattern .compile("[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\+[0-9]{2}:[0-9]{2}"); private DateUtils() { } public static void setThreadDateTime(final Date datetime) { ThreadLocalUtils.set(THREAD_LOCAL_KEY, datetime); } public static Date getThreadDateTime() { return ThreadLocalUtils.get(THREAD_LOCAL_KEY, Date.class); } public static String format(Date date) { if (date == null) { return null; } return DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.format(date); } public static Date parse(String strDate) { try { return DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.parse(strDate); } catch (Exception e) { return null; } } public static boolean isDefaultDateTimeFormat(String str) { if (str == null) { return false; } return DEFAULT_DATE_TIME_FORMAT_PATTERN.matcher(str).matches(); } }
/src/main/java/com/example/demo/common/utils/ThreadLocalUtils.java
package com.example.demo.common.utils; import java.util.Map; import com.google.common.collect.Maps; public class ThreadLocalUtils { private ThreadLocalUtils() { } private static final ThreadLocal<Map<String, Object>> POOL = new ThreadLocal<Map<String, Object>>() { @Override protected Map<String, Object> initialValue() { return Maps.newHashMap(); } }; public static Object set(String key, Object value) { return POOL.get().put(key, value); } public static <T> T get(String key, Class<T> clazz) { return clazz.cast(POOL.get().get(key)); } public static void clear() { POOL.get().clear(); } }
/src/main/java/com/example/demo/common/utils/ConvertUtils.java
package com.example.demo.common.utils; import java.lang.reflect.InvocationTargetException; import java.util.Date; import org.apache.commons.beanutils.BeanUtilsBean; import org.apache.commons.beanutils.ConvertUtilsBean; import org.apache.commons.beanutils.Converter; import org.apache.commons.beanutils.converters.DateTimeConverter; public class ConvertUtils { private static final BeanUtilsBean BUB; static { BUB = new BeanUtilsBean(new ConvertUtilsBean(), BeanUtilsBean.getInstance().getPropertyUtils()); BUB.getConvertUtils().register(new MyStringConverter(), String.class); final MyDateConverter myDateConverter = new MyDateConverter(); myDateConverter.setPatterns(new String[] { "yyyyMMdd", "yyyyMMddHHmmss", "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss" }); BUB.getConvertUtils().register(myDateConverter, Date.class); } static class MyStringConverter implements Converter { @Override public <T> T convert(Class<T> type, Object value) { if (value == null) { return null; } else { if (value instanceof java.util.Date) { return type.cast(DateUtils.format((Date) value)); } return type.cast(value.toString()); } } } static class MyDateConverter extends DateTimeConverter { public MyDateConverter() { super(); } public MyDateConverter(Object defaultValue) { super(defaultValue); } @Override protected Class<?> getDefaultType() { return Date.class; } @Override public <T> T convert(Class<T> type, Object value) { if (value != null && value instanceof java.lang.String && DateUtils.isDefaultDateTimeFormat((String) value)) { return type.cast(DateUtils.parse((String) value)); } else { return super.convert(type, value); } } } public static void copyProperties(Object src, Object dest) { try { BUB.copyProperties(dest, src); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); } } }
/src/main/java/com/example/demo/web/filter/ThreadLocalFilter.java
package com.example.demo.web.filter; import java.io.IOException; import java.util.Date; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.springframework.stereotype.Component; import com.example.demo.common.utils.DateUtils; @Component public class ThreadLocalFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { DateUtils.setThreadDateTime(new Date()); chain.doFilter(request, response); } }