一、mybatis plus 介绍
mybatis plus 是国内人员开发的 mybatis 增强工具,在 mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。mybatisx 是一款基于 idea 的快速开发插件,为效率而生。
mybatis plus 的核心功能有:支持通用的 crud、代码生成器与条件构造器。
- 通用 crud:定义好 mapper 接口后,只需要继承
basemapper<t>接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件 - 条件构造器:通过
entitywrapper<t>(实体包装类),可以用于拼接 sql 语句,并且支持排序、分组查询等复杂的 sql - 代码生成器:支持一系列的策略配置与全局配置,比 mybatis的代码生成更好用
/***
* 通用删除操作 deletebatchids 通过多个id进行删除
*/
@test
public void testdeletebatchids() {
list<integer> idlist = new arraylist<integer>();
idlist.add(5);
idlist.add(6);
int result = employeemapper.deletebatchids(idlist);
system.out.println("*******************" + result);
}
public void selectin() {
querywrapper<user> wrapper = new querywrapper<>();
wrapper.setsqlselect(winagent.col_id, winagent.col_ag_num);
wrapper.in(user.col_ag_num, new string[]{"11","22","33","44"});
list agreelist = arrays.aslist(agreeman.split(",|,"));
if (agreelist.size() > 1) {
wrapper.in(user.col_create_user, agreelist);
} else {
wrapper.eq(user.col_create_user, agreelist.get(0));
}
usermapper.selectlist(wrapper);
}
/**
* 根据条件构造器删除
*/
public void testdeletebywrapper(){
querywrapper<user> wrapper = new querywrapper<>();
// 参数1-字段名,参数2-匹配的值
wrapper.eq("user_name", "lisi");
// count代表删除成功的记录数
int count = usermapper.delete(wrapper);
}
/***
* 通用更新操作
*/
@test
public void testupdateorinsert() {
employee entity = new employee();
entity.setid(1);
entity.setname("更新测试成功");
int result = employeemapper.updatebyid(entity);
//int result = employeemapper.insert(entity);
system.out.println("插入后:employee id="+entity.getid());
}
public void testupdate() {
employee entity = new employee();
entity.setname("更新测试成功");
querywrapper<user> querywrapper = new querywrapper<>();
querywrapper.eq("id", "1");
int result = employeemapper.update(entity, querywrapper);
}
/**
* 查询所有数据
* usermapper 中的 selectlist() 方法的参数
* 为 mp 内置的条件封装器 wrapper,所以不填写就是无任何条件(条件构造器)
*/
@test
public void selectlist() {
list<user> list = usermapper.selectlist(null);
list.foreach(system.out::println);
}
/**
* 根据 entity 条件,查询一条记录
* 其实 querywrapper 条件构造器(相当于给sql的条件语句)
* selectone() 方法查询必须有且只能查询一条记录,多一条会报错。
*/
@test
public void selectone() {
querywrapper<user> querywrapper = new querywrapper<>();
//查询名字为 tom 的一条记录
querywrapper.eq("name", "tom");
user user = usermapper.selectone(querywrapper);
system.out.println(user);
}
/**
* 根据主键id查询数据
* 查询主键id=2 的一条数据,只能查询一个主键的数据不能多个
*/
@test
public void selectbyid() {
user user = usermapper.selectbyid(1);
system.out.println(user);
}
/**
* 根据 list 的 id 集合 查询对应的用户list
*/
@test
public void selectbatchids() {
list<user> list = usermapper.selectbatchids(arrays.aslist(new string[] { "1", "2" }));
list.foreach(system.out::println);
}
/**
* 查询(根据 columnmap 条件),查询年龄为20岁的所有记录
* <p>
* 注意:建议尽量减少使用map这种方式。
* 因为可能字段名可能存在修改的情况,
* 如果,项目开发一段时间后,再修改,影响太大
*/
@test
public void selectbymap() {
map<string, object> map = new hashmap<>();
map.put("age", "20");
list<user> list = usermapper.selectbymap(map);
list.foreach(system.out::println);
}
/**
* 查询大于20岁的学生,名称中包含“j”的人,带条件判断的,可以采用这种方式
* select * from user where (name like ? and age > ?)
* gt()方法 相当于:大于 > 但没有等于=
*/
@test
public void selectlistwrapper() {
querywrapper<user> querywrapper = new querywrapper<>();
querywrapper.like("name", "j");//格式:(字段,值)
querywrapper.gt("age", "19"); //查询不小于20岁
list<user> list = usermapper.selectlist(querywrapper);
list.foreach(system.out::println);
}
/**
* 根据 wrapper 条件,查询全部记录。
* 查询名字含有 a 的,且年龄大于等于20。
* ge()方法 相当于: 大于等于 >= 。
* like()方法 相当于: not like '%值%' 。
*/
@test
public void selectmaps() {
querywrapper<user> querywrapper = new querywrapper<>();
querywrapper.like("name", "a");
querywrapper.ge("age", "20");
list<map<string, object>> maps = usermapper.selectmaps(querywrapper);
maps.foreach(system.out::println);
}
/**
* 根据 wrapper 条件,查询总记录数
* 查询一共有多少条数据记录
*/
@test
public void selectcount() {
querywrapper<user> querywrapper = new querywrapper<>();
integer count = usermapper.selectcount(querywrapper);
system.out.printf("一共有" + string.valueof(count) + "条记录");
}不更新名称,前提没有配置fieldstrategy.ignored
employee entity = new employee(); entity.setid(1); entity.setname(null); employeemapper.updatebyid(entity);
查询括号与sql打印
//有很多中wrapper实现类,我也不是很了解,这里我使用的是entitywrapper
entitywrapper wrapper = new entitywrapper();
wrapper.eq("status", status);
wrapper.andnew().gt("gmt_end", now).or().isnotnull("days");
integer count = couponmapper.selectcount(wrapper);
// 打印生成的sql
log.info("生成的sql: {}", wrapper.getsqlsegment());
select count(1) from unimall_coupon where status = 1 and (gmt_end > '2019-07-14 16:14:23.51' or days is not null)分页查询
/**
* 根据 entity 条件,查询全部记录(并翻页)。
* 查询年龄大于 18 且 小于等于30 岁的所有记录,分 3 条数记录为一页
* gt()方法 相当于: 大于 > 。
* le()方法 相当于: 小于等于 <= 。
*/
@test
public void selectpage() {
querywrapper<user> querywrapper = new querywrapper<>();
querywrapper.gt("age", "18");
querywrapper.le("age", "30");
//第一个参数表示当前页
//第二个参数表示当前页显示多少条
//第三个参数是否查询count
page<user> page = new page<>(1, 3);
page<user> useripage = usermapper.selectpage(page, querywrapper);
list<user> records = useripage.getrecords();
records.foreach(system.out::println);
}
public void selectpage1() {
wrapper<user> wrapper = new entitywrapper<>();
wrapper.last("limit 0,30");
list<user> list = this.selectlist(wrapper);
}
/**
* 根据 wrapper 条件,查询全部记录(并翻页)
*/
@test
public void selectmapspage() {
querywrapper<user> query = new querywrapper<user>();
//第一个参数表示当前页
//第二个参数表示当前页显示多少条
page<map<string, object>> page = new page<>(2, 3);
page<map<string, object>> maps = usermapper.selectmapspage(page, query);
maps.getrecords().foreach(system.out::println);
}querywrapper是mybatis plus中实现查询的对象封装操作类.。
lambdaquerywrapper<billchargeinfo> wrapper = new lambdaquerywrapper<>();
wrapper.eq(billchargeinfo::gettransactionno, transactionno);
wrapper.last("limit 1");
return basemapper.selectone(wrapper);注意这是因为我的mybatisplus的版本是3.2版本,只有3.x的版本才有querywrapper,而如果是2.x版本,是没有querywrapper的,如果找不到querywrapper,可以在配置里将mybatisplus的版本改为3.x
<mybatis-plus.version>2.2.0</mybatis-plus.version>
或者使用entitywrapper,两者的功能其实差不多。
条件参数说明


二、mybatis plus 集成 spring
数据表结构
create table user
(
id bigint(20) not null comment '主键id',
name varchar(30) null default null comment '姓名',
age int(11) null default null comment '年龄',
email varchar(50) null default null comment '邮箱',
primary key (id)
);
insert into user (id, name, age, email) values
(1, 'jone', 18, 'test1@baomidou.com'),
(2, 'jack', 20, 'test2@baomidou.com'),
(3, 'tom', 28, 'test3@baomidou.com'),
(4, 'sandy', 21, 'test4@baomidou.com'),
(5, 'billie', 24, 'test5@baomidou.com');创建一个 springboot 项目。直接使用 ide 工具创建一个。 spring initializer。从 spring boot 3 开始,官方不再支持 java 8 和 java 11
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>2.6.3</version> <!-- 使用支持的版本号 -->
</parent>
注意选择type,jdk等.

输入group、artifact、package的对应的信息,再点击next

注意:这里必须得选择web,否则demo不能使用controller层。 在我们的实际项目中,最好还是要引用spring-boot release版本的
添加 mybatis-plus 依赖(mybatis-plus-boot-starter)
<dependency>
<groupid>com.baomidou</groupid>
<artifactid>mybatis-plus-boot-starter</artifactid>
<version>3.3.1</version>
</dependency>使用 mysql,需要引入 mysql 相关依赖。为了简化代码,引入 lombok 依赖
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
</dependency>
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<version>1.18.10</version>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupid>org.mapstruct</groupid>
<artifactid>mapstruct</artifactid> <!-- use mapstruct-jdk8 for java 8 or higher -->
</dependency>
<dependency>
<groupid>org.mapstruct</groupid>
<artifactid>mapstruct-jdk8</artifactid>
</dependency>
<!-- 测试相关 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
</dependency>完整pom.xml 文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>2.2.6.release</version>
<relativepath/> <!-- lookup parent from repository -->
</parent>
<groupid>com.lyh.test</groupid>
<artifactid>test-mybatis-plus</artifactid>
<version>0.0.1-snapshot</version>
<name>test-mybatis-plus</name>
<description>测试 -- 测试 mybatis-plus 功能</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupid>com.baomidou</groupid>
<artifactid>mybatis-plus-boot-starter</artifactid>
</dependency>
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
</dependency>
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<version>1.18.10</version>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter</artifactid>
<version>3.3.1</version>
</dependency>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-test</artifactid>
<scope>test</scope>
<exclusions>
<exclusion>
<groupid>org.junit.vintage</groupid>
<artifactid>junit-vintage-engine</artifactid>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-maven-plugin</artifactid>
</plugin>
</plugins>
</build>
</project>mysql数据源
在 resources/application.yml 文件中配置 mysql 数据源信息。
server:
port: 2525
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/testmybatisplus?useunicode=true&characterencoding=utf8
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
jdbc-type-for-null: null该配置表示 mybatis-plus 会在 resources/mapper/ 目录下查找所有的 .xml 文件作为 mapper 映射文件 src/main/resources/mapper/*mapper.xml
三、快速体验 mybatis plus
实体类
package entity;
import lombok.data;
@data
public class user {
private long id;
private string name;
private int age;
private string email;
}mapper 接口,这是 mybatis-plus 封装好的类。
@select @update
package mapper;
import entity.user;
import org.apache.ibatis.annotations.param;
import org.apache.ibatis.annotations.select;
import com.baomidou.mybatisplus.core.conditions.wrapper;
import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.baomidou.mybatisplus.core.toolkit.constants;
import java.util.list;
public interface usermapper extends basemapper<user> {
//@select("select * from user where name = #{name}")
//list<user> selectbyname(@param("name") string name);
@update({"update user set supplier_fee_id=null,category_id=#{categoryid},supplier_id=#{supplierid} where number_type=#{numbertype} and number=#{phone}"})
boolean updateuserbyphone(@param("phone") string phone, @param("numbertype") integer numbertype, @param("supplierid") integer supplierid, @param("categoryid") integer categoryid);
//传入对象参数xml方式
//list<user> selectallusers(@param("user") user user, @param("bm") string bm);
}实体类、mapper 类都写好了,就可以使用了。接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@select @update 等注解里面包含 sql 语句来绑定,另外一种就是通过xml 里面写 sql 来绑定,在这种情况下,要指定 xml 映射文件里面的namespace 必须为接口的全路径名。当 sql 语句比较简单时候,用注解绑定;当 sql 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多
添加usermapper.xml文件(可省略)
对于$和#的用法区别
简单来说 #{} 会在将参数加上引号,而${}并不会在给参数加上引号。mybatis对参数没有进行任何的处理。通常${}用于group by,order by,in等的后面
但是,实际应用中,并不提倡使用 ${},因为使用 #{} 写法,除了可以防止sql注入以外,还能在参数里含有单引号的时候自动转义
<?xml version="1.0" encoding="utf-8" ?>
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="usermapper">
<select id="findusercount" resulttype="java.lang.integer">
select count(1) from user
</select>
<select id="selectallusers" resultmap="baseresultmap">
select * from user where number_type = ${@net.test.api.constants.productenum@phone400.type}
<if test="user.name != null and user.name != ''">
and name like '%${user.name}%'
</if>
<if test="user.starttime != null ">
and start_time>='${user.starttime} 00:00:00' and <![cdata[ start_time<='${user.starttime} 23:59:59' ]]>
</if>
</select>
</mapper>serviceimpl层 通用的iservice实现crud功能
@service
public class userserviceimpl extends serviceimpl<usermapper, user> implements userservice {
@autowired
private usermapper usermapper;
public integer getusernum() {
return usermapper.findusercount();
}
// 增
@override
public int insertuser(user user) {
return insert(user);
}
// 改
@override
public int updateuser(user user) {
return updatebyid(user);
}
// 删
@override
public int deleteuser(user user) {
integer id = user.getuserid();
return deletebyid(id);
}
// 查
@override
public user finduserbyname(string username) {
return basemapper.getuserbyname(username);
}
// 分页查
@override
public ipage getuserpage(page page, user user) {
return basemapper.getuserspage( page, user );
}
}service层
public interface userservice extends iservice<user> {
integer getusernum();
int insertuser( user user );
int updateuser( user user );
int deleteuser( user user );
user finduserbyname( string username );
ipage getuserpage( page page, user user );step1:启动类里扫描 mapper 类,即添加 @mapperscan 注解
package com.lyh.test.testmybatisplus;
import org.mybatis.spring.annotation.mapperscan;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@mapperscan({"com.kfit.*.mapper","org.kfit.*.mapper"})
@springbootapplication
public class testmybatisplusapplication {
public static void main(string[] args) {
springapplication.run(testmybatisplusapplication.class, args);
}
}多module项目扫描失败解决: consider defining a bean of type 'xxx' in your configuration
@springbootapplication(scanbasepackages = {"com.xx.xx.xx.a", "com.xx.xx.common"})step2:写一个测试类测试一下。
package com.lyh.test.testmybatisplus;
import entity.user;
import mapper.usermapper;
import com.baomidou.mybatisplus.core.conditions.query.lambdaquerywrapper;
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 testmybatisplusapplicationtests {
@autowired
private usermapper usermapper;
@test
public void testselect() {
system.out.println(("----- selectall method test ------"));
list<user> userlist = usermapper.selectlist(null);
for(user user:userlist) {
system.out.println(user);
}
}
@test
public void testselectbysql() {
lambdaquerywrapper<user> wrapper = new lambdaquerywrapper();
wrapper.like("name" , "k").lt("age", 40).last("limit 4");
list<user> userlist = usermapper.selectbysql(wrapper);
userlist.foreach(system.out::println);
}
}
上面几个例子中,并没有在 usermapper 接口中定义任何方法,也没有在配置文件中编写 sql 语句,而是通过继承 basemapper 接口获得通用的的增删改查方法,复杂的 sql 也可以使用条件构造器拼接。通过这两种方式已经能够满足很多的开发需求了,不过复杂的业务需求还是要编写 sql 语句的,流程和 mybatis 一样。
若遇到 @autowired 标记的变量出现 红色下划线,但是不影响 正常运行。可以进入 settings,找到 inspection,并选择其中的 spring core -> code -> autowiring for bean class,将 error 改为 warning,即可。

四、代码生成器
package com.example.demo;
import com.baomidou.mybatisplus.generator.autogenerator;
import com.baomidou.mybatisplus.generator.config.datasourceconfig;
import com.baomidou.mybatisplus.generator.config.globalconfig;
import com.baomidou.mybatisplus.generator.config.packageconfig;
import com.baomidou.mybatisplus.generator.config.strategyconfig;
import com.baomidou.mybatisplus.generator.config.rules.namingstrategy;
/**
* mybatisplus代码生成器
*/
public class mybatisplusgeneratorrunner {
public static final string mysql_driver = "com.mysql.cj.jdbc.driver";
//模块名 如果有模块名,则需在模块名前加. 例:log
public static final string module_name = "";
public static final string author = "danny";
public static final string mysql_url = "jdbc:mysql://locallhost:3306/test?servertimezone=utc";
public static final string username = "root";
public static final string password = "0rb!t";
public static final string[] db_table_names = {"user", "role"};
public static final string package_name = "cn.kerninventory.mybatis";
public static void main(string[] args) {
autogenerator generator = new autogenerator();
//配置数据库连接参数
datasourceconfig datasourceconfig = new datasourceconfig();
datasourceconfig.setdrivername(mysql_driver);
datasourceconfig.seturl(mysql_url);
datasourceconfig.setusername(username);
datasourceconfig.setpassword(password);
generator.setdatasource(datasourceconfig);
//配置文件生成路径参数
packageconfig packageconfig = new packageconfig();
packageconfig.setmodulename(module_name);
packageconfig.setparent(package_name);
packageconfig.setentity("entity");
packageconfig.setxml("xml");
packageconfig.setmapper("mapper");
//packageconfig.setserviceimpl("serviceimpl");
packageconfig.setservice("service");
packageconfig.setcontroller("controller");
generator.setpackageinfo(packageconfig);
//策略配置
strategyconfig strategyconfig = new strategyconfig();
//表名转驼峰
strategyconfig.setnaming(namingstrategy.underline_to_camel);
//字段驼峰命名
strategyconfig.setcolumnnaming(namingstrategy.underline_to_camel);
//set方法builder模式
strategyconfig.setchainmodel(true);
//使用lombok注解
strategyconfig.setentitylombokmodel(true);
//不生成serial version uuid
//strategyconfig.setentityserialversionuid(false);
//要生成的表名
strategyconfig.setinclude(db_table_names);
//rest风格
strategyconfig.setrestcontrollerstyle(true);
//是否生成字段常量
strategyconfig.setentitycolumnconstant(true);
//是否生成实体时,生成字段注解
strategyconfig.setentitytablefieldannotationenable(true);
generator.setstrategy(strategyconfig);
//全局配置
globalconfig globalconfig = new globalconfig();
globalconfig.setservicename("%sservice");
globalconfig.setxmlname("mapper");
globalconfig.setbaseresultmap(true); //xml中的resultmap标签
globalconfig.setbasecolumnlist(true); //xml标签
globalconfig.setopen(false); //不弹出生成目录
//输出目录
globalconfig.setoutputdir(system.getproperty("user.dir") + "/src/main/java");
globalconfig.setauthor(author);
generator.setglobalconfig(globalconfig);
generator.execute();
}
}五、@tablefield注解
- @tablefield(exist = false) 注解加载bean属性上,表示当前属性不是数据库的字段,但在项目中必须使用,这样在新增等使用bean的时候,mybatis-plus就会忽略这个,不会报错
- @tablefield(.. , update="%s+1") 其中 %s 会填充为字段。输出 sql 为:update 表 set 字段=字段+1 @tablefield(.. , update="now()") 输出 sql 为:update 表 set 字段=now() where
- @tablefield(condition = sqlcondition.like) 输出 sql 为:select 表 where name like concat('%',值,'%')
- @tablefield(fill = fieldfill.insert)插入自动填充,fieldfill.insert_update在插入和更新操作时的自动填充 private date updatetime;
- @tablefield("`inout`") 避免关键字冲突
- @tablefield(select = false) 把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。如:密码
- @tablefield(value = "user_name")
private string username; setsqlselect("user_name")可能识别不了。换成setsqlselect("user_name as username")
@tablename注解
- @tablename(value = "my_table_name") 表名和类名不一致时指定
mybatis-plus批量插入
savebatch方法分析:底层也是通过for来完成,默认是一个事务一次提交n条数据。即伪批量性能比较差。引入依赖,3.4.0之上的版本都可以
<dependency>
<groupid>com.baomidou</groupid>
<artifactid>mybatis-plus-boot-starter</artifactid>
<version>3.5.2</version>
</dependency>insertbatchsomecolumn
自定义sql注入器
import com.baomidou.mybatisplus.annotation.fieldfill;
import com.baomidou.mybatisplus.core.injector.abstractmethod;
import com.baomidou.mybatisplus.core.metadata.tableinfo;
import com.baomidou.mybatisplus.extension.injector.methods.insertbatchsomecolumn;
import com.github.yulichang.injector.mpjsqlinjector;
import java.util.list;
/**
* mybatis-plus-join兼容批量插入
*
* @author hudy
*/
public class insertbatchsqlinjector extends mpjsqlinjector {
/**
* 获取方法列表
*
* @param mapperclass mapper 类
* @param tableinfo 表信息
* @return 方法列表
*/
@override
public list<abstractmethod> getmethodlist(class<?> mapperclass, tableinfo tableinfo) {
// 保留 mybatis plus-join 自带的方法
list<abstractmethod> methodlist = super.getmethodlist(mapperclass, tableinfo);
// 添加自定义方法:批量插入,方法名为 insertbatchsomecolumn
// 过滤掉仅在更新时填充的字段
methodlist.add(new insertbatchsomecolumn(i -> i.getfieldfill() != fieldfill.update));
return methodlist;
}
}版本2:注意根据plus的版本api不同选择
import com.baomidou.mybatisplus.annotation.fieldfill;
import com.baomidou.mybatisplus.core.injector.abstractmethod;
import com.baomidou.mybatisplus.core.injector.defaultsqlinjector;
import com.baomidou.mybatisplus.extension.injector.methods.insertbatchsomecolumn;
import java.util.list;
/**
* @author hudeyong 批量插入 sql 注入器.
* @date 2025/7/24
*/
public class insertbatchsqlinjector extends defaultsqlinjector {
@override
public list<abstractmethod> getmethodlist(class<?> mapperclass) {
// super.getmethodlist() 保留 mybatis plus 自带的方法
list<abstractmethod> methodlist = super.getmethodlist(mapperclass);
// 添加自定义方法:批量插入,方法名为 insertbatchsomecolumn
methodlist.add(new insertbatchsomecolumn(i -> i.getfieldfill() != fieldfill.update));
return methodlist;
}
}在mybatis-plus配置类中注册该注入器
/**
* 自定义批量插入 sql 注入器.
*/
@bean
public insertbatchsqlinjector insertbatchsqlinjector() {
return new insertbatchsqlinjector();
}到此定义完毕,继承下面定义的 mybasemapper,你就可以撒手不管了,直接调用就行。或者直接在serviceimpl通过mapper调用insertbatchsomecolumn,然后alt+回车生成此方法
import com.github.yulichang.base.mpjbasemapper;
import java.util.list;
import org.apache.ibatis.annotations.param;
/**
* mpjbasemapper 是mybatis-plus-join插件提供的mapper接口
*
* @author hudeyong
* @date 2025/7/24
*/
public interface mybasemapper<t> extends mpjbasemapper<t> {
/**
* 执行批量插入操作,针对指定列表中的对象插入数据库。 此方法专注于通过批量操作提高数据插入的效率,而不是逐个插入对象。这有助于减少数据库访问次数,从而提高性能。
*
* @param batchlist 待插入数据库的对象列表。列表中的每个对象都将被插入到数据库中, 注意,对象的属性和数据库表的字段需要对应,以便正确插入数据。
* @return 返回插入操作影响的行数。这可以用于判断插入操作的成功与否,或者了解操作的范围。
*/
int insertbatchsomecolumn(@param("list") list<t> batchlist);
}baseservice
import java.util.arraylist;
import java.util.list;
public class baseserviceimpl<m extends mybasemapper<t>, t> extends serviceimpl<m, t> implements
imyservice<t> {
@override
public int insertbatchsomecolumn(list<t> entitylist) {
return this.basemapper.insertbatchsomecolumn(entitylist);
}
@override
public int insertbatchsomecolumn(list<t> entitylist, int batchsize) {
int size = entitylist.size();
if (size < batchsize) {
return this.basemapper.insertbatchsomecolumn(entitylist);
}
int page = 1;
if (size % batchsize == 0) {
page = size / batchsize;
} else {
page = size / batchsize + 1;
}
for (int i = 0; i < page; i++) {
list<t> sub = new arraylist<>();
if (i == page - 1) {
sub = entitylist.sublist(i * batchsize, entitylist.size());
} else {
sub.sublist(i * batchsize, (i + 1) * batchsize);
}
if (sub.size() > 0) {
this.basemapper.insertbatchsomecolumn(sub);
}
}
return size;
}
}测试代码,调用insertbatchsomecolumn方法
@test
void testinsertbatch() {
list<user> users = new arraylist<>();
for (int i = 0; i < 3; i++) {
user user = new user();
user.setname("犬小哈" + i);
user.setage(i);
user.setgender(1);
users.add(user);
}
usermapper.insertbatchsomecolumn(users);
}mybatis-plus-join用法
mybatis plus 封装的 mapper 不支持 join,如果需要支持就必须自己去实现。但是对于大部分的业务场景来说,都需要多表 join。mybatis-plus-join(mpj)框架提供的基础 mapper 接口,需要 mybatis-plus version >= 3.4.0。它是对 mybatis-plus 的 basemapper 的扩展,主要增强了以下功能:
- 联表查询支持:提供了便捷的多表关联查询能力
- 复杂查询构建:支持更复杂的 sql 查询构建
- 继承 basemapper:保留了 basemapper 的所有基础功能。
安装
- maven
<dependency>
<groupid>com.github.yulichang</groupid>
<artifactid>mybatis-plus-join</artifactid>
<version>1.4.5</version>
</dependency>使用
- mapper继承mpjbasemapper (必选)
- service继承mpjbaseservice (可选)
- serviceimpl继承mpjbaseserviceimpl (可选)
查询例子
// 多表连接查询
mpjlambdawrapper<hoteljob> wrapper = new mpjlambdawrapper<hoteljob>()
.selectall(hoteljob.class)
.select(hotel::gethotelname, city::getcityname)
.leftjoin(hotel.class, hotel::getid, hoteljob::gethotelid)
.leftjoin(city.class, city::getid, hotel::getcityid)
.eq(hotel::getstatus, 1)
.eq(city::getisactive, true)
.groupby(hoteljob::getjobtype)
.having(hoteljob::getjobtype, ">", 5);
list<hoteljob> result = mapper.selectjoinlist(hoteljob.class, wrapper);
// 聚合函数使用
mpjlambdawrapper<hoteljob> wrapper = new mpjlambdawrapper<hoteljob>()
.selectsum(hoteljob::getsalary, "totalsalary")
.selectcount(hoteljob::getid, "jobcount")
.selectas(hotel::gethotelname, "hotelname")
.leftjoin(hotel.class, hotel::getid, hoteljob::gethotelid)
.groupby(hotel::getid);
list<map<string, object>> result = mapper.selectjoinmaps(wrapper);
其他
java计时stopwatch的使用方法详解
stopwatch 是位于 org.springframework.util 包下的一个工具类,通过它可方便的对程序部分代码进行计时(ms级别),适用于同步单线程代码块。简单总结一句,spring提供的计时器stopwatch对于秒、毫秒为单位方便计时的程序,尤其是单线程、顺序执行程序的时间特性的统计输出支持比较好。也就是说假如我们手里面有几个在顺序上前后执行的几个任务,而且我们比较关心几个任务分别执行的时间占用状况,希望能够形成一个不太复杂的日志输出,stopwatch提供了这样的功能。而且spring的stopwatch基本上也就是仅仅为了这样的功能而实现。
想要使用它,首先你需要在你的 maven 中引入 spring 核心包,当然 spring mvc 和 spring boot 都已经自动引入了该包:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
<version>${spring.version}</version>
</dependency>对一切事物的认知,都是从使用开始,那就先来看看它的用法,会如下所示:
public static void main(string[] args) throws interruptedexception {
stopwatch stopwatch = new stopwatch();
// 任务一模拟休眠3秒钟
stopwatch.start("taskonename");
thread.sleep(1000 * 3);
system.out.println("当前任务名称:" + stopwatch.currenttaskname());
stopwatch.stop();
// 任务一模拟休眠10秒钟
stopwatch.start("tasktwoname");
thread.sleep(1000 * 10);
system.out.println("当前任务名称:" + stopwatch.currenttaskname());
stopwatch.stop();
// 任务一模拟休眠10秒钟
stopwatch.start("taskthreename");
thread.sleep(1000 * 10);
system.out.println("当前任务名称:" + stopwatch.currenttaskname());
stopwatch.stop();
// 打印出耗时
system.out.println(stopwatch.prettyprint());
system.out.println(stopwatch.shortsummary());
// stop后它的值为null
system.out.println(stopwatch.currenttaskname());
// 最后一个任务的相关信息
system.out.println(stopwatch.getlasttaskname());
system.out.println(stopwatch.getlasttaskinfo());
// 任务总的耗时 如果你想获取到每个任务详情(包括它的任务名、耗时等等)可使用
system.out.println("所有任务总耗时:" + sw.gettotaltimemillis());
system.out.println("任务总数:" + sw.gettaskcount());
system.out.println("所有任务详情:" + sw.gettaskinfo());
}hutool工具
hutool是一个小而全的java工具类库,通过静态方法封装,降低相关api的学习成本,提高工作效率,使java拥有函数式语言般的优雅。hutool是对文件、流、加密解密、转码、正则、线程、xml等jdk方法进行封装,组成各种util工具类。
在项目的pom.xml的dependencies中加入以下内容:
<dependency>
<groupid>cn.hutool</groupid>
<artifactid>hutool-all</artifactid>
<version>5.5.2</version>
</dependency>禁用 netflix-eureka 客户端
- spring cloud eureka主要负责实现微服务架构中的服务治理功能
- springcloudgateway作为springcloud生态系中的网关,为微服务架构提供统一的路由管理,并且根据http请求进行相应的匹配、断言、过滤
eureka.client.enabled=false
事务处理
public class transactionstatusutils {
@autowired
platformtransactionmanager datasourcetransactionmanager;
public transactionstatus gettransactionstatus(datasourcetype dbtype){
dbcontextholder.setdbtype(dbtype);
defaulttransactiondefinition transactiondefinition = new defaulttransactiondefinition();
transactiondefinition.setpropagationbehavior(defaulttransactiondefinition.propagation_requires_new);
transactionstatus transactionstatus = datasourcetransactionmanager.gettransaction(transactiondefinition);
return transactionstatus;
}
public void commit(transactionstatus transactionstatus) {
datasourcetransactionmanager.commit(transactionstatus);
}
public void rollback(transactionstatus transactionstatus) {
datasourcetransactionmanager.rollback(transactionstatus);
}
}mybatisplus 获取实体表名或字段名
mybatis plus 通过实体的原理是通过tableid,tablefield,tablename等注解或属性名称/类名称转下划线实现的
//表名
sqlhelper.table(user.class).gettablename();
//主键
sqlhelper.table(user.class).getkeycolumn();
//获取username的数据库字段
wrapperkit.gettablefieldinfobyproperty(user.class, "username").getcolumn();
import com.baomidou.mybatisplus.entity.tablefieldinfo;
import com.baomidou.mybatisplus.entity.tableinfo;
import com.baomidou.mybatisplus.toolkit.tableinfohelper;
import java.util.list;
public class wrapperkit {
/**
* 获取主键的实体属性名
*
* @param cls
* @return
*/
public static string gettableidproperty(class<?> cls) {
return tableinfohelper.gettableinfo(cls).getkeyproperty();
}
public static list<tablefieldinfo> gettablefieldinfo(class<?> cls) {
tableinfo tableinfo = tableinfohelper.gettableinfo(cls);
return tableinfo.getfieldlist();
}
public static tablefieldinfo gettablefieldinfobyproperty(class<?> cls, string property) {
final list<tablefieldinfo> tablefieldinfolist = gettablefieldinfo(cls);
for (tablefieldinfo tablefieldinfo : tablefieldinfolist) {
if (tablefieldinfo.getproperty().equals(property)) {
return tablefieldinfo;
}
}
if (!gettableidproperty(cls).equals(property)) {
system.out.println(cls.tostring() + " 属性" + property + "不存在,或属性没有配置@tablefield");
} else {
system.out.println(property + "主键不支持,因为@tableid不会加入到fieldlist");
}
return null;
}
public static <t> void appenddynamicequal(wrapper<t> wrapper, object model, string... attrs) {
tableinfo tableinfo = tableinfohelper.gettableinfo(model.getclass());
list<tablefieldinfo> fieldlist = tableinfo.getfieldlist();
map<string, tablefieldinfo> collect = fieldlist.stream()
.collect(collectors.tomap(tablefieldinfo::getproperty, account -> account));
for (string attr : attrs) {
string[] attrarray = attr.split(":");
// 最后一个就是真实的
string reallyattrname = attrarray[attrarray.length - 1];
object attrvalue = reflectionkit.getmethodvalue(model, reallyattrname);
if (attrvalue == null) {
continue;
}
if (string.class.equals(attrvalue.getclass()) && strkit.isempty((string) attrvalue)) {
continue;
}
tablefieldinfo tablefieldinfo = collect.get(reallyattrname);
if (attrarray.length > 1) {
switch (attrarray[0]) {
case "in":
wrapper.in(tablefieldinfo.getcolumn(), attrvalue.tostring());
break;
case "like":
wrapper.like(tablefieldinfo.getcolumn(), attrvalue.tostring());
break;
case "notlike":
wrapper.notlike(tablefieldinfo.getcolumn(), attrvalue.tostring());
break;
default:
wrapper.eq(tablefieldinfo.getcolumn(), attrvalue);
break;
}
} else {
wrapper.eq(tablefieldinfo.getcolumn(), attrvalue);
}
}
}
}
更新不了空字符串或null问题
- 使用 fieldstrategy.ignored 允许设置 null 值
- 使用 fieldstrategy.not_empty 会忽略 null 和空字符串
- 使用 fieldstrategy.not_null 只会忽略 null 值
@tablefield(strategy = fieldstrategy.ignored)
the valid characters are defined in rfc 7230 and rfc 3986
在yml中配置
server:
tomcat:
relaxedquerychars: <,>, [,],^,`,{,|,} #参数内容
relaxedpathchars: <,>, [,],^,`,{,|,} #参数名称总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论