使用
1. 引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
2. 配置数据库连接
spring:
datasource:
username: root
password: 123
url: jdbc:mysql://localhost:3306/mybatisPuls?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
3. 编写pojo和mapper
pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class user {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper
//在对应的mapper中继承BaseMapper
@Repository //代表持久层 和@mapper作用一样,只是说明是持久层而已
public interface userMapper extends BaseMapper<user> {
//所有的crud操作都已经编写完成了
//不需要像以前一样配置一大堆文件了
}
4. 在启动加入@scanmapper
@MapperScan("com.example.mapper") //重点
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
5. 测试
@Test
void contextLoads() {
//参数是一个wrapper,是一个条件构造器
List<user> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
执行结果
配置日志
org.apache.ibatis.logging.stdout.StdOutImpl是mabatis自带的日志,如果用log4j或者其他需要导入相应的依赖
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
CRUD扩展
insert
id默认会自动生成,默认使用的是雪花算法即id_worker
在主键上如下配置即可设置不同的主键策略
分布式唯一ID:https://juejin.im/post/5b3a23746fb9a024e15cad79
public enum IdType {
AUTO(0), //自增id
NONE(1), //没有
INPUT(2), //手动输入
ID_WORKER(3), //雪花算法
UUID(4), //uuid
ID_WORKER_STR(5); // 雪花算法的字符型形式
private int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
update
@Test
void testUpdate(){
user user = new user();
//通过条件自动拼接sql
user.setId(1L);
user.setName("狂神");
//updateById 名字虽然是通过id更新,但是参数却是user对象
int i = userMapper.updateById(user);
System.out.println(i);
}
自动填充
创建时间、修改时间!这个操作希望自动话完成
阿里巴巴开发手册:所有的数据库表:gmt_create gmt_modified 几乎所有的表都要配置上,而且要自动化
方式一:数据库级别
- 在表中新增 create_time update_time 字段
方式二:代码级别
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
编写处理器
@Slf4j
@Component //一定不要忘记把处理器加入到spingboot的ioc容器中
public class MyDataObject implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start execute insert ");
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start execute update ");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
乐观锁
乐观锁:总是认为不会发生问题,无论干什么都不去加锁!如果出现了问题,再次更新值测试
悲观锁:总是认为会发生问题,无论干什么都加锁
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
1. 给数据加个version字段
设置默认值为1
2. 给version字段加上相应注解
@Version
private int version;
3. 注册组件
//扫描mapper文件夹
@MapperScan("com.example.mapper")
@EnableTransactionManagement// 默认就是这样的
@Configuration
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
4. 测试
多线程下一定要加锁
//单线程情况下是可以成功的 乐观锁
@Test
void testOptimisticLocker(){
user user = userMapper.selectById(1L);
user.setName("kuangshen");
user.setEmail("qq.com");
userMapper.updateById(user);
}
//多线程会出错
@Test
void testOptimisticLocker2(){
//线程1
user user = userMapper.selectById(1L);
user.setName("kuangshen");
user.setEmail("qq.com");
//模拟多线程情况
//在user还没有执行更新操作之前,user2另一个线程修改了对应数据
user user2 = userMapper.selectById(1L);
user.setName("fail");
user.setEmail("qq.com");
//自旋锁多次尝试提交!
userMapper.updateById(user); //如果没有乐观锁,会覆盖插队线程的值
}
select
//测试查询
@Test
void testSelectById(){
user user = userMapper.selectById(1L);
System.out.println(user);
}
//测试批量查询
@Test
void testSelectBatchIds(){
List<user> user = userMapper.selectBatchIds(Arrays.asList(1,2,3));
user.forEach(System.out::println);
}
//条件查询 用map
@Test
void selectByMap(){
HashMap<String, Object> map= new HashMap<>();
map.put("name","Tom");
List<user> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
连表查询
1. 新建一个pojo类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class userClassName {
private Long id;
private String name;
private Integer age;
private String email;
//字段填充内容
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
@Version //乐观锁注解
private int version;
@TableLogic //逻辑删除
private int deleted;
//上面是 userPojo类,下面是classNamePojo类
@TableId(type = IdType.AUTO)
private int iid;
private String className;
private int classId;
}
2. 新建一个对应的Mapper
@Repository
public interface userClassNameMapper extends BaseMapper<userClassName> {
@Select("select * from user u left join className c on u.id=c.iid where u.id=#{id}")
List<userClassName> get(int id);
}
3. 测试
//测试连表查询
@Test
void testLeftJoin11() {
userClassNameMapper.get(1).forEach(System.out::println);
}
测试结果:
userClassName(id=1, name=fail, age=18, email=qq.com, createTime=null, updateTime=null, version=3, deleted=0, iid=1, className=一年级二班, classId=12)
在iid之前是 user,之后是className
分页查询
1. 配置分页
//扫描mapper文件夹
@MapperScan("com.example.mapper")
@EnableTransactionManagement// 默认就是这样的
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
2.直接就可以使用了
//测试分页查询
@Test
void testPage(){
//参数一:当前页,参数二:页面大小
Page<user> page = new Page<>(2,5);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
delete
//测试删除
@Test
void testDelete(){
userMapper.deleteById(1L);
}
直接执行相应的方法就行
逻辑删除
物理删除:从数据库直接删除
逻辑删除:在数据库中没有删除,通过一个字段让他失效
管理员可以看到被删除的记录!防止数据的丢失,类似于回收站!
1. 在数据库中增加一个deleted字段
2. 实体类中增加属性
@TableLogic //逻辑删除
private int deleted;
3. 配置
//逻辑删除组件
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
数据库中 deleted默认为1
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-not-delete-value: 1
logic-delete-value: 0
4. 测试
执行的是删除操作,但是实际上是更新操作
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
查询的时候会自动过滤掉deleted为0的信息,即被逻辑删除了,查询不到该值了
性能分析插件
作用:用于输出每条 SQL 语句及其执行时间
1. 导入插件
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//设置sql最大执行时间,超过不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
2. 设置为开发 或测试环境
spring:
profiles:
active: dev
3. 测试
查询所有用户
@Test
void contextLoads() {
//参数是一个wrapper,是一个条件构造器
List<user> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
条件构造器
参考官方文档执行即可
@Test
public void test(){
QueryWrapper<user> wrapper = new QueryWrapper<>();
wrapper.isNotNull("name")
.isNotNull("email")
.ge("age",21);
userMapper.selectList(wrapper).forEach(System.out::println);
}
//模糊查询
@Test
public void test2(){
QueryWrapper<user> wrapper = new QueryWrapper<>();
wrapper.like("name","e");
userMapper.selectList(wrapper).forEach(System.out::println);
}
//内查询
@Test
public void test3(){
QueryWrapper<user> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where id>3");
List<Object> list = userMapper.selectObjs(wrapper);
list.forEach(System.out::println);
}
代码生成器
写一个相关配置类,运行即可
public static void main(String[] args) {
AutoGenerator autoGenerator = new AutoGenerator();
//配置策略
//1.全局配置
GlobalConfig config = new GlobalConfig();
//获取用户目录
String property = System.getProperty("user.dir");
config.setOutputDir(property+"/src//main/java");
config.setAuthor("shy");
config.setOpen(false);
config.setFileOverride(false);//是否覆盖
config.setServiceName("%sService"); //去除servie的i前缀
config.setIdType(IdType.ID_WORKER);
config.setDateType(DateType.ONLY_DATE);
config.setSwagger2(true); //自动配置swagger文档
autoGenerator.setGlobalConfig(config);
//2. 设置数据源
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatisPuls?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=false");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("123");
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setDbType(DbType.MYSQL);
autoGenerator.setDataSource(dataSourceConfig);
//3. 包的配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setModuleName("test");
packageConfig.setParent("com.example");
packageConfig.setEntity("entity");
packageConfig.setController("controller");
packageConfig.setMapper("mapper");
packageConfig.setService("service");
autoGenerator.setPackageInfo(packageConfig);
//4. 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("user");//设置要映射的表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true); //自动使用lombok
strategy.setLogicDeleteFieldName("deleted");//逻辑删除的名字
//自动填充配置
TableFill insertTime = new TableFill("create_time", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
List<TableFill> objects = new ArrayList<>();
objects.add(insertTime);
objects.add(updateTime);
strategy.setTableFillList(objects);
//乐观锁配置
strategy.setVersionFieldName("version");
strategy.setRestControllerStyle(true);//开启restFul的驼峰命名
strategy.setControllerMappingHyphenStyle(true);
autoGenerator.setStrategy(strategy);
autoGenerator.execute();
}
排除非表字段的三种方式
建立实体类的时候,正常的时候是会将数据库中所有字段映射的,但是有时候需要用到一些信息,这些信息不需要存入到数据库中,而是有其他用途,这时候,就要用下面的东西了。
无序列化
private transient String remark; transient就是表示这个变量是非表字段
有序列化
private static String remark; 生成gat set方法
@TableField(exist=false)
private String remark;