学习视频:https://www.bilibili.com/video/BV1rE41197jR
记录学习笔记❤️
方法定义:int insert(T entity);
测试用例
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserMapper {
@Autowired
private UserMapper userMapper;
@Test
public void testInsert() {
User user = new User();
user.setMail("[email protected]");
user.setAge(25);
user.setUserName("zhugeliang");
user.setName("诸葛亮");
user.setPassword("123456");
user.setAddress("广州");
int result = this.userMapper.insert(user); //result数据库受影响的行数
System.out.println("result => " + result);
//获取自增长后的id值, 自增长后的id值会回填到user对象中
System.out.println("id => " + user.getId());
}
修改User对象 ,使id自增长@TableId(type = IdType.AUTO) 不会出现其他的数字
@TableId(type = IdType.AUTO) //使id自增长
private Long id;
@TableField :在MP中通过@TableField注解可以指定字段的一些属性
// 插入数据时进行填充
@TableField(select = false, fill = FieldFill.INSERT) //查询时不返回该字段的值
private String password;
private String name;
private Integer age;
@TableField(value = "email") //指定数据表中字段名
private String mail;
@TableField(exist = false)
private String address; //在数据库表中是不存在的
根据id更新:(测试用例)
@Test
public void testUpdateById() {
User user = new User();
user.setId(1L); //条件,根据id更新 L是数据类型 此处就是id = 1 的信息会被全部修改
user.setAge(19); //更新的字段
user.setPassword("666666");
int result = this.userMapper.updateById(user);
System.out.println("result => " + result);
}
根据条件更新:
第一种方式: QueryWrapper
@Test
public void testUpdate() {
User user = new User();
user.setAge(20); //更新的字段
user.setPassword("8888888");
QueryWrapper wrapper = new QueryWrapper<>();
//两个参数:column:字段,val:字段的值
wrapper.eq("user_name", "zhangsan"); //匹配user_name = zhangsan 的用户数据
//根据条件做更新
int result = this.userMapper.update(user, wrapper);
System.out.println("result => " + result);
}
第二种方式: UpdateWrapper
@Test
public void testUpdate2() {
//可以同时设置字段和条件
UpdateWrapper<User> wrapper = new UpdateWrapper<>();
wrapper.set("age", 21).set("password", "999999") //更新的字段
.eq("user_name", "zhangsan"); //更新的条件
//根据条件做更新
int result = this.userMapper.update(null, wrapper);
System.out.println("result => " + result);
}
根据id删除:deleteById()
@Test
public void testDeleteById(){
// 根据id删除数据
int result = this.userMapper.deleteById(2L);
System.out.println("result => " + result);
}
根据columnMap 条件删除记录 deleteByMap()
@Test
public void testDeleteByMap(){
Map<String,Object> map = new HashMap<>();
map.put("user_name", "zhangsan");
map.put("password", "999999");
// 根据map删除数据,多条件之间是and关系
int result = this.userMapper.deleteByMap(map);
System.out.println("result => " + result);
}
根据entity 条件删除记录 Wrapper< User> wrapper
@Test
public void testDelete(){
//用法一:
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.eq("user_name", "caocao1")
.eq("password", "123456");
//用法二:推荐
User user = new User();
user.setPassword("123456");
user.setUserName("caocao");
QueryWrapper wrapper = new QueryWrapper<>(user);
// 根据包装条件做删除
int result = this.userMapper.delete(wrapper);
System.out.println("result => " + result);
}
根据id 批量删除 id 不能为null 已经 empty deleteBatchIds()
@Test
public void testDeleteBatchIds(){
// 根据id批量删除数据 传递集合 里面传递是就是id的值 则会删除掉对应的id的数据
int result = this.userMapper.deleteBatchIds(Arrays.asList(10L, 11L));
System.out.println("result => " + result);
}
根据id查询 selectById
@Test
public void testSelectById() {
User user = this.userMapper.selectById(2L);
System.out.println(user);
}
根据id批量查询 selectBatchIds
@Test
public void testSelectBatchIds(){
// 根据id批量查询数据 通过集合接受数据 查询 id = 2,3,4,100的数据 没有的id值是查询不到的
List<User> users = this.userMapper.selectBatchIds(Arrays.asList(2L, 3L, 4L, 100L));
for (User user : users) {
System.out.println(user);
}
}
查询一条数据:selectOne
@Test
public void testSelectOne(){
//T selectOne(@Param("ew") Wrapper queryWrapper);
QueryWrapper<User> wrapper = new QueryWrapper<>();
//查询条件
wrapper.eq("password", "123456");
// 查询的数据超过一条时,会抛出异常
User user = this.userMapper.selectOne(wrapper);
System.out.println(user);
}
查询总记录数 selectCount()
@Test
public void testSelectCount(){
// Integer selectCount(@Param("ew") Wrapper queryWrapper);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 20); // 条件:年龄大于20岁的用户
// 根据条件查询数据条数
Integer count = this.userMapper.selectCount(wrapper);
System.out.println("count => " + count);
}
查询数据的列表 selectList()
@Test
public void testSelectList(){
// List selectList(@Param("ew") Wrapper queryWrapper);
QueryWrapper wrapper = new QueryWrapper<>();
//设置查询条件 val:模糊查询匹配
wrapper.like("email", "itcast");
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
分页查询 selectPage()
配置分页插件:
@Configuration
@MapperScan("cn.itcast.mp.mapper") //设置mapper接口的扫描包
public class MybatisPlusConfig {
@Bean //配置分页插件
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
测试用例
// 测试分页查询 要配置一个分页插件
@Test
public void testSelectPage(){
//IPage selectPage(IPage page, @Param("ew") Wrapper queryWrapper);
Page<User> page = new Page<>(3,1); //查询第一页,查询1条数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置查询条件
wrapper.like("email", "itcast");
IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
System.out.println("数据总条数: " + iPage.getTotal());
System.out.println("数据总页数: " + iPage.getPages());
System.out.println("当前页数: " + iPage.getCurrent());
//getRecords获取数据
List<User> records = iPage.getRecords();
for (User record : records) {
System.out.println(record);
}
}
configLocation
单独设置MyBatis配置文档,就讲配置路径配置到configLocation中。
SpringBoot配置方式:
mybatis-plus.config-location = classpath:mybatis-config.xml
mapperLocations
在 Mapper 中有自定义方法(XML 中有自定义实现),需要进行该配置,指定Mapper 所对应的 XML 文件位置
SpringBoot配置方式:
mybatis-plus.mapper-locations = classpath*:mybatis/*.xml
typeAliasesoPackge
MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名,注册后在 Mapper 对应的 XML 文件中可以直接使 用类名,而不用使用全限定的类名(即 XML 中调用的时候不用包含包名)。
mybatis-plus.type-aliases-package = cn.itcast.mp.pojo
mapUnderscoreToCamelCase
类型:boolean
默认值:true
是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属 性名 aColumn(驼峰命名) 的类似映射。
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
cacheEnable
类型:booolean
默认值:true
全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true。
mybatis-plus.configuration.cache-enabled=false
idType
tablePrefix
类型:String
默认值:null
表示前缀,全局配置后可以省略@Tablename()配置
SpringBoot:
mybatis-plus.global-config.db-config.id-type=auto
在Mp中 wrapper 中重要的实现类有:AbstractWrapper AbstractChainWrapper是重点实现 ,前者重点学习
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类 用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件 注意: entity 生成的 where 条件与 使用各个 api 生成 的 where 条件没有任何关联行为
测试用例:
@Test
public void testAllEq(){
Map params = new HashMap<>();
params.put("name", "李四");
params.put("age", "20");
params.put("password", null);
QueryWrapper wrapper = new QueryWrapper<>();
// 生成的sql语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE password IS NULL AND name = ? AND age = ?
// wrapper.allEq(params);
//生成的sql语句:SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
// wrapper.allEq(params, false);
//SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE age = ?
//过滤 满足条件才能进行过滤
// wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id")) , params);
//SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id") || k.equals("name")) , params);
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
eq
ne
gt
ge
lt
le
between
notBetween
in 字段
notIn 字段
NOT IN (v0, v1, …)
测试用例:
@Test
public void testEq() {
QueryWrapper wrapper = new QueryWrapper<>();
// 生成的sql语句:SELECT id,user_name,password,name,age,email FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
wrapper.eq("password", "123456")
.ge("age", 20)
.in("name", "李四", "王五", "赵六");
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
like 模糊前后的值
notLike 不模糊
NOT LIKE '%值%
例: notLike(“name”, “王”) —> name not like ‘%王%’
likeLeft 左模糊
likeRight 右模糊
测试用例:
@Test
public void testLike(){
QueryWrapper wrapper = new QueryWrapper<>();
//生成的sql语句: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name LIKE ?
// 传入参数:%五(String)
wrapper.likeLeft("name", "五");//就把五前面的 字 或者 其他内容 给忽略掉 就是查询以 五 结尾的
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
测试用例:
@Test
public void testOrderByAgeDesc(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
//按照年龄倒序排序
//生成的sql语句: SELECT id,user_name,name,age,email AS mail FROM tb_user ORDER BY age DESC
wrapper.orderByDesc("age");
List<User> users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
测试用例:
@Test
public void testOr(){
QueryWrapper wrapper = new QueryWrapper<>();
//生成的sql语句: SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? OR age = ?
//若是中间没有or()操作,则是用and链接 返回的结果就会不一样
//or不走索引 用 Union拼接
wrapper.eq("name", "王五").or().eq("age", 21);
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
测试用例:
@Test
public void testSelect(){
QueryWrapper wrapper = new QueryWrapper<>();
//生成的sql语句:SELECT id,name,age FROM tb_user WHERE name = ? OR age = ?
wrapper.eq("name", "王五")
.or()
.eq("age", 21)
.select("id","name","age"); //指定查询 id name age 的字段
List users = this.userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
}
ActiveRecord的主要思想:
import cn.itcast.mp.enums.SexEnum;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User extends Model<User> {
//继承Model类
private Long id;
private String userName;
private String password;
private String name;
private Integer age;
private String mail;
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserMapper2 {
@Test
public void testSelectById(){
User user = new User();
user.setId(16L);
User user1 = user.selectById();
System.out.println(user1);
}
}
@Test
public void testInsert(){
User user = new User();
user.setUserName("diaochan");
user.setPassword("123456");
user.setAge(20);
user.setName("貂蝉");
user.setMail("[email protected]");
user.setVersion(1);
user.setSex(SexEnum.WOMAN); //使用的是枚举
// 调用AR的insert方法进行插入数据
boolean insert = user.insert();
System.out.println("result => " + insert);
}
@Test
public void testUpdate(){
User user = new User();
user.setId(13L);// 查询条件
user.setAge(31); // 更新的数据 在13中设置年龄为31岁
boolean result = user.updateById();
System.out.println("result => " + result);
}
@Test
public void testDelete(){
User user = new User();
user.setId(13L);//删除id为13行的数据
boolean delete = user.deleteById();
System.out.println("result => " + delete);
}
@Test
public void testSelect(){
User user = new User();
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.ge("age", 30); //大于等于30岁的用户查询出来 使用ge 这个比较运算符 比较运算符在上面有笔记
List<User> users = user.selectList(wrapper);
for (User user1 : users) {
System.out.println(user1);
}
}
MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用 (拦截器)
拦截了Executor接口部分方法(update、query、commit、rollback)
对上面的方法概述:
插件的方法
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
@Intercepts({@Signature(
type= Executor.class,//指定拦截的类型 Executor
method = "update",//拦截的是update方法
args = {MappedStatement.class,Object.class})})
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//拦截方法,具体业务逻辑编写的位置
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
//这次方法会执行多次,因为要执行上面的四个点(Executor、ParameterHandler 、ResultSetHandler 、StatementHandler )总共执行四次
//创建target对象的代理对象,目的是将当前拦截器加入到该对象中
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
//属性设置
}
}
SpringBoot配置:
@Bean //SQL分析插件
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> list = new ArrayList<>();
list.add(new BlockAttackSqlParser()); //全表更新、删除的阻断器
sqlExplainInterceptor.setSqlParserList(list);
return sqlExplainInterceptor;
}
测试用例 : 查询结构是错误的 被拦截了才会出错 ,说明拦截器生效了
/**
* 测试全表更新,SQL分析器阻断效果
*/
@Test
public void testUpdateAll(){
User user = new User();
user.setAge(110); // 更新的数据
boolean result = user.update(null); // 条件为null 则是全表更新
System.out.println("result => " + result);
}
当执行全表更新时,会抛出异常,这样有效防止了一些误操作
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
注意:该插件只用于开发环境,不建议生产环境使用。(性能问题)
mybatis-config.xml配置文件
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100"/>
<property name="format" value="true"/>
plugin>
目的: 当要更新一条记录的时候,希望这条记录没有被别人更新
并发操作
实现方式:
乐观锁防止多个事务对同一个数据同时修改
Spring .xml
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
Spring boot
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
MyBatis 全局配置
注解实体字段
为实体添加@Version注解
添加@version注解,设置初始值为1
ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';
为User实体对象添加version字段,并且添加@version注解
@Version //乐观锁的版本字段
private Integer version;
测试:
@Test
public void testUpdateVersion(){
User user = new User();
user.setId(2L);// 查询条件
//先拿到版本 再设置id
User userVersion = user.selectById();
user.setAge(23); // 更新的数据
user.setVersion(userVersion.getVersion()); // 当前的版本信息
boolean result = user.updateById();
System.out.println("result => " + result);
}
特别说明:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<T> findAll();
// 扩展其他的方法
}
然后其他的Mapper类就继承这个MyBaseMapper接口 相当于嵌套
import cn.itcast.mp.pojo.User;
public interface UserMapper extends MyBaseMapper<User> {
User findById(Long id);
}
继承DefaultSqlInjector 进行扩展 ,不能直接继承 AbstractSqlInjector 因为继承了这个 导致BaseMapper中的方法失效
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.ArrayList;
import java.util.List;
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
List<AbstractMethod> list = new ArrayList<>();
// 获取父类中的集合
list.addAll(super.getMethodList());
// 再扩充自定义的方法
list.add(new FindAll());
return list;
}
}
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class FindAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "select * from " + tableInfo.getTableName();
SqlSource sqlSource = languageDriver.createSqlSource(configuration,sql, modelClass);
return this.addSelectMappedStatement(mapperClass, "findAll", sqlSource, modelClass, tableInfo);
}
}
5.4注册到Spring容器中
在MyBatisPlusConfig中
@Bean
public MySqlInjector mySqlInjector(){
return new MySqlInjector();
}
测试:
@Test
public void testFindAll(){
List<User> userlist = this.userMapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
为password添加自动填充功能,在新增数据时有效
FieldFill提供了多种模式选择
测试:
删除操作需要实现逻辑删除 ,所谓逻辑删除就是将数据标记为删除,而并非真正 的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免 数据被真正的删除。
application.properties
7.3测试
解决繁琐的配置 ,通常表示一些固定的属性 比如:性别(男、女),学历:(小、初、高、大)
ALTER TABLE `tb_user`
ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;
package cn.itcast.mp.enums;
import com.baomidou.mybatisplus.core.enums.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;
public enum SexEnum implements IEnum<Integer> {
MAN(1,"男"),
WOMAN(2,"女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return this.desc;
}
}
application.properties文件
MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
就是将数据标记为删除,而并非真正 的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免 数据被真正的删除。
application.properties
7.3测试
解决繁琐的配置 ,通常表示一些固定的属性 比如:性别(男、女),学历:(小、初、高、大)
ALTER TABLE `tb_user`
ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;
package cn.itcast.mp.enums;
import com.baomidou.mybatisplus.core.enums.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;
public enum SexEnum implements IEnum<Integer> {
MAN(1,"男"),
WOMAN(2,"女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
@Override
public String toString() {
return this.desc;
}
}
application.properties文件
MyBatisX 是一款基于 IDEA 的快速开发插件,为效率而生。