Запись пошагового выполнения решения Mybatis plus с несколькими арендаторами

задняя часть MyBatis

Запись пошагового выполнения решения Mybatis plus с несколькими арендаторами

Старый проект компании нужно трансформировать в мультитенант, поэтому он попал в большую яму.В этой статье написано о встречавшихся ямах и решениях.Каждый раз, когда возникает проблема, ее долго ищут в интернете, и это записывается, чтобы предотвратить его забвение в будущем.

(1). Схема

Есть много онлайн-программ, мы пишем только последнее, а именно: увеличенный идентификатор таблицы арендатора, для достижения изоляции данных

Вариант 1. Увеличьте идентификатор арендатора и вручную добавьте идентификатор арендатора во все места, где вызывается картограф.

Например:

LambdaQueryWrapper<Entity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Entity::getTenantId,"tenantId");
entityMapper.selectList(lambdaQueryWrapper);

Этот метод сложен, рабочая нагрузка велика, и его легко утечь. не принят

Вариант 2. Используйте официальный многопользовательский подключаемый модуль mp, код здесь опущен, перейдите к официальной документации, чтобы запросить

(2) Оптимизация и подводные камни официальной многопользовательской схемы

После принятия мультитенантного плагина официальной документации пуско-наладочные работы прошли очень гладко, а сырость прошла самотестирование.Я думал, что проблем нет, поэтому отправил тестовую среду, но с углублением тест, было обнаружено много проблем и нужно изменить. , перечисленные здесь:

1. Проанализируйте, что нужно арендаторам Cadogan, а что не добавляет

(1) Перезапись идентификатора арендатора Официальный метод переопределения по умолчанию:

@Override
public Expression getTenantId() {
    return null;
}

Здесь вам нужно определить, как получить идентификатор арендатора.

(2) Определение поля арендатора

private static final String TENANT_ID = "tenant_id";

@Override
public String getTenantIdColumn() {
    return TENANT_ID;
}

(3) Перехват арендатора

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

Решение, которое я использую здесь, это перехват имени таблицы, код выглядит следующим образом:

@Override
public boolean ignoreTable(String tableName) {

    /**
     * 此处的list,临时用作拦截
     * 原因是:下面的表解析方法,用的是大驼峰转下划线,再跟sql拦截器拦截到的表名对比,如果匹配到了,则认为该表需要多租户拼接
     * 但是有的表没有严格的按照大驼峰转下划线,所以这些表需要额外定义
     * @TableName 这个注解能否完成该职责,目前还未测试,以后再说。
     */
    List<String> list = new ArrayList<>();
    list.add("das_standard_operation");
    list.add("t_expert");
    list.add("t_nominate_dict");
    list.add("t_nominate_dict_history");
    list.add("t_order");
    list.add("t_standard_sort");
    list.add("t_task");
    list.add("t_task_confirm");
    list.add("das_view");
    if (list.contains(tableName)) {
        return false;
    }
    EntityTableCache instance = EntityTableCache.getInstance();
    if (null == instance || null == instance.getCacheData(tableName)) {
        //如果未初始化到,不拼接租户id
        return true;
    }
    String entityPath = EntityTableCache.getInstance().getCacheData(tableName).toString();
    //该方法会将大驼峰转为下划线,并完成初始化
    return !EntityUtils.isHaveAttr(entityPath, COLUMN_TENANTID);
}

Код метода EntityUtils выглядит следующим образом (написал молодой человек на github, свяжитесь со мной, чтобы удалить нарушение)

/**
 * 判断实体是否有某个属性
 *
 * @param entityPath 实体全路径
 * @param attrName   属性名字
 * @return boolean
 */
public static boolean isHaveAttr(String entityPath, String attrName) {
    Optional<String> epOptional = Optional.ofNullable(entityPath);
    if (!epOptional.isPresent()) {
        return false;
    }
    try {
        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(epOptional.get());
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            if (attrName.equals(field.getName())) {
                return true;
            }
        }
        return false;
    } catch (ClassNotFoundException e) {
        // log.error("SystemSqlParser->isHaveAttr类加载异常:" + e.getMessage());
        return false;
    }
}

2.jsqlparser Этот пакет не так с версией PageHelper

Во время самопроверки я обнаружил, что оператор обновления не может быть остановлен.Оказалось, что номер версии jsqlparser был 1.2 и 1.2 и 2.0 (версия jspparser mp3.4.1 была 2.0).Посредством отладки я нашел что я ввел метод обновления 1.2. Как показано на рисунке:

image.png

Версия 1.2 — это getTables(), а 2.0 — это getTable(), как показано ниже.

Решение: родительский pom применяет версию

</dependencyManagement>
    </dependencies>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.1.10</version>
        </dependency>
    </dependencies>
</dependencyManagement>

3. Не удалось выполнить синтаксический анализ SQL

1.regexp

В настоящее время найден использовать RegexP> 0 SQL-заявление, парсер будет жаловаться

2.заменить в заявлении

Я обнаружил, что это утверждение пока не поддерживается.

Вышеуказанные две проблемы до сих пор не решены.Я читал некоторые материалы и спрашивал некоторых коллег.Я мало контактирую с этой темой.В настоящее время используемый в 3.4.3.4 com.github.jsqlparser используется последней версией mp:jsqlparser Версия 4.2, в настоящее время не поддерживает два вышеупомянутых (если какой-либо великий бог решил это, пожалуйста, прокомментируйте и направьте!)

Решение

Раз у вас не получается, то вы мне не нужны, склейка вручную, смотрите метод игнора ниже⬇️

4. Игнорирование мультитенантности не работает

Мы все знаем, что по аннотации: @interceptorignore (tenantline = "on") может получить оператор MAPPER без разрешения SQL и не выполняет многотенантное преобразование. Но в реальном сценарии приложения обнаруживается, что есть особая сцена, в которой эта аннотация не действует.

Например:

Page<Expert> page = PageHelper.startPage(param.getPageNumber(), param.getPageSize());
List<Expert> experts = expertMapper.queryExpertList(page);

Этот вопрос действительно долгое время отнимал у меня время впустую.Позже, ознакомившись с информацией, я обнаружил, что @InterceptorIgnore будет недействительным при наличии нумерации страниц, но я снова думаю, это все мп вещи, вы конфликтуете, ну и что? .

Потом я нашел штуку PageHelper, она выглядела очень странно. Затем я удалил его без подкачки, и это сработало! Аннотация вступает в силу. Отлично, в то же время mp уже предусмотрел пагинацию, зачем использовать com.github.

Затем преобразуйте следующим образом:

IPage<Expert> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(param.getPageNumber(),param.getPageSize())
List<Expert> experts = expertMapper.queryExpertList(expertMapping.dtoToEntity(param));