文章
问答
冒泡
Mybatis-plus 租户插件拓展

上一篇文章写了mybatis-plus插件的初步使用,但是使用仍不方便,例如需要过滤的表只能写死或者写配置文件,非常的不方便而且不可控,于是我想到可以使用自定义注解来统一管控受控制的表.那么就尝试实现吧.

实现方式:

1、 先写一个自定义注解,表示被打上此注解的entity受多租户插件管控.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface TenantLine {
}

2、定义一个baseEntity ,提供租户字段,并且打上此注解

@Data
@TenantLine
public class BaseTenantEntity extends BaseEntity {

    private Long tenantId;
}

3、写一个方法,扫描包里的所有拥有@TenantLine注解以及@TableName注解的类,再获取@TableName注解的value来统一添加表名

public static void getTableNames(){
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Set<String> tableNames = Sets.newHashSet();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath("com.dane") + "/**/*.class";
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                TableName tableName = null;
                TenantLine tenantLine = null;
                //判断当前class及父类是否有指定主解
                while (clazz != null) {
                    if (tableName == null) {
                        tableName = clazz.getAnnotation(TableName.class);
                    }
                    if (tenantLine == null) {
                        tenantLine = clazz.getAnnotation(TenantLine.class);
                    }
                    clazz = clazz.getSuperclass();
                }
                if (tableName != null && tenantLine != null) {
                    tableNames.add(tableName.value());
                }
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        tables.addAll(tableNames);
    }

4、在多租户插件的类中,提供此静态方法,并且在构造中加载这些受管控的类.

public class MyTenantLineHandler implements TenantLineHandler {

    public MyTenantLineHandler() {
        getTableNames();
    }

    private static Set<String> tables = Sets.newHashSet();

    @Override
    public Expression getTenantId() {
        Long tenantId = UserUtil.getTenantId();
        return tenantId == null ? new LongValue(1) : new LongValue(tenantId);
    }

    @Override
    public String getTenantIdColumn() {
        return "tenant_id";
    }

    @Override
    public boolean ignoreTable(String tableName) {
        return !tables.contains(tableName);
        // return TenantLineHandler.super.ignoreTable(tableName);
    }

    public static void getTableNames(){
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Set<String> tableNames = Sets.newHashSet();
        try {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath("com.dane") + "/**/*.class";
            Resource[] resources = resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource resource : resources) {
                //用于读取类信息
                MetadataReader reader = readerfactory.getMetadataReader(resource);
                //扫描到的class
                String classname = reader.getClassMetadata().getClassName();
                Class<?> clazz = Class.forName(classname);
                TableName tableName = null;
                TenantLine tenantLine = null;
                //判断是否有指定主解
                while (clazz != null) {
                    if (tableName == null) {
                        tableName = clazz.getAnnotation(TableName.class);
                    }
                    if (tenantLine == null) {
                        tenantLine = clazz.getAnnotation(TenantLine.class);
                    }
                    clazz = clazz.getSuperclass();
                }
                if (tableName != null && tenantLine != null) {
                    tableNames.add(tableName.value());
                }
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        tables.addAll(tableNames);
    }
}

5、实体类继承基础BaseTenantEntity类.

@Data
@TableName("sys_user")
public class SysUserEntity extends BaseTenantEntity {
    private String username;
    private String password;
}

6、测试

6.1 开启多租户

如图,此表已经受管控了

6.2 关闭多租户(取消继承baseTenantEntity父类)

如图,这时候sys_user表是不受管控的.

mybatis-plus的多租户插件还是非常强大的,依靠mybatis的jsqlparser来添加多租户,实测下来,无论是mybatis-plus的基础方法还是自定义sql,都能进行不错的多租户支持.

Mybatis-plus
多租户

关于作者

Dane.shang
快30岁了还没去过酒吧
获得点赞
文章被阅读