持续学习&持续更新中…
守破离
【Java从零到架构师第③季】【46】SpringBoot-MyBatisPlus_MyBatisGenerator
MyBatis-Plus
https://www.mybatis-plus.com/
HelloWorld
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
mybatis-plus:
type-aliases-package: programmer.lp.jk.pojo
global-config:
db-config:
id-type: auto # 主键自增
# mapper-locations的默认配置
# mapper-locations: classpath*:/mapper/**/*.xml
条件、分页查询
MyBatisPlusConfig:
@Configuration
@MapperScan("programmer.lp.jk.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
// 当页数超过总页数时,自动跳回到第1页
innerInterceptor.setOverflow(true);
interceptor.addInnerInterceptor(innerInterceptor);
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
Query Model:
@Data
public class PageQuery<T> {
private static final int MIN_SIZE = 1;
private static final int DEFAULT_SIZE = 10;
private long size;
private long no;
private List<T> data;
public long getSize() {
return size < MIN_SIZE ? DEFAULT_SIZE : size;
}
public long getNo() {
return no < MIN_SIZE ? MIN_SIZE : no;
}
}
@EqualsAndHashCode(callSuper = true)
@Data
public class KeywordQuery<T> extends PageQuery<T> {
private String keyword;
}
@EqualsAndHashCode(callSuper = true)
@Data
public class DictTypeQuery extends KeywordQuery<DictType> {
private long total;
private long pages;
}
查询业务:
@Service
@Transactional
public class DictTypeServiceImpl extends ServiceImpl<DictTypeMapper, DictType> implements DictTypeService {
@Autowired
private DictTypeMapper mapper;
@Override
public void list(DictTypeQuery query) {
final String keyword = query.getKeyword();
LambdaQueryWrapper<DictType> wrapper = new LambdaQueryWrapper<>();
// 按照关键字查询
if (!StringUtils.isEmpty(keyword)) {
wrapper.like(DictType::getName, keyword).or()
.like(DictType::getIntro, keyword).or()
.like(DictType::getValue, keyword);
}
// 分页查询
Page<DictType> page = new Page<>(query.getNo(), query.getSize());
mapper.selectPage(page, wrapper);
query.setData(page.getRecords());
query.setPages(page.getPages());
query.setTotal(page.getTotal());
}
}
Controller:
@Controller
@RequestMapping("/dictTypes")
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping("/list")
public String list(DictTypeQuery query, Model model) {
service.list(query);
model.addAttribute("query", query);
return "pages/dictType";
}
}
浏览器客户端使用:
<form id="search-form" action="${ctx}/dictTypes/list">
<input type="hidden" name="no" value="${query.no}">
<input type="hidden" name="size" value="${query.size}">
<input type="text" name="keyword" value="${query.keyword!}"
placeholder="名称、值、简介"/>
</form>
<#list query.data as item>
<tr>
<td>${item.name}</td>
<td>${item.value}</td>
<td>${item.intro!}</td>
</tr>
</#list>
<div>
<label>
显示
<select name="sizex" class="form-control form-control-sm">
<option value="5">5</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
条
</label>
,共${query.pages}页,共${query.total}条
<#-- ,共${10}页,共${10}条-->
</div>
<script>
// 搜索表单
const $searchForm = $('#search-form')
// 搜索表单 - size
const $hiddenSize = $searchForm.find('[name=size]')
// 搜索表单 - no
const $hiddenPage = $searchForm.find('[name=no]')
// 搜索表单 - 搜索按钮
const $searchBtn = $searchForm.find('#search-btn')
$searchBtn.click(() => {
$hiddenPage.val(1)
$searchForm.submit()
})
// 监听size改变—写法1
const $selectSize = $('[name=sizex]')
$selectSize.change(() => {
window.location.href = '${ctx}/dictTypes/list?size=' + $selectSize.val()
+ "&keyword=" + $('[name=keyword]').val()
})
$selectSize.val(${query.size})
// 监听size改变—写法2
const $selectSize = $('[name=sizex]')
$selectSize.change(() => {
$hiddenSize.val($selectSize.val())
$hiddenPage.val(1)
$searchForm.submit()
})
$selectSize.val(${query.size})
</script>
前端处理分页信息:
<!-- Bootstrap Core JavaScript -->
<script src="${ctx}/assets/libs/popper/popper.min.js"></script>
<script src="${ctx}/assets/libs/bootstrap/bootstrap.min.js"></script>
<!-- Bootstrap 分页插件 -->
<script src="${ctx}/assets/libs/bootstrap/bootstrap-paginator.min.js"></script>
<ul class="pagination">
</ul>
// 处理分页信息
$('ul.pagination').bootstrapPaginator({
bootstrapMajorVersion: 3,
currentPage: ${query.no},
totalPages: ${query.pages},
onPageClicked: (e1, e2, type, page) => {
// 如果是当前页,直接返回
if (page === ${query.no}) return
$hiddenPage.val(page)
$searchForm.submit()
}
})
po自动生成—MyBatisGenerator
http://mybatis.org/generator/
注意:建议将MyBatis-Generator自动生成目录设置为src/test目录下,而不是src/main目录下,防止自己写好的src/main下的东西被覆盖。
注意:把自动生成的、不需要的Mapper和XML删掉,只需要保留po即可,因为数据库SQL语句还得是自己写的比较放心。
pom.xml:
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<configurationFile>src/test/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
generatorConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="default" targetRuntime="MyBatis3Simple">
<commentGenerator>
<!-- 去除所有注释 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- 数据库连接 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/jiakao?serverTimezone=Asia/Shanghai"
userId="root" password="root"/>
<!-- Model的位置 -->
<javaModelGenerator targetPackage="programmer.lp.jk.pojo.po"
targetProject="src/test/java"/>
<!-- XML的位置 -->
<sqlMapGenerator targetPackage="programmer.lp.jk.mapper"
targetProject="src/test/resources"/>
<!-- Mapper的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="programmer.lp.jk.mapper"
targetProject="src/test/java"/>
<!-- %代表所有表 -->
<table tableName="%"/>
<!-- <table tableName="dict_type"/> -->
</context>
</generatorConfiguration>
逻辑删除
-
物理删除:真正从数据库中删除了,永久消失。
-
逻辑删除(假删除、软删除):数据还在数据库中,只是对用户来说,数据被删掉了。
-
逻辑删除的实现:在需要实现逻辑删除的表中增加一个字段来标识数据是否被删除。
数据库建表语句:
# db: logic_delete
create table user
(
id int unsigned auto_increment
primary key,
name varchar(15) default '' not null,
deleted tinyint unsigned default 0 not null comment '1是被删除,0是未删除',
constraint user_name_uindex
unique (name)
);
依赖:
<?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>
<groupId>org.example</groupId>
<artifactId>TestLogicDelete</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<druid.version>1.2.1</druid.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<springfox.version>3.0.0</springfox.version>
<swagger-models.version>1.6.2</swagger-models.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 接口文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger-models.version}</version>
</dependency>
<!-- debug阶段 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>logic_delete</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SpringBoot配置:
server:
port: 9999
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/logic_delete?serverTimezone=GMT%2B8
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jackson:
default-property-inclusion: non_null
mybatis-plus:
global-config:
db-config:
id-type: auto
# 全局配置
# 逻辑删除字段
logic-delete-field: deleted
# 代表已删除的字段值
logic-delete-value: 1
# 代表未删除的字段值
logic-not-delete-value: 0
logging:
level:
com.mj: debug
PO:
@Data
@ApiModel("用户")
public class User {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("姓名")
private String name;
@ApiModelProperty(hidden = true)
// 逻辑删除的局部配置
// @TableLogic(value = "0", delval = "1")
// 不要查询这个字段
@TableField(select = false)
private Short deleted;
}
Mapper(Dao):
public interface UserMapper extends BaseMapper<User> {
}
Service:
public interface UserService extends IService<User> {
}
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
Controller:
@RestController
@RequestMapping("/users")
@Api(tags = "用户")
public class UserController {
@Autowired
private UserService service;
@GetMapping("/list")
@ApiOperation("查询所有")
public DataJsonVo<List<User>> list() {
return new DataJsonVo<>(service.list());
}
@PostMapping("/save")
@ApiOperation("添加或更新")
public JsonVo save(User user) {
JsonVo jsonVo = new JsonVo();
jsonVo.setMsg(service.saveOrUpdate(user) ? "保存成功" : "保存失败");
return jsonVo;
}
@PostMapping("/remove")
@ApiOperation("删除")
public JsonVo remove(Long id) {
JsonVo jsonVo = new JsonVo();
jsonVo.setMsg(service.removeById(id) ? "删除成功" : "删除失败");
return jsonVo;
}
@PostMapping("/json")
@ApiOperation("测试")
public DataJsonVo<User> json(@RequestBody User user) {
return new DataJsonVo<>(user);
}
}
注意
- Mapper(以前的DAO)更接近数据库,因此Mapper中方法的命名更接近数据库的SQL语句关键字:
- mapper.insert();
- mapper.delete();
- mapper.update();
- mapper.select();
- Service更接近业务,因此Service的方法命名更贴近业务:
- service.save();
- service.remove();
- service.update();
- service.list();
- 以后自己写项目,也推荐使用这种写法。
参考
小码哥-李明杰: Java从0到架构师③进阶互联网架构师.
本文完,感谢您的关注支持!