核心功能
主要就是插入数据的时候,会自动添加参数 tenantId
,而需要查询的时候,帮你自动添加where tenant=xx
。
新增数据
SimpleExecutor#doUpdate
,执行器调用doUpdate
方法,会编译sql
。
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
MybatisPlusInterceptor#intercept
,编译就会调用拦截器MybatisPlusInterceptor
,如果拦截的是StatementHandler
,会调用所有的InnerInterceptor
来执行。
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object target = invocation.getTarget();
Object[] args = invocation.getArgs();
if (target instanceof Executor) {
final Executor executor = (Executor) target;
Object parameter = args[1];
boolean isUpdate = args.length == 2;
MappedStatement ms = (MappedStatement) args[0];
if (!isUpdate && ms.getSqlCommandType() == SqlCommandType.SELECT) {
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
BoundSql boundSql;
if (args.length == 4) {
boundSql = ms.getBoundSql(parameter);
} else {
// 几乎不可能走进这里面,除非使用Executor的代理对象调用query[args[6]]
boundSql = (BoundSql) args[5];
}
for (InnerInterceptor query : interceptors) {
if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) {
return Collections.emptyList();
}
query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);
}
CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
} else if (isUpdate) {
for (InnerInterceptor update : interceptors) {
if (!update.willDoUpdate(executor, ms, parameter)) {
return -1;
}
update.beforeUpdate(executor, ms, parameter);
}
}
} else {
// StatementHandler
final StatementHandler sh = (StatementHandler) target;
// 目前只有StatementHandler.getBoundSql方法args才为null
if (null == args) {
for (InnerInterceptor innerInterceptor : interceptors) {
innerInterceptor.beforeGetBoundSql(sh);
}
} else {
Connection connections = (Connection) args[0];
Integer transactionTimeout = (Integer) args[1];
for (InnerInterceptor innerInterceptor : interceptors) {
innerInterceptor.beforePrepare(sh, connections, transactionTimeout);
}
}
}
return invocation.proceed();
}
TenantLineInnerInterceptor#processInsert
,判断是否需要忽略租户,获取租户。具体的实现是TenantLineHandler
来处理的。
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
if (tenantLineHandler.ignoreTable(insert.getTable().getName())) {
// 过滤退出执行
return;
}
List<Column> columns = insert.getColumns();
if (CollectionUtils.isEmpty(columns)) {
// 针对不给列名的insert 不处理
return;
}
String tenantIdColumn = tenantLineHandler.getTenantIdColumn();
if (tenantLineHandler.ignoreInsert(columns, tenantIdColumn)) {
// 针对已给出租户列的insert 不处理
return;
}
columns.add(new Column(tenantIdColumn));
// fixed gitee pulls/141 duplicate update
List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new StringValue(tenantIdColumn));
equalsTo.setRightExpression(tenantLineHandler.getTenantId());
duplicateUpdateColumns.add(equalsTo);
}
Select select = insert.getSelect();
if (select != null && (select.getSelectBody() instanceof PlainSelect)) { //fix github issue 4998 修复升级到4.5版本的问题
this.processInsertSelect(select.getSelectBody(), (String) obj);
} else if (insert.getItemsList() != null) {
// fixed github pull/295
ItemsList itemsList = insert.getItemsList();
Expression tenantId = tenantLineHandler.getTenantId();
if (itemsList instanceof MultiExpressionList) {
((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(tenantId));
} else {
List<Expression> expressions = ((ExpressionList) itemsList).getExpressions();
if (CollectionUtils.isNotEmpty(expressions)) {//fix github issue 4998 jsqlparse 4.5 批量insert ItemsList不是MultiExpressionList 了,需要特殊处理
int len = expressions.size();
for (int i = 0; i < len; i++) {
Expression expression = expressions.get(i);
if (expression instanceof RowConstructor) {
((RowConstructor) expression).getExprList().getExpressions().add(tenantId);
} else if (expression instanceof Parenthesis) {
RowConstructor rowConstructor = new RowConstructor()
.withExprList(new ExpressionList(((Parenthesis) expression).getExpression(), tenantId));
expressions.set(i, rowConstructor);
} else {
if (len - 1 == i) { // (?,?) 只有最后一个expre的时候才拼接tenantId
expressions.add(tenantId);
}
}
}
} else {
expressions.add(tenantId);
}
}
} else {
throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
}
}
查询数据
TenantLineInnerInterceptor#beforeQuery
,查询调用。
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
return;
}
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(parserSingle(mpBs.sql(), null));
}
TenantLineInnerInterceptor#processSelect
,处理查询逻辑。
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
final String whereSegment = (String) obj;
processSelectBody(select.getSelectBody(), whereSegment);
List<WithItem> withItemsList = select.getWithItemsList();
if (!CollectionUtils.isEmpty(withItemsList)) {
withItemsList.forEach(withItem -> processSelectBody(withItem, whereSegment));
}
}
BaseMultiTableInnerInterceptor#builderExpression
,将租户的表达式和原有的表达式用and
连接。
protected Expression builderExpression(Expression currentExpression, List<Table> tables, final String whereSegment) {
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(tables)) {
return currentExpression;
}
// 构造每张表的条件
List<Expression> expressions = tables.stream()
.map(item -> buildTableExpression(item, currentExpression, whereSegment))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 没有表需要处理直接返回
if (CollectionUtils.isEmpty(expressions)) {
return currentExpression;
}
// 注入的表达式
Expression injectExpression = expressions.get(0);
// 如果有多表,则用 and 连接
if (expressions.size() > 1) {
for (int i = 1; i < expressions.size(); i++) {
injectExpression = new AndExpression(injectExpression, expressions.get(i));
}
}
if (currentExpression == null) {
return injectExpression;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), injectExpression);
} else {
return new AndExpression(currentExpression, injectExpression);
}
}
TenantLineInnerInterceptor#buildTableExpression
,判断是否忽略该表,获取租户字段名和租户的值。
@Override
public Expression buildTableExpression(final Table table, final Expression where, final String whereSegment) {
if (tenantLineHandler.ignoreTable(table.getName())) {
return null;
}
return new EqualsTo(getAliasColumn(table), tenantLineHandler.getTenantId());
}
protected Column getAliasColumn(Table table) {
StringBuilder column = new StringBuilder();
// todo 该起别名就要起别名,禁止修改此处逻辑
if (table.getAlias() != null) {
column.append(table.getAlias().getName()).append(StringPool.DOT);
}
column.append(tenantLineHandler.getTenantIdColumn());
return new Column(column.toString());
}