导入依赖
<!--MP--> <dependency> <groupId></groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <!-- 代码生成器--> <dependency> <groupId></groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!-- 代码生成器模板引擎--> <dependency> <groupId></groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
雪花算法
数据库的扩展方式主要包括:业务分库,主从复制,数据库分表
数据库分表
如果业务持续发展,同一业务的单表数据也会达到数据库服务器的处理瓶颈;例如淘宝几亿用户数据,因此需要对单表数据进行拆分。
垂直分表
列也比较多,查询量比较大的时候,通常把经常被查询的字段 和 数据量比较大的字段,拆分到不同的表中,比如age,sex 主要是查询使用,nickname昵称字段和描述字段主要用于展示使用而且本身还比较长,可以将后面两个字段独立到另一个表中去,这样查询的age 和 sex时 能带来一定的性能提升,相当于对表垂直切了一刀,把非主要查询的这两个字段独立出去。也就是把大字段或一般查询用不到的字段分离到另外一张表中。user user_info 用 id进行关联,user id字段自增长,user_info id字段不设策略,user的id是啥 我就是啥
主键一对一关联;
水平分表
适合表行数特别大的表,数据量特别大的时候,也没有明确查询字段之类的需求,关键还要看表的访问性能,一些比较复杂的表,可能超过1000万就要分表了,按数据分表,多少万条数据在一个表上,多少行数据在一个表上,相当于横着切一刀 分出去。水平分表相比垂直分表会引入更多的复杂性。
复杂性体现:主键自增问题 如果分2张表,按照主键范围存在不同的表上,比如1~1千万,1千万零一~2千万,分别在两张表上,如果满了 扩展容易,再加即可,实现了动态扩展,各表内数据量有多有少,现在一般都是负载均衡,会导致服务器访问不均衡。如果解决均衡问题,可以将表的主键起始id分表设为1、 2、 3 步长为3,这样就均衡了,但是扩展又会出问题,如果扩展又会出现数据迁移问题,重新分配布局。两种方式扩展都可能会遇到之前的表里面有数据被删除,然后后面的数据主键自增已经占用了新的主键,扩展的id并不是理论上应该存的id,而是之前的表可能已经用掉了。会不安全
解决办法:
Hash:取模运算,放到哪一个表中,要求初始表数量确定,表数量太多维护比较麻烦,太少又可能导致单表性能出现问题,表分布会比较均匀,但是扩展会很麻烦,所有数据都要重分布,uuid不能和mysql的聚簇索引一起用,不利于mysql查询优化
雪花算法snow flake(分布式id占19个符号位):mybatis-plus中默认的主键策略就是雪花算法
分布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性
核心思想:
长度64bit一个Long型,分四个部分:
1.第一个位置,符号位,为0,表示为正数,一般都不会用负数
2.第二个位置,时间戳位,41bit,存储的是差值,约定于69.73年,这样生成的时间都是不一样的
3.第三个位置,5bit是数据中心(相当于机房代码),5bit机器ID(表示数据中心存储数据的机器,可以部署在1024个节点,相当于1024台机器),
4.第四个位置,12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),支持并发量很大
mybatis-plus 默认识别 名字叫“id”的字段,自动进行雪花算法插入数据库id,如果数据库是uid实体类也是uid字段, 或者其他则识别不了,无法进行默认的雪花算法插入主键到数据库,会报错。所以此时需要在实体类的字段上加@TableId 告诉mybatis-plus这个字段是id 字段(数据库中是uid);
@TableName(value="t_user") public class User { //默认雪花算法 也可以设置 // @TableId(type= IdType.ASSIGN_ID) private String id; 也可以 @TableId private Long uid;
或者,数据库名字叫uid,实体类叫做id,也无法完成映射,所以需要加value属性,如同@TableName注解
@TableName(value="t_user") public class User { //@TableId(value = "uid",type = ) 主键自增策略 @TableId(value = "uid") private Long id; private String name; private int age; private String email;xxxxxxxxxx @TableId(value = "uid")private Long id;private String name;private int age;private String email;
业务中id为空,数据库中也没有设置id;id是主键,不能为空,所以在插入的时候就会报错。如果所有的表都要求主键自增,可以在配置文件中设置
#设置全局主键自增 -type=auto
新增两个字段 ,如果类中的列名为驼峰命名
private LocalDateTime createTime; private LocalDateTime updateTime; //在数据库中字段为:create_time update_time mybatis-plus 会自动把驼峰进行转换
如果数据库中的字段名跟实体类不一致,需要用@TableField(value="")
@TableId(value = "uid",type = ) private Long id; @TableField(value = "username") private String name; private int age; private String email;
数据库字段可进行自动填充,创建和更新时间 可以用CURRENT_TIMESTAMP 系统自动管理,更新字段,再勾上默认根据当前时间戳进行更新,这样管理就不需要每次业务里面涉及。也可以在业务层进行设计,自动填充功能,需要在实体类加注解来标识。
@TableField(fill = ) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) //插入或更新的时候自动填充 private LocalDateTime updateTime; //但是填充什么呢?需要定义实现自动填充功能 实现元对象处理器接口
@Slf4j @Component public class MyMeatObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { ("insert自动填充...."); //插入数据的时候 识别到注解 @TableField(fill = ) 就会进入这个方法来执行 (metaObject,"createTime", , ()); (metaObject,"updateTime", , ()); } @Override public void updateFill(MetaObject metaObject) { ("update自动填充。。。"); //更新的时候 (metaObject,"updateTime",,()); } }
//判断当前对象自动填充属性是否包含 当前属性 boolean hasAuthor = ("author"); //如果 有setter的方法 有这个author字段 进行自动填充author if(hasAuthor) { ("insert author属性"); (metaObject, "author", , "石头"); }
年龄也自动填充
@TableField(fill = ) private Integer age;
("age 填充为18"); //对age进行自动填充 当没传age的时候填充18进去 Object age = ("age", metaObject); //如果业务层赋值了就不用去填充,如果没有赋值去进行填充 if(age==null){ (metaObject, "age", , 18); }
@TableLogic 逻辑删除
物理删除:真实删除
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录使用场景,可以进行数据恢复
数据库中此字段 一般默认类型tinyint类型,默认1为已删除,0未删除
在数据库中创建逻辑删除状态列 同时实体字段一般前面不要加is有些框架识别可能出问题
//private Integer deleted; //逻辑删除字段 0表示false 1表述true @TableLogic @TableField(value = "is_deleted") private Boolean deleted;
执行记录 需要数据库中此字段 值为0
Preparing: UPDATE t_user SET is_deleted=1 WHERE uid=? AND is_deleted=0 ==> Parameters: 1499759447969390597(Long)<== Updates: 0
//自定义 1为未删除 -1为已删除 需要去配置 @TableLogic @TableField(value = "is_deleted") private Integer deleted;
-delete-field=deleted -delete-value=-1 -not-delete-value=1
分页插件
1.添加配置类 config包 可以将配置全部写在这个包内
spring
<bean class=""> <!-- 其他属性 略 --> <property name="configuration" ref="configuration"/> <property name="plugins"> <array> <ref bean="mybatisPlusInterceptor"/> </array> </property> </bean> <bean class=""> <!-- 需配置该值为false,避免1或2级缓存可能出现问题,该属性会在旧插件移除后一同移除 --> <property name="useDeprecatedExecutor" value="false"/> </bean> <bean class=""> <property name="interceptors"> <list> <ref bean="paginationInnerInterceptor"/> </list> </property> </bean> <bean class=""> <!-- 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型 --> <constructor-arg name="dbType" value="H2"/> </bean>
spring-boot
//可将包扫描写在这个类里面 也可以写在启动类上,对持久层的扫描
@Configuration//表示这是一个配置类,应用程序启动的时候会被自动读取并以配置的形式读取
@MapperScan("")//可以将持久层所有的配置集中在这里管理 全写在这个配置类中
public class MybatisPConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
//所有的插件都是以拦截器的形式存在
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//先创建拦截器(插件)管理器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分页插件 将分页(这里是mysql)添加到一个内置的拦截器管理器中( 将分页插件的拦截器对象创建出来,然后用此方法配置到 这个管理器之中)
(new PaginationInnerInterceptor());
//添加乐观锁插件
(new OptimisticLockerInnerInterceptor());
//最后将这个interceptor对象,作为一个bean对象返回
return interceptor;
}
// @Bean
// public ConfigurationCustomizer configurationCustomizer() {
// return configuration -> (false);
// }
}
针对mysql的分页配置类 写好了。进行测试
@Slf4j
@SpringBootTest
public class IntercepterTest {
@Resource
private UserMapper userMapper;
@Resource
private ProductMapper productMapper;
@Test
public void test1(){
//新建分页参数对象 查询第几页,每页5条记录
Page<User> userPage = new Page<>(2, 5);
//这两个对象是一个对象 参数为页对象 及qureyMapper条件构造器 先传null
//Page<User> userPage1 = (userPage, null);
//("确认是否是一个对象"+(userPage==userPage1)); true
//优化为
(userPage, null);
//当前页码下的所有记录
List<User> users = ();
(::println);
//总数
long total = ();
("总数+"+total);
//有没有下一页
boolean bn = ();
("下一页??"+bn);
//有没有上一页
boolean bp = ();
("上一页?"+bp);
}
在xml中如何使用配置分页?
//@Repository public interface UserMapper extends BaseMapper<User> { //自定义一个方法 具体的xml实现在resources下面的mapper文件中 //扩展mapper List<User> selectAllByName(String name); //第一个参数为分页对象 第二个为查询条件 根据年龄来查询用户 并分页展示 IPage<User> selectPageVo(IPage<?> page, Integer age);
<select resultType=""> select <include ref/> from t_user where age > #{age} </select> </mapper>
xml中不需要去进行 分页,只需要写条件即可,我们传递了page对象进去,MP会自动的去读取参数中的配置对象,且会作为整个查询语句的后缀,然后执行sql
@Test public void selectByAge(){ Page<User> userPage = new Page<>(2,6); IPage<User> userIPage = userMapp (userPage, 18); List<User> users = (); (::println); }
执行语句为:自动追加
Total: 1==> Preparing: select uid,name,age,email from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: uid, name, age, email<== Row: 1500133241527472131, 建国2, 22, jiangu2o@<== Row: 1500133241527472134, 建国更新, 100, jiangu2o@<== Row: 1500133241527472139, 观海200, 200, jianguo5@
但是实体类里面是 id 所以这里自定义的方法 的xml 需要改
<mapper namespace=""> <!--sql片段--> <sql > uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime </sql> <select resultType=""> select <include ref/> from t_user where name = #{name} </select> <select resultType=""> select <include ref/> from t_user where age > #{age} </select>
==> Preparing: select uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: id, name, age, email, deleted, createTime, updateTime<== Row: 1500133241527472131, 建国2, 22, jiangu2o@, 0, 2022-03-06 14:38:17, 2022-03-06 14:38:17Closing non transactional SqlSession [@4e1459ea]User(id=1500133241527472131, name=建国2, age=22, email=jiangu2o@, createTime=2022-03-06T14:38:17, updateTime=2022-03-06T14:38:17, deleted=false)
MP的乐观锁应用
A和B都要对数据进行修改操作,但是取出的数据都是未更新过的数据,同时修改,存入就会存在数据覆盖的情况,出现错误数据。比如两个人同时修改某一商品的价格。一个增加一个减少,造成并发冲突,并发也不高但是造成数据不一致问题。可以通过乐观锁来解决
SELECT id,name,version FROM product WHERE id=1;
查询的时候就需要将 控制版本的version取出来 更新时,对version进行控制,每次有人修改的时候都需要提前判断,符合条件才能进行修改。每修改一次 版本号会加一
//告诉mp这 就是版本号 @Version private Integer version; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分页插件 (new PaginationInnerInterceptor()); //添加乐观锁插件 (new OptimisticLockerInnerInterceptor()); return interceptor; }
Preparing: SELECT id,name,price,version FROM product WHERE id=?==> Parameters: 1(Long)<== Columns: id, name, price, version<== Row: 1, 笔记本, 150, 3
UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 200(Integer), 4(Integer), 1(Long), 3(Integer)
修改后 将版本号为3的记录 版本号更改为4 那么同时另外一条修改语句 则无法修改
=> Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 120(Integer), 4(Integer), 1(Long), 3(Integer)<== Updates: 0
Wrapper条件构造
Wrapper → AbstractWrapper
AbstractLambdaWrapper(抽象类) → LambdaUpdateWrapper 和 LambdaQueryWrapper
UpdateWrapper QueryWrapper
/** * 查询名字中包含a * 年龄大于10且小于20 email不为空的用户 */ @Test public void test(){ QueryWrapper<User> queryWrapper = new QueryWrapper<User>(); //column对应数据库表的列名 而不是属性名 ("name","a"); //左侧为% ("name","k"); //gt为> ge为≥ lt为< le为≤ ("age",10) .lt("age",20) .isNotNull("email"); List<User> users = (queryWrapper); (::println); //between 大于等于 小于等于 ("age",0,20);
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND name LIKE ? AND age > ? AND age < ? AND email IS NOT NULL)==> Parameters: %a%(String), %k(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499409859102179330, jack, 18, jianguo@, null, null, 0
case函数
按照姓名 分国籍 操作 SELECT ( CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END ) country , sum(age) 总年龄 FROM `t_user` GROUP BY( CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END );
/** * 按年龄降序查询用户,如果年龄相同则按id升序排序 */ @Test public void test2() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //组装排序条件 ("age").orderByAsc("uid"); List<User> list = (queryWrapper); (::println); }
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted
/** * 删除email为空的用户 */ @Test public void test3(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); ("email"); int res = (queryWrapper); ("删除的记录数:"+res); //之前配置了逻辑删除,所以只是把is_deleted更新为1 }
Preparing: UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
/** * 查询名字中包含花 且(年龄小于12或email为空的用户),并将这些用户的年龄设置为18,设置为user@ */ @Test public void test4(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); ("name","花") .and(i->("age",18).or().isNull("email")); User user = new User(); (18); ("user@"); int res = (user, queryWrapper); ("更新的条数为:"+res);
Preparing: UPDATE t_user SET age=?, email=?, update_time=? WHERE is_deleted=0 AND (name LIKE ? AND (age < ? OR email IS NULL))==> Parameters: 18(Integer), user@(String), 2022-03-14T23:07:38.807(LocalDateTime), %花%(String), 18(Integer)<== Updates: 5
查询所有用户的用户名和年龄 其他的不要
//查询用户名和年龄 其他的不要 @Test public void test5(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); ("name","age"); //其他属性 不会返回 返回的是map泛型 //select语句通常会和selectMaps一起出现 List<Map<String, Object>> maps = (queryWrapper); (::println); }
==> Preparing: SELECT name,age FROM t_user WHERE is_deleted=0==> Parameters: <== Columns: name, age<== Row: Jack, 20<== Row: 正阳, 20<== Row: Billie, 24
/** * 使用子查询 * 查询id不大于3的所有用户的id列表 */ @Test public void test6(){ QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //如果子查询由用户输入 容易sql注入 ("uid","select uid from t_user where uid<=3"); // 可以这么写 ("uid",3); List<User> users = (queryWrapper); (::println); }
SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid<=3))==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 2, Jack, 20, test2@, null, null, 0<== Total: 1
UpdateWrapper
/** * 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@ */ @Test public void test7(){ UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); ("age",16) .set("email","user@") .like("name","花") .and(i-> ("age",18).or().isNotNull("email")); //如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime //不需要 可以传null User user = new User(); int res = (user, updateWrapper); ("更新的条数为:"+res); }
Preparing: UPDATE t_user SET age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 16(Integer), user@(String), %花%(String), 18(Integer)<== Updates: 5
==> Preparing: UPDATE t_user SET update_time=?, age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 2022-03-14T23:33:21.205(LocalDateTime), 16(Integer), user@(String), %花%(String), 18(Integer)<== Updates: 5
condition 动态组装查询条件
/** * 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的 */ @Test public void test8() { String username = "花"; Integer ageBegin =10; Integer ageEnd = 20; QueryWrapper<User> queryWrapper = new QueryWrapper<>(); if ((username)) { ("name", username); } if(ageBegin!=null){ ("age",ageBegin); } if(ageEnd !=null) { ("age", ageEnd); } List<User> users = (queryWrapper); (::println); } //利用重载的方法 @Test public void test8() { String username = "花"; Integer ageBegin = null; Integer ageEnd = 20; QueryWrapper<User> queryWrapper = new QueryWrapper<>(); ((username), "name", username) .ge(ageBegin != null, "age", ageBegin) .le(ageEnd != null, "age", ageEnd); List<User> users = (queryWrapper); (::println); }
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age >= ? AND age <= ?)==> Parameters: %花%(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@, null, 2022-03-14 23:33:21, 0
SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)==> Parameters: %花%(String), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390594, 花花1, 16, user@, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390595, 花花2, 16, user@, null, 2022-03-14 23:33:21, 0
lambdaQueryWrapper
/** * 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的 */ @Test public void test9() { String username = "花"; Integer ageBegin = null; Integer ageEnd = 20; LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>(); ((username), User::getName, username) .ge(ageBegin != null, User::getAge, ageBegin) .le(ageEnd != null, User::getAge, ageEnd); List<User> users = (lambdaQueryWrapper); (::println); }
lambdaUpdateWrapper
/** * 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@ */ @Test public void test10() { LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); (User::getAge, 16) .set(User::getEmail, "user@") .like(User::getName, "花") .and(i -> (User::getAge, 18).or().isNotNull(User::getEmail)); //如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime //不需要 可以传null User user = new User(); int res = (user, updateWrapper); ("更新的条数为:" + res); }
其实就是把条件组装里的字符串形式 替换为Lambda替换,避免编译的时候没有发现的不必要的错误。
Gennerator代码生成器
public class CodeGenerator { @Test public void genCode(){ // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = (""); (projectPath + "/src/main/java"); ("eric"); //去掉Service接口的首字母I 不加 接口名字前面会有I %s是占位符 实体名字 ("%sService"); //生成后是否打开资源管理器 (false); //主键策略 (); //开启swagger2模式 gc.setSwagger2(true); (gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); ("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8"); // ("public"); (""); ("root"); ("root"); (); (dsc); // 包配置 PackageConfig pc = new PackageConfig(); // (); (""); //此对象与数据库表结构一一对应,通过dao层向上传输数据源对象 (""); // (); // (); (pc); // 策略配置 StrategyConfig strategy = new StrategyConfig(); //数据库表映射到实体的命名策略 数据库表名称 下划线变驼峰 (NamingStrategy.underline_to_camel); //数据库表字段映射到实体的命名策略 列名转驼峰 (NamingStrategy.underline_to_camel); // ("你自己的父类实体,没有就不用设置!"); (true); //逻辑删除字段 ("is_deleted"); //去掉布尔值的is_前缀(确保tinyint(1) 实体类中最好不要用is做前缀 容易出问题 变成deleted 会加字段映射@TableField (true); //restful风格控制器 一般都会返回json 有@ResponseBody 会直接用@RestController (true); (strategy); //执行 (); } }
核心模块的yml配置
erver: port: 8110 spring: profiles: active: dev application: # 以后要注册到微服务中 起一个 服务名 name: service-core datasource: type: driver-class-name: url: jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8 username: root password: root mybatis-plus: configuration: #日志输出 log-impl: mapper-locations: classpath:com/xie/srb/core/mapper/xml/*.xml
创建主类
package ; import ; import ; import ; @SpringBootApplication @ComponentScan("") public class ServiceCoreApplication { public static void main(String[] args) { (,args); } }
配置MP分页
package ; /** * 配置分页 */ @Configuration @MapperScan("") //事务处理 @EnableTransactionManagement public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //分页 (new PaginationInnerInterceptor()); return interceptor; } }
<build> <resources> <!-- xml放在java目录--> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!-- 指定资源的位置(xml放在resources下可以不用指定 --> <resource> <directory>src/main/java/com/xie/srb/core/mapper/xml</directory> </resource> </resources> </build>