当前位置: 代码网 > it编程>编程语言>Java > Mybatis-Plus多租户插件&属性自动赋值方式

Mybatis-Plus多租户插件&属性自动赋值方式

2026年05月14日 Java 我要评论
1、mybatis-plus 多租户插件tenantlineinnerinterceptor 是 mybatis-plus 提供的一个插件,用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能

1、mybatis-plus 多租户插件

tenantlineinnerinterceptor 是 mybatis-plus 提供的一个插件,用于实现多租户的数据隔离。通过这个插件,可以确保每个租户只能访问自己的数据,从而实现数据的安全隔离。

其实就是一个拦截器,用于进行sql 增删改查 时自动添加租户字段

1.1、属性介绍

tenantlineinnerinterceptor 的关键属性是 tenantlinehandler,它是一个 tenantlinehandler 接口的实例,用于处理租户相关的逻辑。

属性名类型默认值描述
tenantlinehandlertenantlinehandler租户处理器( tenantid 行级 )

tenantlinehandler 接口定义了以下方法:

public interface tenantlinehandler {

    /**
     * 获取租户 id 值表达式,只支持单个 id 值
     *
     * @return 租户 id 值表达式
     */
    expression gettenantid();

    /**
     * 获取租户字段名
     * 默认字段名叫: tenant_id
     *
     * @return 租户字段名
     */
    default string gettenantidcolumn() {
        return "tenant_id";//默认
    }

    /**
     * 根据表名判断是否忽略拼接多租户条件
     * 默认都要进行解析并拼接多租户条件
     *
     * @param tablename 表名
     * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
     */
    default boolean ignoretable(string tablename) {
        return false;
    }

    /**
     * 忽略插入租户字段逻辑
     *
     * @param columns        插入字段
     * @param tenantidcolumn 租户 id 字段
     * @return
     */
    default boolean ignoreinsert(list<column> columns, string tenantidcolumn) {
        return columns.stream().map(column::getcolumnname).anymatch(i -> i.equalsignorecase(tenantidcolumn));
    }
}

1.2、使用多租户插件

比方我有一张表biz_archive_common

-- security_manager.biz_archive_common definition
create table `biz_archive_common` (
  `id` bigint(20) not null auto_increment comment '数据主键id',
  `title_name` varchar(255) default null comment '题名',
  `secrecy_level_id` bigint(20) default null comment '密级id',
  `archive_num` varchar(510) default null comment '档号(照片号)',
  `roll_num` varchar(31) default null comment '案卷号(册号/带号)',
  `abandon` tinyint(1) not null default '0' comment '是否废弃 默认 0false/1true',
  `del` varchar(200) not null default '0' comment '是否删除 默认 0false/1true',
  `create_user` varchar(31) default null comment '创建者账户',
  `create_time` datetime default null comment '创建时间',
  `update_user` varchar(31) default null comment '更新者账户',
  `update_time` datetime default null comment '更新时间',
  `archive_company_id` bigint(20) default null comment '全宗单位id',
  primary key (`id`) using btree,
  key `indexarchivecompanyid` (`archive_company_id`) using btree
) engine=innodb default charset=utf8mb4 row_format=dynamic comment='档案共有信息表';

maven

<dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!--mybatisplus依赖-->
        <dependency>
            <groupid>com.baomidou</groupid>
            <artifactid>mybatis-plus-spring-boot3-starter</artifactid>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>druid-spring-boot-starter</artifactid>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <!--lombok依赖-->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
        </dependency>
    </dependencies>

yml

server:
  port: 8001
  #address: 127.0.0.1
#spring数据源配置
spring:
  application:
    name: token #项目名
  # 数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.driver
    url: jdbc:mysql://localhost:3306/security_manager?servertimezone=gmt%2b8&useunicode=true&usessl=false&characterencoding=utf-8
    username: root
    password: root
    druid:
      initial-size: 20
      min-idle: 20
      max-active: 100
      max-wait: 10000
      time-between-eviction-0runs-millis: 60000
      min-evictable-idle-time-millis: 30000
      validation-query: select 1 from dual
      test-while-idle: true
      test-on-borrow: true
      test-on-return: true
# mybatis-plus配置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  configuration:
    map-underscore-to-camel-case: true # 数据库下划线自动转驼峰标示关闭
    log-impl: org.apache.ibatis.logging.stdout.stdoutimpl #日志配置
  mapper-locations: classpath*:/mapper/**/*.xml

threadlocalutil

package cn.js.util;

import java.util.hashmap;
import java.util.map;

/**
 * description:
 *
 * @author js
 * @create 2024-11-17 14:12
 * @version 1.0
 */
public class threadlocalutil {


    //1 初始化treadlocal
    private static threadlocal<map<string, object>> res = new threadlocal<map<string, object>>() {

        /**
         * 和继承threadlocal 类一样,也是一个方法的复写
         */
        protected map<string, object> initialvalue() {


            return new hashmap<string, object>();
        }

        ;
    };

    /*
     * 给线程里面设置一个值
     */
    public static void set(string name, object object) {

        map<string, object> map = res.get(); // 取出来的map 集合位null
        map.put(name, object);
    }

    /**
     * 从线程里面取值
     */
    public static object get(string name) {

        map<string, object> map = res.get();
        if (!map.containskey(name)) {


            return null;
        }
        return map.get(name);
    }

    /**
     * 清空线程的值
     */
    public static void clear() {
        map<string, object> map = res.get();
        map.clear();
        map = null; // jvm 自动回收

    }



}

实现 定义,注入租户处理器插件

package cn.js.config;

import cn.js.util.threadlocalutil;
import com.baomidou.mybatisplus.autoconfigure.mybatisplusautoconfiguration;
import com.baomidou.mybatisplus.extension.plugins.mybatisplusinterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.tenantlineinnerinterceptor;
import net.sf.jsqlparser.expression.expression;
import net.sf.jsqlparser.expression.longvalue;
import net.sf.jsqlparser.schema.column;
import org.springframework.boot.autoconfigure.autoconfigurebefore;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;

import java.util.arraylist;
import java.util.list;

/**
 * @author js
 * @description
 * @date 2024-11-15 21:34
 * @version 1.0
 **/
@configuration
@autoconfigurebefore(mybatisplusautoconfiguration.class)
public class paginationinterceptorconfig {
    @bean
    public mybatisplusinterceptor addmybatisplusinterceptor(){
        mybatisplusinterceptor interceptor = new mybatisplusinterceptor();
        interceptor.addinnerinterceptor(new tenantlineinnerinterceptor(new tenantlinehandler()));
        return interceptor;
    }
    

    private class tenantlinehandler implements com.baomidou.mybatisplus.extension.plugins.handler.tenantlinehandler{

        /**
         * 获取当前租户 id。
         */
        @override
        public expression gettenantid() {
            object id = threadlocalutil.get("id");
            long tenantid=long.valueof(string.valueof(id));
            // 返回租户id的表达式,longvalue 是 jsqlparser 中表示 bigint 类型的 class
            return new longvalue(tenantid);
        }


        /**
         * 表结构中那个字段用于拼接多租户条件
         */
        @override
        public string gettenantidcolumn() {
            return "archive_company_id";
        }


        /**
         * 默认返回false:表示所有表都需要拼接多租户条件
         * tablename:表名称
         */
        @override
        public boolean ignoretable(string tablename) {
            //如果那些表不需要拼接多租户条件,
            list<string> tablelist = new arraylist<>();
            tablelist.add("sql_version");
            tablelist.add("user");
            tablelist.add("kf");
            if(tablelist.contains(tablename)){
                //如果不需要添加的表名称在list中,就返回false,不用拼接租户条件
                return true;

            }
            return false;
        }


        /**
         * 获取租户 id 字段名。
         */
        @override
        public boolean ignoreinsert(list<column> columns, string tenantidcolumn) {
            return com.baomidou.mybatisplus.extension.plugins.handler.tenantlinehandler.super.ignoreinsert(columns, tenantidcolumn);
        }
    }

}

测试

package cn.js.controller;

import cn.js.domain.bizarchivecommon;
import cn.js.service.bizarchivecommonservice;
import cn.js.util.threadlocalutil;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

import javax.annotation.resource;
import java.util.list;

/**
 * @author js
 * @description
 * @date 2024-11-15 21:26
 * @version 1.0
 **/
@requestmapping("/common")
@restcontroller
public class bizarchivecommoncontroller {
    @resource
    private bizarchivecommonservice bizarchivecommonservice;

    @getmapping("/getall")
    public list<bizarchivecommon> getall() {

        threadlocalutil.set("id",1645);
        list<bizarchivecommon> archivecommons = bizarchivecommonservice.list();
        return archivecommons;
    }

}

domian
package cn.js.domain;

import com.baomidou.mybatisplus.annotation.fieldfill;
import com.baomidou.mybatisplus.annotation.tablefield;
import com.baomidou.mybatisplus.annotation.tablelogic;
import com.baomidou.mybatisplus.annotation.tablename;
import lombok.data;

import java.time.localdatetime;

/**
 * @author js
 * @description
 * @date 2024-11-15 21:22
 * @version 1.0
 **/
@data
@tablename(value="biz_archive_common")
public class bizarchivecommon {

    private long id;
    private string titlename;
    private long secrecylevelid;
    private string archivenum;
    private string rollnum;
    @tablelogic(value = "false", delval = "true")
    private boolean abandon;
    /**
     * 创建人
     */
    @tablefield(fill = fieldfill.insert)
    private string createuser;
    /**
     * 创建时间
     */
    @tablefield(fill = fieldfill.insert)
    private localdatetime createtime;
    /**
     * 修改人
     */
    @tablefield(fill = fieldfill.update)
    private string updateuser;
    /**
     * 修改时间
     */
    @tablefield(fill = fieldfill.update)
    private localdatetime updatetime;
    /**
     * 是否逻辑删除,true:删除 false:未删除
     */
    @tablelogic(value = "false", delval = "true")
    private boolean del;
    /**
     * 全宗单位id
     */
    private long archivecompanyid;

}

service & serviceimpl
package cn.js.service;

import cn.js.domain.bizarchivecommon;
import com.baomidou.mybatisplus.extension.service.iservice;

public interface bizarchivecommonservice extends iservice<bizarchivecommon> {
}



package cn.js.service.impl;

import cn.js.domain.bizarchivecommon;
import cn.js.mapper.bizarchivecommonmapper;
import cn.js.service.bizarchivecommonservice;
import com.baomidou.mybatisplus.extension.service.impl.serviceimpl;
import org.springframework.stereotype.service;

/**
 * @author js
 * @description
 * @date 2024-11-15 21:27
 * @version 1.0
 **/
@service
public class bizarchivecommonserviceimpl extends serviceimpl<bizarchivecommonmapper, bizarchivecommon> implements  bizarchivecommonservice {
}



mapper
package cn.js.mapper;

import cn.js.domain.bizarchivecommon;
import com.baomidou.mybatisplus.core.mapper.basemapper;
import org.apache.ibatis.annotations.mapper;



@mapper
public interface bizarchivecommonmapper extends basemapper<bizarchivecommon> {
}

jdbc connection [hikariproxyconnection@639980080 wrapping com.mysql.cj.jdbc.connectionimpl@7c12090] will not be managed by spring
==>  preparing: select id, title_name, secrecy_level_id, archive_num, roll_num, abandon, del, create_user, create_time, update_user, update_time, archive_company_id from biz_archive_common where archive_company_id = 1645
==> parameters: 
<==      total: 0
closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@38e12bd4]

测试mapper.xml 方式

<?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="cn.js.mapper.bizarchivecommonmapper">
    <select id="ones" resulttype="cn.js.domain.bizarchivecommon">
        select * from biz_archive_common where id=1
    </select>
</mapper>
@requestmapping("/common")
@restcontroller
public class bizarchivecommoncontroller {
    @resource
    private bizarchivecommonservice bizarchivecommonservice;

   
    @getmapping("/getone")
    public bizarchivecommon getones() {

        threadlocalutil.set("id",1645);
        bizarchivecommon archivecommon = bizarchivecommonservice.getones();
        return archivecommon;
    }

}

jdbc connection [hikariproxyconnection@1523711906 wrapping com.mysql.cj.jdbc.connectionimpl@6db5719b] will not be managed by spring
==>  preparing: select * from biz_archive_common where id = 1 and archive_company_id = 1645
==> parameters: 
<==      total: 0
closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@70ef05c0]

1.3、不使用多租户插件

可能我们并不是所有的sql语句都需要拼接租户条件,那该如何解决,只需要在相应的mapper接口上面添加注解

@interceptorignore(tenantline = "true")
package cn.js.mapper;

import cn.js.domain.bizarchivecommon;
import com.baomidou.mybatisplus.annotation.interceptorignore;
import com.baomidou.mybatisplus.core.mapper.basemapper;
import org.apache.ibatis.annotations.mapper;



@mapper
public interface bizarchivecommonmapper extends basemapper<bizarchivecommon> {
    @interceptorignore(tenantline = "true")
    bizarchivecommon ones();
}

2、实体对象的属性自动赋值

比方说表中有这个4个字段,我们在新增,修改的时候能不能自动插入,而不是每次,操作的时候我们给他插入

使用

1. 定义实体类

在实体类中,你需要使用 @tablefield 注解来标记哪些字段需要自动填充,并指定填充的策略。

public class user {
    @tablefield(fill = fieldfill.insert)
    private string createtime;

    @tablefield(fill = fieldfill.update)
    private string updatetime;

    // 其他字段...
}

2. 实现 metaobjecthandler

创建一个类来实现 metaobjecthandler 接口,并重写 insertfillupdatefill 方法。

package cn.js.config;

import com.baomidou.mybatisplus.core.handlers.metaobjecthandler;
import lombok.extern.slf4j.slf4j;
import org.apache.ibatis.reflection.metaobject;
import org.springframework.stereotype.component;

import java.time.localdatetime;

/**
 * description:
 *
 * @author js
 * @create 2024-11-17 15:39
 * @version 1.0
 */
@component
@slf4j
public class metaobjecthandlerconfig implements metaobjecthandler {
    @override
    public void insertfill(metaobject metaobject) {
       log.info("开始插入填充...");
       this.strictinsertfill(metaobject,"createtime", localdatetime.class,localdatetime.now());
       this.strictinsertfill(metaobject,"createuser", string.class,"张三");
    }

    @override
    public void updatefill(metaobject metaobject) {
        log.info("开始更新填充...");
        this.strictupdatefill(metaobject,"updatetime", localdatetime.class,localdatetime.now());
        this.strictupdatefill(metaobject,"updateuser", string.class,"王五");

    }
}

3. 配置自动填充处理器

确保你的 mymetaobjecthandler 实现类被 spring 管理,可以通过 @component@bean 注解来实现。

注意事项

  • 自动填充是直接给实体类的属性设置值。
  • 如果属性没有值,入库时会是 null
  • metaobjecthandler 提供的默认方法策略是:如果属性有值则不覆盖,如果填充值为 null 则不填充。
  • 字段必须声明 @tablefield 注解,并设置 fill 属性来选择填充策略。
  • 填充处理器需要在 spring boot 中声明为 @component@bean
  • 使用 strictinsertfillstrictupdatefill 方法可以根据注解 fieldfill.xxx、字段名和字段类型来区分填充逻辑。
  • 如果不需区分,可以使用 fillstrategy 方法。
  • update(t entity, wrapper<t> updatewrapper) 时,entity 不能为空,否则自动填充失效。
  • update(wrapper<t> updatewrapper) 时不会自动填充,需要手动赋值字段条件。

4.测试

package cn.js.controller;

import cn.js.domain.bizarchivecommon;
import cn.js.service.bizarchivecommonservice;
import cn.js.util.threadlocalutil;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

import javax.annotation.resource;
import java.util.list;

/**
 * @author js
 * @description
 * @date 2024-11-15 21:26
 * @version 1.0
 **/
@requestmapping("/common")
@restcontroller
public class bizarchivecommoncontroller {
    @resource
    private bizarchivecommonservice bizarchivecommonservice;

  

    @getmapping("/save")
    public boolean save() {

        threadlocalutil.set("id",1645);
        bizarchivecommon bizarchivecommon = new bizarchivecommon();
        bizarchivecommon.settitlename("这是新增的");
        bizarchivecommon.setsecrecylevelid(123l);
        bizarchivecommon.setarchivenum("8080-25201-38245");
        bizarchivecommon.setrollnum("25201");
        bizarchivecommon.setabandon(false);
        boolean archivecommon = bizarchivecommonservice.save(bizarchivecommon);
        return archivecommon;
    }



    @getmapping("/update")
    public boolean update() {

        threadlocalutil.set("id",1645);
        bizarchivecommon bizarchivecommon = new bizarchivecommon();
        bizarchivecommon.setid(1l);
        bizarchivecommon.settitlename("这是新增的,进行修改!");
        bizarchivecommon.setsecrecylevelid(123l);
        bizarchivecommon.setarchivenum("8080-25201-38245");
        bizarchivecommon.setrollnum("25201");
        bizarchivecommon.setabandon(false);
        boolean b = bizarchivecommonservice.updatebyid(bizarchivecommon);
        return b;
    }

}

新增
jdbc connection [hikariproxyconnection@1186756471 wrapping com.mysql.cj.jdbc.connectionimpl@22ef7a2e] will not be managed by spring
==>  preparing: insert into biz_archive_common (id, title_name, secrecy_level_id, archive_num, roll_num, abandon, create_user, create_time, archive_company_id) values (?, ?, ?, ?, ?, ?, ?, ?, 1645)
==> parameters: 1858059911531872257(long), 这是新增的(string), 123(long), 8080-25201-38245(string), 25201(string), false(boolean), 张三(string), 2024-11-17t16:09:38.650358300(localdatetime)
<==    updates: 1
closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@7b266740]

修改
jdbc connection [hikariproxyconnection@724111921 wrapping com.mysql.cj.jdbc.connectionimpl@5db6c17a] will not be managed by spring
==>  preparing: update biz_archive_common set title_name = ?, secrecy_level_id = ?, archive_num = ?, roll_num = ?, abandon = ?, update_user = ?, update_time = ? where id = ? and del = false and archive_company_id = 1645
==> parameters: 这是新增的,进行修改!(string), 123(long), 8080-25201-38245(string), 25201(string), false(boolean), 王五(string), 2024-11-17t16:22:59.641094300(localdatetime), 1(long)
<==    updates: 1
closing non transactional sqlsession [org.apache.ibatis.session.defaults.defaultsqlsession@5390448d]

总结

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

(0)

相关文章:

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

发表评论

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