MyTenantMasterSlaveAutoRoutingPlugin.java 4.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /*
  2. * Copyright © 2018 organization baomidou
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package jnpf.database.plugins;
  17. import cn.hutool.core.text.StrPool;
  18. import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
  19. import com.baomidou.dynamic.datasource.enums.DdConstants;
  20. import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
  21. import com.baomidou.dynamic.datasource.tx.TransactionContext;
  22. import jnpf.constant.MsgCode;
  23. import jnpf.database.util.DynamicDataSourceUtil;
  24. import jnpf.exception.DataException;
  25. import jnpf.util.TenantHolder;
  26. import lombok.extern.slf4j.Slf4j;
  27. import org.apache.ibatis.cache.CacheKey;
  28. import org.apache.ibatis.executor.Executor;
  29. import org.apache.ibatis.mapping.BoundSql;
  30. import org.apache.ibatis.mapping.MappedStatement;
  31. import org.apache.ibatis.mapping.SqlCommandType;
  32. import org.apache.ibatis.plugin.*;
  33. import org.apache.ibatis.session.ResultHandler;
  34. import org.apache.ibatis.session.RowBounds;
  35. import org.springframework.transaction.support.TransactionSynchronizationManager;
  36. import org.springframework.util.StringUtils;
  37. import javax.sql.DataSource;
  38. import java.util.Optional;
  39. /**
  40. * 租户连接模式读写分离
  41. *
  42. * @author TaoYu
  43. * @since 2.5.1
  44. */
  45. @Intercepts({
  46. @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
  47. @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
  48. @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
  49. @Slf4j
  50. public class MyTenantMasterSlaveAutoRoutingPlugin implements Interceptor, ITenantPlugin {
  51. protected DynamicRoutingDataSource dynamicDataSource;
  52. public MyTenantMasterSlaveAutoRoutingPlugin(DataSource dataSource){
  53. this.dynamicDataSource = (DynamicRoutingDataSource) dataSource;
  54. }
  55. @Override
  56. public Object intercept(Invocation invocation) throws Throwable {
  57. if(TenantHolder.getLocalTenantCache() == null){
  58. printNoTenant(v -> log.warn("未设置租户信息, 禁止查询数据库, {}, {}, {}, {}", v.getUserId(), v.getUrl(), v.getToken(), v.getStack()));
  59. //未设置租户信息不允许操作数据库
  60. throw new DataException(MsgCode.LOG113.get());
  61. }
  62. if (!TenantHolder.getLocalTenantCache().isRemote()
  63. || !DynamicDataSourceUtil.isPrimaryDataSoure()) {
  64. return invocation.proceed();
  65. }
  66. Object[] args = invocation.getArgs();
  67. MappedStatement ms = (MappedStatement) args[0];
  68. String pushedDataSource = null;
  69. try {
  70. String tenantId = Optional.ofNullable(TenantHolder.getLocalTenantCache().getEnCode()).orElse("");
  71. String masterKey = tenantId + StrPool.DASHED +DdConstants.MASTER;
  72. String slaveKey = tenantId + StrPool.DASHED +DdConstants.SLAVE;
  73. // 存在事务只使用主库
  74. boolean hasTrans = TransactionSynchronizationManager.isActualTransactionActive();
  75. if (!hasTrans) {
  76. hasTrans = StringUtils.hasText(TransactionContext.getXID());
  77. }
  78. // 判断切库
  79. String dataSource = SqlCommandType.SELECT == ms.getSqlCommandType() ? slaveKey :masterKey;
  80. if (hasTrans || !dynamicDataSource.getGroupDataSources().containsKey(dataSource)) {
  81. dataSource = masterKey;
  82. }
  83. pushedDataSource = DynamicDataSourceContextHolder.push(dataSource);
  84. return invocation.proceed();
  85. } finally {
  86. if (pushedDataSource != null) {
  87. DynamicDataSourceContextHolder.poll();
  88. }
  89. }
  90. }
  91. }