当前位置: 代码网 > it编程>编程语言>Java > MyBatisPlus mybatis-plus-join使用及说明

MyBatisPlus mybatis-plus-join使用及说明

2026年04月26日 Java 我要评论
一、mybatis plus 介绍mybatis plus是国内人员开发的 mybatis 增强工具,在 mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。mybatisx 是一款基

一、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: <,>, [,],^,`,{,|,} #参数名称

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com