Mybatis逆向工程和新版本MybatisPlus3.4逆向工程的使用
Mybatis和MybatisPlus3.4的使用
1 RESTFUL
前后端风你开发的项目中,前后端之间是接口进行请求和响应,后端向前端提供请求时就要对外暴露一个URL;URL的设计不能是随意的,需要遵从一定的设计规范----RESTFUL。
RESTful是一种web api的标准,也就是一种url设计风格/规范
每个URL请求路径代表服务器上的唯一资源。
传统的URL设计:
http://localhost:8080/goods/delete?goodsId=1 商品1
http://localhost:8080/goods/delete?goodsId=2 商品2
RESTful设计:
示例:
@DeleteMapping("/delete/{id}")
public ResultVO deleteGoods(@PathVariable("id") Integer goodsId) {
return new ResultVO(10003,"请求成功",goodsId);
}
使用不同的请求方式表示不同的操作,保证唯一URL对应唯一资源
- @GetMapping 查询
- @PutMapping 修改
- @PostMapping 添加
- @DeleteMapping 删除
根据id删除一个商品:
//http://localhost:8080/goods/1 [delete]
@DeleteMapping("/{id}")
public ResultVO deleteGoods(@PathVariable("id") Integer goodsId) {
return new ResultVO(10003,"请求成功",goodsId);
}
根据id查询一个商品:
//http://localhost:8080/goods/1 [get]
@GetMapping("/{id}")
public ResultVO getGoods(@PathVariable("id") Integer goodsId) {
return new ResultVO(10003,"请求成功",goodsId);
}
接口响应的资源表现形式使用JSON
- 可以在控制类或者需要的接口方法上添加
@ResponseBody
注解讲返回的对象格式化为JSON - 也可以在控制类上使用
@RestController
声明控制器
- 可以在控制类或者需要的接口方法上添加
前端(Android\ios\PC)通过无状态的HTTP协议与后端接口交互
2 逆向工程
mybatis官方提供了一种根据数据库表生成mybatis执行代码的工具,这个工具就是一个逆向工程
逆向工程:针对数据库单表—->生成代码(
mapper.xml、mapper.java、pojo。。
)这里我最开始使用的是
mybatis
逆向工程,可以生成mapper.xml、mapper.java、pojo
后面我想尝试使用
MP
做逆向工程,所以使用了两种方式,从简洁上来看,还是MP
比较方便一点.
2.1 tkMybatis逆向工程
逆向工程并且自动添加swagger注解,注意需要在beans的pom也要添加swagger依赖
2.1.1 导入依赖
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
- 修改启动类注解
@MapperScan
的包,因为这个注解还有个包是org.mybatis.spring.annotation.MapperScan;
2.1.2 添加插件
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client --><!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>com.github.misterchangray.mybatis.generator.plugins</groupId>
<artifactId>myBatisGeneratorPlugins</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
同步成功后会出现
2.1.3 添加配置文件
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>
<!--导入属性配置-->
<!-- <properties resource="db.properties"></properties>-->
<!--指定特定数据库的jdbc驱动jar包的位置-->
<!--<classPathEntry location="${jdbc.location}"/>-->
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!--结合通用Mapper插件 指定生成 Mapper 的继承模板-->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.mymall.general.GeneralDao"/>
</plugin>
<!--pojo实现序列化接口-->
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<!--pojo类中增加toString方法-->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
<!--覆盖生成XML文件 每次执行,把以前的mapper.xml覆盖而不是合并-->
<!-- <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin" />-->
<!-- 自动为entity生成swagger2文档-->
<plugin type="mybatis.generator.plugins.GeneratorSwagger2Doc">
<property name="apiModelAnnotationPackage" value="io.swagger.annotations.ApiModel"/>
<property name="apiModelPropertyAnnotationPackage" value="io.swagger.annotations.ApiModelProperty"/>
</plugin>
<!--注意, plugin 需要写在commentGenerator上面-->
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="false" />
</commentGenerator>
<!--jdbc的数据库连接 -->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mymall?useSSL=false&serverTimezone=GMT%2b8"
userId="root"
password="123456">
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
<!-- 生成 JavaBean 对象重写 toString方法 -->
<!-- <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />-->
<!-- 生成 JavaBean 对象继承 Serializable 类 -->
<!-- <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />-->
<!-- 生成 JavaBean 对象重写 equals 和 hashCode 方法 -->
<!-- <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" /> -->
<!-- 对应生成的pojo所在包 -->
<javaModelGenerator targetPackage="com.mymall.entity" targetProject="src/main/java">
<!-- 是否对model添加 构造函数 -->
<property name="constructorBased" value="true"/>
</javaModelGenerator>
<!-- 对应生成的mapper.xml所在目录 -->
<sqlMapGenerator targetPackage="/" targetProject="src/main/resources/mappers"/>
<!-- 配置mapper对应的java映射 -->
<javaClientGenerator targetPackage="com.mymall.dao" targetProject="src/main/java"
type="XMLMAPPER"/>
<!-- 配置需要指定生成的数据库和表,%代表所有表,开发中不要这么做,可能会生成很多无用的表-->
<!-- <table tableName="%"/>-->
<table tableName="category" />
<table tableName="index_img" />
<table tableName="order_item" />
<table tableName="orders" />
<table tableName="product" />
<table tableName="product_comments" />
<table tableName="product_img" />
<table tableName="product_params" />
<table tableName="product_sku" />
<table tableName="shopping_cart" />
<table tableName="user_addr" />
<table tableName="user_login_history" />
<table tableName="users" />
<!-- <table schema="" tableName="oauth_access_token" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<domainObjectRenamingRule searchString="^Tb" replaceString="" />
</table>
<table schema="" tableName="oauth_approvals" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
<domainObjectRenamingRule searchString="^Tb" replaceString="" />
</table>-->
<!--将你要生成的表尽数罗列 -->
</context>
</generatorConfiguration>
调整配置信息
创建
GeneralDao
接口package com.mymall.general; import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper; /**
* @author 18230
* @version 1.0.0
* @ClassName GereralDao.java
* @Description
* @createTime 2021年09月25日 15:50:00
*/
public interface GeneralDao<T> extends Mapper<T>, MySqlMapper<T> {
}<!--配置GeneralDao-->
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="com.mymall.general.GneralDao"/>
</plugin>
配置数据库连接信息
<!--jdbc的数据库连接,url后面的配置不要修改 -->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mymall?useSSL=false&serverTimezone=GMT%2b8"
userId="root"
password="123456">
</jdbcConnection>
配置实体类路径
<!-- 对应生成的pojo所在包 -->
<javaModelGenerator targetPackage="com.mymall.entity" targetProject="src/main/java">
<!-- 是否对model添加 构造函数 -->
<property name="constructorBased" value="true"/>
</javaModelGenerator>
配置
mapper.xml
目录<!-- 对应生成的mapper.xml所在目录 -->
<sqlMapGenerator targetPackage="/" targetProject="src/main/resources/mappers"/>
配置dao的目录和生成表的名称
<!-- 配置mapper对应的java映射 -->
<javaClientGenerator targetPackage="com.mymall.dao" targetProject="src/main/java"
type="XMLMAPPER"/>
<!-- 配置需要指定生成的数据库和表,%代表所有表,开发中不要这么做,可能会生成很多无用的表-->
<!-- <table tableName="%"/>-->
<table tableName="category" />
<table tableName="index_img" />
<table tableName="order_item" />
<table tableName="orders" />
<table tableName="product" />
<table tableName="product_comments" />
<table tableName="product_img" />
<table tableName="product_params" />
<table tableName="product_sku" />
<table tableName="shopping_cart" />
<table tableName="user_addr" />
<table tableName="user_login_history" />
<table tableName="users" />
将配置文件
generatorConfig.xml
配置到逆向工程的maven插件中<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
</configuration>
2.1.4 执行逆向
2.1.5 生成成功
将生成的entity剪切到beans子项目中
将生成的entity移到到beans目录之后会出现报错,因为使用了tkMapper的注解,所以需要将tkMapper的依赖直接剪切到beans中,mapper项目引用了beans的依赖所以就可以不再pom中声明tkMapper的依赖了.
2.1.6 使用tkMybatis
package com.qc.dao;
import com.qc.TkMapperDemoApplication;
import com.qc.entity.Category;
import org.apache.ibatis.session.RowBounds;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import tk.mybatis.mapper.entity.Example;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author 18230
* @version 1.0.0
* @ClassName CategoryDaoTest.java
* @Description
* @createTime 2021年09月05日 20:40:00
*/
@SpringBootTest(classes = TkMapperDemoApplication.class)
@RunWith(SpringRunner.class)
public class CategoryDaoTest {
@Autowired
private CategoryDao categoryDao;
@Test
public void testInsert() {
Category category = new Category(0,"test02",1,0,"01.png","hehe","111.jpg","black");
//将数据插入表中,返回结果,1,成功
//int i = categoryDao.insert(category);
//将数据插入后将生成的主键返回给对象保存
int i = categoryDao.insertUseGeneratedKeys(category);
System.out.println(category.getCategoryId());
assertEquals(1,i);
}
@Test
public void testUpdate() {
Category category = new Category(13,"test03",1,0,"01.png","hehe","111.jpg","black");
//根据主键修改数据
int i = categoryDao.updateByPrimaryKey(category);
assertEquals(1,i);
}
@Test
public void testDelete() {
//根据主键删除数据
int i = categoryDao.deleteByPrimaryKey(13);
assertEquals(1,i);
}
@Test
public void testSelect1() {
//查询所有
List<Category> categories = categoryDao.selectAll();
for (Category category : categories) {
System.out.println(category);
}
}
@Test
public void testSelect2() {
// 根据主键查询
Category category = categoryDao.selectByPrimaryKey(13);
System.out.println(category);
}
@Test
public void testSelect3() {
// 条件查询
// 1.创建一个example封装,类别category查询条件
Example example = new Example(Category.class);
Example.Criteria criteria = example.createCriteria();
//criteria.andEqualTo("categoryLevel", 1);
criteria.andLike("categoryName", "%干%");
List<Category> categories = categoryDao.selectByExample(example);
for (Category category : categories) {
System.out.println(category);
}
}
@Test
public void testSelect4() {
// 分页查询
int pageNum = 2;
int pageSize= 5;
int start =(pageNum-1)*pageSize;
RowBounds rowBounds = new RowBounds(start,pageSize);
List<Category> categories = categoryDao.selectByRowBounds(new Category(), rowBounds);
for (Category category : categories) {
System.out.println(category);
}
// 查询总记录数
int i = categoryDao.selectCount(new Category());
System.out.println(i);
}
@Test
public void testSelect5() {
//准备条件
Example example = new Example(Category.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("categoryLevel", 1);
//准备分页条件
int pageNum = 1;
int pageSize= 3;
int start =(pageNum-1)*pageSize;
RowBounds rowBounds = new RowBounds(start,pageSize);
//条件分页显示
List<Category> categories = categoryDao.selectByExampleAndRowBounds(example, rowBounds);
for (Category category : categories) {
System.out.println(category);
}
// 查询满足条件的记录数
int i = categoryDao.selectCountByExample(example);
System.out.println(i);
}
}
2.2 MybatisPlus使用教程
2.2.1 在mapper子项目中添加依赖
在创建mapper子项目的时候已经添加了
mybatis-plus-boot-starter
,所以这里不需要额外添加依赖
2.2.2 配置application.yml
当时用
mysql8
时,需要mysql的驱动同:com.mysql.cj.jdbc.Driver
,同时需要增加时区设置:serverTimezone=GMT%2b8
datasource:
druid:
#数据库连接驱动
driver-class-name: com.mysql.cj.jdbc.Driver
#数据库连接地址
url: jdbc:mysql://localhost:3306/mymall?serverTimezone=GMT%2b8&useSSL=false&allowPublicKeyRetrieval=true
#数据库连接用户名和密码
username: root
password: 123456
2.2.3 配置@MapperScan
package com.mymall;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.oas.annotations.EnableOpenApi;
@SpringBootApplication
@MapperScan("com.mymall.dao")
@EnableOpenApi
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
2.2.4 编写实体类Users
package com.mymall.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
Integer userId;
String username;
String password;
}
2.2.5 编写Mapper.java
继承模版BaseMapper
package com.mymall.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mymall.entity.Users;
import org.springframework.stereotype.Repository;
@Repository
public interface UsersMapper extends BaseMapper<Users> {
}
2.2.6 测试是否能够正常调用MP
package com.mymall;
import com.mymall.entity.Users;
import com.mymall.dao.UsersMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class ApiApplicationTests {
@Autowired
private UsersMapper usersMapper;
@Test
void contextLoads() {
List<Users> users = usersMapper.selectList(null);
for (Users user : users) {
System.out.println(user);
}
System.out.println(users);
}
}
测试成功!
2.2.7 配置日志
mybatis-plus:
configuration:
# 配置日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
显示效果
2.2.8 CRUD扩展
2.2.8.1 数据库插入的id的默认值:全局的唯一id
雪花算法:snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake。雪花算法支持的TPS可以达到419万左右(2^22*1000)。
注意:
使用雪花或者uuid的时候在数据中的字段不能是自增,不然不会生效.
设置要求:
主键需要设置成自增
实体类字段上添加
MP
注解@TableId(type = IdType.ASSIGN_ID)
IdType
类型源码package com.baomidou.mybatisplus.annotation; import lombok.Getter; /**
* 生成ID类型枚举类
*
* @author hubin
* @since 2015-11-10
*/
@Getter
public enum IdType {
/**
* 数据库ID自增
* <p>该类型请确保数据库设置了 ID自增 否则无效</p>
*/
AUTO(0),
/**
* 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
*/
NONE(1),
/**
* 用户输入ID
* <p>该类型可以通过自己注册自动填充插件进行填充</p>
*/
INPUT(2), /* 以下2种类型、只有当插入对象ID 为空,才自动填充。 */
/**
* 分配ID (主键类型为number或string),
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)
*
* @since 3.3.0
*/
ASSIGN_ID(3),
/**
* 分配UUID (主键类型为 string)
* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))
*/
ASSIGN_UUID(4), private final int key; IdType(int key) {
this.key = key;
}
}
Insert自动生成id
@Test//测试插入
public void insertTest(){
User user = new User();
user.setName("wsk");
user.setEmail("2803708553@qq.com");
Integer result = userMapper.insert(user); //会帮我们自动生成id
System.out.println(result); //受影响的行数
System.out.println(user); //通过日志发现id会自动回填
}
2.2.8.2 自动填充
创建时间、更改时间! 这些操作一般都是自动化完成,我们不希望手动更新
阿里巴巴开发手册︰几乎所有的表都要配置 gmt_create、gmt_modified !而且需要自动化
方式一:数据库级别(工作中不允许修改数据库级别)
1、在表中增加字段:create_time,update_time
-- auto-generated definition
create table mp_user
(
user_id bigint auto_increment comment '用户id' primary key,
username varchar(32) not null comment '用户名',
password varchar(32) not null comment '密码',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
update_time datetime default CURRENT_TIMESTAMP not null comment '更新时间'
);
2、更新实体类中的成员变量
package com.mymall.entity; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor; import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MpUser {
@TableId(type = IdType.ASSIGN_ID)
Long userId;
String username;
String password;
Date createTime;
Date updateTime;
}3、再次运行之前插入或更新一条数据的代码显示成功自动插入时间
方式二:代码级别
1、删除数据库的默认值,更新操作!
2、实体类字段属性上需要增加注解
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MpUser {
@TableId(type = IdType.ASSIGN_ID)
Long userId;
String username;
String password;
//插入时注解
@TableField(fill = FieldFill.INSERT)
Date createTime;
//更新时注解
@TableField(fill = FieldFill.UPDATE)
Date updateTime;
}
注解详情
package com.baomidou.mybatisplus.annotation;
/**
* 字段填充策略枚举类
* <p>
* 判断注入的 insert 和 update 的 sql 脚本是否在对应情况下忽略掉字段的 if 标签生成
* <if test="...">......</if>
* 判断优先级比 {@link FieldStrategy} 高
* </p>
* @author hubin
* @since 2017-06-27
*/
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}3、编写处理器来处理这个注解即可!
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler { @Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
// 或者
this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
} @Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
// 或者
this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
// 或者
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
}
注意事项:
- 填充原理是直接给
entity
的属性设置值!!! - 注解则是指定该属性在对应情况下必有值,如果无值则入库会是
null
MetaObjectHandler
提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null
则不填充- 字段必须声明
TableField
注解,属性fill
选择对应策略,该声明告知Mybatis-Plus
需要预留注入SQL
字段 - 填充处理器
MyMetaObjectHandler
在 Spring Boot 中需要声明@Component
或@Bean
注入 - 要想根据注解
FieldFill.xxx
和字段名
以及字段类型
来区分必须使用父类的strictInsertFill
或者strictUpdateFill
方法 - 不需要根据任何来区分可以使用父类的
fillStrategy
方法
4.测试运行插入或更新语句
- 填充原理是直接给
2.2.9 乐观锁
在面试过程中经常被问到乐观锁/悲观锁,这个其实很简单
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不上锁!如果出现了问题,再次更新值测试
悲观锁:顾名思义十分悲观,他总是认为出现问题,无论干什么都会上锁!再去操作!
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时,set version = newVersion where version = oldVersion
如果version不对,就更新失败
乐观锁:先查询,获得版本号
-- A
update user set name = "wsk",version = version+1
where id = 1 and version = 1
-- B (B线程抢先完成,此时version=2,会导致A线程修改失败!)
update user set name = "wsk",version = version+1
where id = 1 and version = 1
更新数据库,添加
version
字段-- auto-generated definition
create table mp_user
(
user_id bigint auto_increment comment '用户id' primary key,
username varchar(32) not null comment '用户名',
password varchar(32) not null comment '密码',
version int null comment '乐观锁',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
update_time datetime default CURRENT_TIMESTAMP not null comment '更新时间'
);
实体类添加加对应的字段和注解
@Version
Integer version;
注册组件
//扫描mapper文件夹
@MapperScan("com.mymall.dao")//交给mybatis做的,可以让这个配置类做扫描
@EnableTransactionManagement//自动管理事务
@Configuration//配置类
public class MyBatisPlusConfig {
//注册乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
测试
测试乐观锁成功
@Test
void testOptimisticLocker1() {
//1、查询用户信息
MpUser mpUser = mpUserMapper.selectById(1L);
//2、修改用户信息
mpUser.setUsername("qq");
//3、执行更新操作
int update = mpUserMapper.updateById(mpUser);
System.out.println(mpUser);
}
模拟多线程测试乐观锁插入数据失败
@Test
void testOptimisticLocker2() {
//线程1
MpUser mpUser = mpUserMapper.selectById(1L);
mpUser.setUsername("aa");
mpUser.setPassword("666666");
//模拟另外一个线程执行了插队操作
MpUser mpUser1 = mpUserMapper.selectById(1L);
mpUser1.setUsername("bb");
mpUser1.setPassword("12344444");
mpUserMapper.updateById(mpUser1);
//自旋锁来多次尝试提交!
mpUserMapper.updateById(mpUser);//如果没有乐观锁就会覆盖插队线程的值
}
2.2.10 查询
通过id查询单个用户
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
通过id查询多个用户
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
users.forEach(System.out::println);
}
条件查询 通过map封装
@Test
public void testMap(){
HashMap<String, Object> map = new HashMap<>();
//自定义要查询的
map.put("name","www");
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页查询
分页在网站的使用十分之多!
1、原始的limit分页
2、PageHelper第三方插件
3、MybatisPlus其实也内置了分页插件!
使用步骤:
配置拦截器组件
package com.mymall.config; import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("com.mymall.dao")
@EnableTransactionManagement
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加乐观锁
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
//新的分页插件,一缓和二缓遵循mybatis的规则,
//需要设置 MybatisConfiguration#useDeprecatedExecutor = false
// 避免缓存出现问题(该属性会在旧插件移除后一同移除)
//也可以不传DbType.MYSQL,为空时会通过URL判断数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}测试分页查询
@Test
public void testPage(){
//参数一current:当前页 参数二size:页面大小
//使用了分页插件之后,所有的分页操作都变得简单了
Page<User> page = new Page<>(2,5);
//第二个参数是高级查询,暂设为null
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
System.out.println("总页数==>"+page.getTotal());
}
2.2.11 删除
基本删除操作
@Test
public void testDeleteById(){
userMapper.deleteById(1);
}
@Test
public void testDeleteBatchIds(){
userMapper.deleteBatchIds(Arrays.asList(1,2));
}
@Test
public void testD(){
HashMap<String, Object> map = new HashMap<>();
map.put("age","18");
map.put("name","lol");
userMapper.deleteByMap(map);
}
逻辑删除
物理删除:从数据库中直接删除
逻辑删除:在数据库中没有被删除,而是通过一个变量来使他失效! deleted=0 ==> deleted=1
管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
实现步骤:
数据库中添加
deleted
字段deleted int default 0 null comment '逻辑删除:0显示1删除',
添加配置
application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
在实体类上添加
deleted
字段并加上@TableLogic
注解(上面配置了application.yml
后可以不添加注解,但是deleted
字段一定要添加)@TableLogic
private Integer deleted;
测试删除一条数据后查询是否数据还存在,测试结果,只是
deleted
设置为了1@Test
void testLogicDelete() {
//删除id为2的数据
int i = mpUserMapper.deleteById(2);
MpUser mpUser = mpUserMapper.selectById(2);
System.out.println(mpUser);
}
查看数据库,发现记录还在,deleted变为1
再次测试查询被删除的用户,发现查询为空
2.2.12 性能分析插件(新版本已经移除)
我们在平时的开发中,会遇到一些慢Sql。测试、druid···
MybatisPlus也提供了性能分析插件,如果超过这个时间就停止运行!
2.2.13 条件构造器wrapper
警告:
不支持以及不赞成在 RPC 调用中把 Wrapper 进行传输
- wrapper 很重
- 传输 wrapper 可以类比为你的 controller 用 map 接收值(开发一时爽,维护火葬场)
- 正确的 RPC 调用姿势是写一个 DTO 进行传输,被调用方再根据 DTO 执行相应的操作
常用api请到网页中查看条件构造器 | MyBatis-Plus (baomidou.com)
使用示例
查询不为空且比某值大的数据
@Test
void testWrapper() {
//参数是一个wrapper ,条件构造器,和刚才的map对比学习!
//查询username不为空,password不为空,user_id大于18的用户
//注意这里的column参数一定是字段名,不是实体类中的成员变量
QueryWrapper<MpUser> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("username")
.isNotNull("password")
.ge("user_id",2);
List<MpUser> userList = mpUserMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
查询某字段值为确定值的数据,返回结果有一个和多个的可能
@Test
public void testWrapper2() {
//查询name=wsk的用户
QueryWrapper<MpUser> wrapper = new QueryWrapper<>();
wrapper.eq("username","MPInsertTest1");
//查询一个数据selectOne,若查询出多个会报错
//Expected one result (or null) to be returned by selectOne(), but found: *
//若出现多个结果使用list或map
MpUser mpUser = mpUserMapper.selectOne(wrapper);//查询一个数据,若出现多个结果使用list或map
System.out.println(mpUser);
List<MpUser> mpUsers = mpUserMapper.selectList(wrapper);
}
查询字段值范围在某个区间的数据
@Test
public void testWrapper3() {
//查询age在10-20之间的用户的用户数量
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 10, 20);//区间
Integer count = userMapper.selectCount(wrapper);//输出查询的数量selectCount
System.out.println(count);
}
模糊查询,
notLike,likeRight,likeLeft
@Test
public void testWrapper4() {
//模糊查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("name","s")
.likeRight("email","t");//qq% 左和右?
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
子查询
@Test
public void testWrapper5() {
// SELECT id,name,age,email,version,deleted,create_time,update_time
//FROM user
//WHERE deleted=0 AND id IN
//(select id from user where id<5)
QueryWrapper<User> wrapper = new QueryWrapper<>();
//id 在子查询中查出来
wrapper.inSql("id","select id from user where id<5");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
通过id进行排序
@Test
public void testWrapper6() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//通过id进行降序排序
wrapper.orderByDesc("id");
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
2.2.14 代码生成器,MP
逆向工程
添加依赖
MyBatis-Plus 从
3.0.3
之后移除了代码生成器与模板引擎的默认依赖,需要手动添加生成器和模版引擎依赖这里尝试使用3.5.1版本(以下教程仅适用 3.5.1 以上版本,对历史版本的不兼容)
// 注意!!当前包未传递依赖 mp 包,需要自己引入
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>Latest Version</version>
</dependency>
添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。这里按照官方文档选择了
freemarker
Velocity(默认):
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>latest-velocity-version</version>
</dependency> Freemarker:
<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency> Beetl:
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>latest-beetl-version</version>
</dependency>
在
mpper
子项目中创建代码生成器配置类package com.mymall; import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.Collections; /**
* @author 18230
* @version 1.0.0
* @ClassName CodeGenerator.java
* @Description
* @createTime 2021年09月30日 22:37:00
*/
public class CodeGenerator {
public static void main(String[] args) {
/*
* url jdbc路径 jdbc:mysql://127.0.0.1:3306/mybatis-plus
* username 数据库账号 root
* password 数据库密码 123456
*/
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mymall?serverTimezone=GMT%2b8&useSSL=false&allowPublicKeyRetrieval=true",
"root", "123456")
.globalConfig(builder -> {
builder.author("QC") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
//禁止打开目录
.disableOpenDir()
// 指定输出目录,这里使用绝对路径
.outputDir("F:\\JavaProjcet\\MyMallPractice2\\mapper\\src\\main\\java");
//如果使用/src/main/java会在根目录下创建文件,也就是没成功
//源码:this.outputDir = System.getProperty("os.name").toLowerCase().contains("windows") ? "D://" : "/tmp";
//System.getProperty("os.name")获取操作系统名称,如果名称包含windows就"D://"否则就"/tmp"
//判断是windows系统,否则就是linux系统,所以还是只能使用绝对地址
//.outputDir("classpath:/src/main/java");
})
.packageConfig(builder -> {
builder.parent("com.mymall") // 设置父包名
//.moduleName("mapper") // 设置父包模块名,也就是在父包名中添加一个包
//但是这里设置相对路径成功了一次,但后面还是生成了根目录,所以还是使用绝对路径
//.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "/src/main/resources/mapper")); // 设置mapperXml生成路径
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "F:\\JavaProjcet\\MyMallPractice2\\mapper\\src\\main\\resources\\mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("users") // 设置需要生成的表名
.addInclude("category")
.addInclude("index_img")
.addInclude("mp_user")
.addInclude("product")
.addInclude("product_sku")
.addInclude("product_img")
.addInclude("orders")
.addInclude("product_comments")
.addInclude("product_params")
//.setEntityLombokModel(true) //开启lombook支持,新版本已经没有了
.addTablePrefix("mp_"); // 设置过滤表前缀,也就是将前缀去掉,只保留后面对的内容作为实体名
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}生成成功
3 常见问题解决办法
3.1 数据库url中的SSL问题
解决办法:将gerneratorConfig.xml
中的url改成useSSL=false
connectionURL="jdbc:mysql://localhost:3306/mymall?useSSL=false&serverTimezone=GMT%2b8"&useUnicode=true&characterEncoding=utf-8
3.2 插件没找到
解决:调整mybatis-generator-maven-plugin
版本
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
3.3 将数据库中的其他表都生成了
解决:在gerneratorConfig.xml
只添加需要生成的表名
<table tableName="category" />
3.4 users表生成了其他表
解决办法:
修改我的表名称
想办法不让MySql保留表影响到我的逆向工程
要保证只生成自己指定的 database 中的 User 表,首先要在 中的 connectionURL 中指定数据库的实例名,然后在 中添加相关配置信息,即 ,即可保证只生成自己需要的 User 类。
配置如下:<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/xxx"
userId="xxx"
password="xxx">
<property name="nullCatalogMeansCurrent" value="true"/>
</jdbcConnection>
Mybatis逆向工程和新版本MybatisPlus3.4逆向工程的使用的更多相关文章
- mybatis自动生成代码工具(逆向工程)
MyBatis自动生成实体类(逆向工程) MyBatis属于一种半自动的ORM框架,它需要我们自己编写sql语句和映射文件,但是编写映射文件和sql语句很容易出错,所以mybatis官方提供了Gene ...
- mybatis 详解(十)------ 逆向工程
通过前面的学习,在实际开发中,我们基本上能对mybatis应用自如了,但是我们发现了一个问题,所有操作都是围绕着po类,xxxMapper.xml文件,xxxMapper接口等文件来进行的.如果实际开 ...
- mybatis 映射生成mapper和pojo ---逆向工程的使用过程
使用逆向工程生成mapper和pojo 2. 新建一个项目,随便叫什么 3.导入mybatis-generator-core .mybatis.mybatis-spring.log4j等jar 4.在 ...
- Mybatis在IDEA中使用generator逆向工程生成pojo,mapper
使用mybatis可以逆向生成pojo和mapper文件有很多种方式,我以前用的是mybtais自带的generator包来生成,连接如下:mybatis自己生成pojo 今天我用了IDEA上使用ma ...
- spring boot 整合mybatis 的xml版本【包括逆向工程以及分页插件】
逆向工程很方便,可以直接根据数据库和配置文件生成pojo,mapper接口和相应的映射文件. xml版本和全注解版本其实差不多,大部分情况下,都会保留xml文件方便其他人去扩展新的dml方法. 文章旨 ...
- Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
MyBatis 今天大年初一,你在学习!不学习做什么,斗地主...人都凑不齐.学习吧,学习使我快乐!除了诗和远方还有责任,我也想担当,我也想负责,可臣妾做不到啊,怎么办?你说怎么办,为啥人家能做到你做 ...
- MyBatis学习--逆向工程
简介 mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper.java,mapper.xml.po..).企业实际 ...
- 十 mybatis逆向工程
1 逆向工程 1.1 什么是逆向工程 mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper. ...
- mybatis 的逆向工程
1 逆向工程 1.1 什么是逆向工程 mybaits需要程序员自己编写sql语句,mybatis官方提供逆向工程 可以针对单表自动生成mybatis执行所需要的代码(mapper.Java ...
随机推荐
- git新建分支及提交代码到分支
二.创建分支并提交代码到分支 上述添加成员的方式非常简单,但是如果说每一个小组成员都可以对仓库push内容,就涉及到一个代码的安全和冲突问题了,当多个成员同时在线编辑时容易出现冲突,假设A的代码是有问 ...
- 网络视频m3u8解密及ts文件合并
网络视频m3u8解密及ts文件合并 参考了两篇博客: https://blog.csdn.net/weixin_41624645/article/details/95939510 https://bl ...
- 转:自增(自减)在Java与C中的区别
转自:http://seiyatime.blog.sohu.com/84358295.html 话说昨日面试,在笔试的25个选择题中,涉及自增自减不止一两题,以前在开发过程中并没太在意这方面的问题,也 ...
- Linux centos 安装 JDK 8
一.下载JDK 官方下载 # 下载地址 https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151. ...
- Linux下MySQL主从复制(GTID)+读写分离(ProxySQL)-实施笔记
GTID概念: GTID( Global Transaction Identifier)全局事务标识.GTID 是 5.6 版本引入的一个有关于主从复制的重大改进,相对于之前版本基于 Binlog 文 ...
- [leetcode]1109. 航班预订统计(击败100%用户算法-差分数组的详解)
执行用时2ms,击败100%用户 内存消耗52.1MB,击败91%用户 这也是我第一次用差分数组,之前从来没有碰到过,利用差分数组就是利用了差分数组在某一区间内同时加减情况,只会改变最左边和最右边+1 ...
- 使用什么快捷键,关闭、打开、最小化qq聊天窗口
Alt+F4或者Alt+C关闭聊天窗口.Alt+空格+N 最小化聊天窗口.Alt+H 打开聊天记录,打开聊天窗口没有快捷键,必须点击qq好友图标
- MySQL双主+Keepalived高可用
原文转自:https://www.cnblogs.com/itzgr/p/10233932.html作者:木二 目录 一 基础环境 二 实际部署 2.1 安装MySQL 2.2 初始化MySQL 2. ...
- Git使用教程五
基于ssh协议(推荐) 该方式与前面https方式相比,只是影响github对于用户的身份鉴权方式,对于git的具体操作(如提交本地.添加注释.提交远程等操作)没有任何影响. 生成公私钥对指令(需 ...
- Nginx版本平滑升级方案
背景:由于负载均衡测试服务器中nginx版本过低,存在安全漏洞,查询相关修复漏洞资料,需要采取nginx版本升级形式对漏洞进行修复. Nginx平滑升级方案 1.案例采用版本介绍 旧版本 nginx- ...