1 Обзор
Содержание этой статьи включает следующее:
- Spring Boot интегрирует mybatis
- Spring Boot интегрирует плагин пейджинга pagehelper для определения связанных классов для пейджинга.
- Реализовать класс инструментов: модель для dto, реализовать разделение уровня данных и транспортного уровня.
- Полностью показывает весь процесс ввода URL-адреса из браузера и манипулирования данными из базы данных.
2. Spring Boot интегрирует Mybatis
2.1. pom.xml
Связанные банки mybatis и базы данных
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<!-- Spring Boot集成mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
2.2 Класс Java Mapper, xml и модель
Тестовая таблица sql выглядит следующим образом
CREATE TABLE `test`.`test` (
`id` INT NOT NULL,
`age` INT NULL,
`name` VARCHAR(45) NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `id_UNIQUE` (`id` ASC));
Таблица соответствует классу Model: com.hry.spring.mybatis.model.TestModel
public class TestModel {
private Integer id;
private Integer age;
private String name;
// set/get略
}
Настройте класс Mapper: com.hry.spring.mybatis.mapper.TestMapper
public interface TestMapper {
int deleteByPrimaryKey(Integer id);
int insert(TestModel record);
int insertSelective(TestModel record);
TestModel selectByPrimaryKey(Integer id);
List<TestModel> selectAll();
int updateByPrimaryKeySelective(TestModel record);
int updateByPrimaryKey(TestModel record);
}
Настроить Mapper xml: TestMapper.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.hry.spring.mybatis.mapper.TestMapper">
<resultMap id="BaseResultMap" type="com.hry.spring.mybatis.model.TestModel">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="age" jdbcType="INTEGER" property="age" />
<result column="name" jdbcType="VARCHAR" property="name" />
</resultMap>
<sql id="Base_Column_List">
id, age, name
</sql>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from test
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from test
order by id desc
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
delete from test
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.hry.spring.mybatis.model.TestModel">
insert into test (id, age, name,
)
values (#{id,jdbcType=INTEGER}, #{age,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.hry.spring.mybatis.model.TestModel">
insert into test
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="age != null">
age,
</if>
<if test="name != null">
name,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="age != null">
#{age,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.hry.spring.mybatis.model.TestModel">
update test
<set>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.hry.spring.mybatis.model.TestModel">
update test
set age = #{age,jdbcType=INTEGER},
name = #{name,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
2.3 конфигурация mybatis
spring_mybatis.xml
Значение spring.datasource.url настраивается в application.yml
<!-- ### 配置数据源 begin ###-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${spring.datasource.url}" />
<property name="username" value="${spring.datasource.username}" />
<property name="password" value="${spring.datasource.password}" />
<property name="driverClassName" value="${spring.datasource.driverClassName}" />
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${spring.datasource.initialSize:5}" />
<property name="minIdle" value="${spring.datasource.minIdle:5}" />
<property name="maxActive" value="${spring.datasource.maxActive:20}" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${spring.datasource.maxWait:30000}" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${spring.datasource.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${spring.datasource.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${spring.datasource.validationQuery}" />
<property name="testWhileIdle" value="${spring.datasource.testWhileIdle}" />
<property name="testOnBorrow" value="${spring.datasource.testOnBorrow}" />
<property name="testOnReturn" value="${spring.datasource.testOnReturn}" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="${spring.datasource.poolPreparedStatements}" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="${spring.datasource.maxPoolPreparedStatementPerConnectionSize}" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="${spring.datasource.filters}" />
<property name="connectionProperties" value="{spring.datasource.connectionProperties}" />
</bean>
<!-- ### 配置数据源 end ###-->
<!-- ### Mybatis和事务配置 begin ###-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 配置扫描Mapper XML的位置 -->
<property name="mapperLocations" value="classpath:com/hry/spring/mybatis/mapper/*.xml"/>
<!-- 配置mybatis配置文件的位置 -->
<property name="configLocation" value="classpath:config/spring/mybatis_config.xml"/>
</bean>
<!-- 配置扫描Mapper接口的包路径 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hry.spring.mybatis.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 事务配置 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="batch*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config >
<aop:pointcut id="pt" expression="execution(* com.hry.spring.mybatis.service..*.*(..))" />
<aop:advisor pointcut-ref="pt" advice-ref="txAdvice"/>
</aop:config>
<!-- ### Mybatis和事物配置 end ###-->
mybatis_config.xml
<?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>
</configuration>
2.4. application.yml
Информация для настройки базы данных следующая:
# 数据库配置
spring:
datasource:
#### Datasource 配置 ####
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
url: jdbc:mysql://127.0.0.1:3306/test?zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2b8&useSSL=true
# url: jdbc:mysql://127.0.0.1:3306/test
driverClassName: com.mysql.cj.jdbc.Driver
# driverClassName: oracle.jdbc.driver.OracleDriver
# 下面为连接池的补充设置,应用到上面所有数据源中# 初始化大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 30000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
timeBetweenEvictionRunsMillis: 60000
validationQuery: SELECT 1 FROM DUAL
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: log4j
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
2.5 Spring boot интегрирует mybatis
Загрузите конфигурацию mybatis через @ImportResource. Метод fastJsonHttpMessageConverters, аннотированный @Bean, означает использование fastjson для разбора json
@SpringBootApplication
// 加载mybatis配置
@ImportResource({"classpath:config/spring/spring_*.xml"})
public class MybatisSpringBoot {
public static void main(String[] args){
SpringApplication.run(MybatisSpringBoot.class, args);
}
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 格式化时间
SerializeConfig mapping = new SerializeConfig();
mapping.put(Date.class, new SimpleDateFormatSerializer(
"yyyy-MM-dd HH:mm:ss"));
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
fastJsonConfig.setSerializeConfig(mapping);
fastConverter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
}
3. Плагин интегрированного дистрибутива Spring Boot Pagehelper
Вышеуказанные функции реализуют простое приложение mybatis. Но когда дело доходит до запросов к базе данных, использование пейджинга неизбежно. Здесь я рекомендую плагин pagehelper для реализации функции пейджинга.
3.1. pom.xml
Импорт связанных банок
<!-- 分布插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.1</version>
</dependency>
<!-- Spring Boot集成 pagehelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>
3.2 Вспомогательные классы, связанные с пейджинговой связью
MyPage
Упаковка возвращает информацию на стойку регистрации, включая код состояния этого запроса, сообщение об ошибке, общее количество записей и список данных.
public class MyPage<T> {
@JSONField(ordinal = 1)
private Integer code = 200;// 状态码,默认状态
@JSONField(ordinal = 2)
private String message = "";// 提示消息或者错误消息
@JSONField(ordinal = 3)
private String apiId = "";// 请求的唯一标识,预留
@JSONField(ordinal = 4)
private Integer totalCount = 0;//记录总数
@JSONField(ordinal = 5)
private List<T> rows = Collections.emptyList();//本次返回的数据列表
// set/get略
}
IPageHelperPageCallBack
Пользовательский метод обратного вызова на заказ, если пейджинг, вы должны использовать Mapper в этом методе.
Этот интерфейс используется в качестве параметра PageCallBackUtil ниже.
public interface IPageHelperPageCallBack {
<T> List<T> select();
}
PageCallBackUtil
Суть pagehelper заключается в следующем: вызовите метод PageHelper.startPage(pageNum, pageSize,requireTotalCount), чтобы установить начальный адрес записи запроса, а затем сразу же вызовите метод класса сопоставления, чтобы получить возвращаемый список List, используйте ( PageInfo pageInfo = new PageInfo(list)) Оберните PageInfo списка, PageInfo содержит информацию об этом запросе, включая общее количество этого запроса, а затем преобразуйте модель в класс Dto.
/**
* 分页的回调函数
* Created by huangrongyou@yixin.im on 2017/9/6.
*/
public class PageCallBackUtil {
/**
* 封装公共PageHelper的操作
* @param qry
* @param callBack
* @param <T>
* @return
*/
public static<T> MyPage<T> selectRtnPage(AbstractQry qry, IPageHelperPageCallBack callBack){
Assert.notNull(qry, "qry can't be null!");
Assert.notNull(callBack, "callBack cant' be null!");
setPageHelperStartPage(qry);
List<T> list = callBack.select();
// 分页时,实际返回的结果list类型是Page<E>
if(!(list instanceof Page)){
throw new RuntimeException("list must be 'com.github.pagehelper.Page', now is " + list.getClass().getCanonicalName());
}
MyPage<T> myPage = new MyPage<T>();
PageInfo<T> pageInfo = new PageInfo<T>(list);
myPage.setTotalCount((int) pageInfo.getTotal());
myPage.setRows(pageInfo.getList());
return myPage;
}
/**
* 设置PageHelper的startPage
* @param qry
*/
private static void setPageHelperStartPage(AbstractQry qry) {
// 设置分页信息
// pageNum
Integer pageNum = qry.getPageNum();
pageNum = pageNum == null? AbstractQry.DEFAULT_PAGENUM : pageNum;
// pageSize
Integer pageSize = qry.getPageSize();
pageSize = pageSize == null ? AbstractQry.DEFAULT_PAGESIZE : pageSize;
// requireTotalCount
Boolean requireTotalCount = qry.getRequireTotalCount();
requireTotalCount = requireTotalCount == null ? AbstractQry.DEFAULT_REQUIRETOTALCOUNT : requireTotalCount;
PageHelper.startPage(pageNum, pageSize,requireTotalCount);
}
}
Qry
Базовый класс для условий запроса
public interface Qry {
String getId();
void setId(String id);
}
AbstractQry
Информация о номере страницы запроса, который определяет нумерацию страниц.
public class AbstractQry implements Qry {
public static final int DEFAULT_PAGENUM = 1;
public static final int DEFAULT_PAGESIZE = 1;
public static final boolean DEFAULT_REQUIRETOTALCOUNT = false;
private String id;
private Integer pageNum = 1;// 第几页,首页为1
private Integer pageSize = 10;// 每页记录条数
private Boolean requireTotalCount = Boolean.FALSE;// 是否需要记录总数
// set/get略
}
TestQry
Этот класс используется для определения конкретных условий запроса.
public class TestQry extends AbstractQry{
}
3.3 Сервисный уровень: ITestService и TestServiceImpl
ITestService
public interface ITestService {
int deleteByPrimaryKey(Integer id);
int insertSelective(TestModel record);
TestModel selectByPrimaryKey(Integer id);
List<TestModel> selectAll(TestQry qry);
MyPage<TestModel> selectAllWithPage(TestQry qry);
}
TestServiceImpl
selectAllWithPage определяет использование метода
@Service
@Primary
public class TestServiceImpl implements ITestService{
@Autowired
private TestMapper testMapper;
@Override
public int deleteByPrimaryKey(Integer id) {
Assert.notNull(id, "id can't be null!");
return testMapper.deleteByPrimaryKey(id);
}
@Override
public MyPage<TestModel> selectAllWithPage(TestQry qry) {
if(qry == null){
qry = new TestQry();
}
MyPage<TestModel> myPage = PageCallBackUtil.selectRtnPage(qry, new IPageHelperPageCallBack() {
@Override
public List<TestModel> select() {
return testMapper.selectAll();
}
});
return myPage;
}
… 其他方法略
}
3.4 Уровень управления
класс TestCtl
@RestController
@RequestMapping(value = "/simple")
@EnableSwagger2
public class TestCtl {
@Autowired
private ITestService testService;
@RequestMapping(value = "delete-by-primary-key/{id}", method = RequestMethod.GET)
public int deleteByPrimaryKey( @PathVariable("id") Integer id){
// 参数验证略
return testService.deleteByPrimaryKey(id);
}
@RequestMapping(value = "insert-selective", method = RequestMethod.POST)
public int insertSelective(@RequestBody TestDto dto){
// 参数验证略
TestModel record = new TestModel();
record.setId(dto.getId());
record.setAge(dto.getAge());
record.setName(dto.getName());
return testService.insertSelective(record);
}
@RequestMapping(value = "select-by-primary-key/{id}", method = RequestMethod.POST)
public TestDto selectByPrimaryKey(@PathVariable("id") String id){
// 参数验证略
return Model2DtoUtil.model2Dto(testService.selectByPrimaryKey(Integer.parseInt(id)), TestDto.class);
}
@RequestMapping(value = "select-all", method = {RequestMethod.POST })
public List<TestDto> selectAll(@RequestBody TestQry qry){
return Model2DtoUtil.model2Dto(testService.selectAll(qry), TestDto.class);
}
@RequestMapping(value = "select-all-with-page", method = {RequestMethod.POST })
public MyPage<TestDto> selectAllWithPage(@RequestBody TestQry qry){
MyPage<TestDto> page = Model2DtoUtil.model2Dto(testService.selectAllWithPage(qry), TestDto.class);
page.setMessage(getLocalInfo());
return page;
}
private String getLocalInfo(){
StringBuilder sb = new StringBuilder();
try {
InetAddress inetAddress = InetAddress.getLocalHost();
sb.append("server info :")
.append("[ip:").append(inetAddress.getHostAddress()).append(",hostname:").append(inetAddress.getHostName())
.append("]");
} catch (UnknownHostException e) {
e.printStackTrace();
}
return sb.toString();
}
}
3.5 Класс инструмента для преобразования модели в класс dto
Model2DtoUtil
Класс инструмента, инкапсулирует и преобразует модель в класс dto для реализации разделения уровня данных и транспортного уровня.
public class Model2DtoUtil {
/**
* 将 MyPage<model> 修改为 MyPage<Dto>
*
* @param sourcePage
* @param cls
* @param <T>
* @param <K>
* @return
*/
public static <T, K> MyPage<K> model2Dto(MyPage<T> sourcePage, Class<K> cls) {
if(sourcePage == null){
return null;
}
Assert.notNull(cls, "cls can't be null!");
MyPage<K> dstPage = new MyPage<K>();
dstPage.setTotalCount(sourcePage.getTotalCount());
dstPage.setApiId(sourcePage.getApiId());
dstPage.setMessage(sourcePage.getMessage());
dstPage.setCode(sourcePage.getCode());
// list
List<T> sourceList = sourcePage.getRows();
List<K> dstList = new ArrayList<K>();
dealListModel2Dto(sourceList, cls, dstList);
dstPage.setRows(dstList);
return dstPage;
}
private static <T, K> void dealListModel2Dto(List<T> sourceList, Class<K> cls, List<K> dstList) {
for (T source : sourceList) {
try {
K dst = cls.newInstance();
CommonBeanUtils.copyProperties(source, dst);
dstList.add(dst);
} catch (InstantiationException e) {
e.printStackTrace();
throw new BeanCreationException(e.getMessage());
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new BeanCreationException(e.getMessage());
}
}
}
}
CommonBeanUtils
Banutils Tool Class:
public class CommonBeanUtils {
public static void copyProperties(Object source, Object target){
BeanUtils.copyProperties(source, target);
}
}
3.6. application.yml
Связанная конфигурация страницы, другие параметры конфигурации, см.Официальный сайт
Если следующие параметры не настроены, механизм пейджинга не будет работать
# 分页配置: https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md
pagehelper:
helperDialect: oracle
reasonable: true
supportMethodsArguments: true
params: count=countSql
4. Spring Boot объединяет чванство и тестирование
Для удобства тестирования вводим swagger
4.1. pom.xml
<!-- swagger2 -->
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
4.2. @EnableSwagger2
Добавьте @EnableSwagger2 в класс TestCtl
@RestController
@RequestMapping(value = "/simple")
@EnableSwagger2
public class TestCtl {
…
}
4.3 Тестирование
Открыть адрес:http://127.0.0.1:8080/swagger-ui.html
Вы можете увидеть доступные в настоящее время интерфейсы URL
Протестируйте функцию подкачки интерфейса /simple/select-all-with-page и установите функцию запроса
Результат возврата следующий
5. Код
Подробный код выше см.код github, пожалуйста, используйте тег v0.2 как можно чаще, не используйте мастер, потому что мастер всегда меняется, нет гарантии, что код в статье и код на github всегда будут одинаковыми