基于SpringBoot+Mybatis實現(xiàn)Mysql分表
基本思路
1.根據(jù)創(chuàng)建時間字段按年進行分表,比如日志表log可以分為log_2024、log_2025
2.在需要進行插入、更新操作的地方利用threadlocal
將數(shù)據(jù)表對應的Entity
和創(chuàng)建時間
放入當前的線程中,利用mybatis提供的攔截器在sql執(zhí)行前進行攔截,將threadlocal
中的Entity類取出,根據(jù)類上標注的注解獲取要操作的表名,再利用創(chuàng)建時間獲得最終要操作的實際表名,最后更換sql中的表名讓攔截器繼續(xù)執(zhí)行
定義注解
定義注解@ShardedTable
,將該注解標注在數(shù)據(jù)表對應的Entity
類上,比如User
類上
/** * 分表注解 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ShardedTable { // 表名前綴 String prefix(); }
@ShardedTable(prefix = "user") @TableName("user") public class User { @TableId(type = IdType.AUTO) private Integer id; private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } }
創(chuàng)建ThreadLocal
public class ShardingContext{ private static final ThreadLocal<ShardingContext> CONTEXT = new ThreadLocal<>(); private Class<?> entityClass; // 數(shù)據(jù)表對應的實體類 private Date date; public static void setContext(Class<?> entityClass, Date date) { ShardingContext context = new ShardingContext(); context.entityClass = entityClass; context.date = date; CONTEXT.set(context); } public static ShardingContext getContext() { return CONTEXT.get(); } public static void clearContext() { CONTEXT.remove(); } public Class<?> getEntityClass() { return entityClass; } public Date getDate() { return date; } }
創(chuàng)建攔截器
@Component @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class,Integer.class})}) public class ShardingInterceptor implements Interceptor { @Autowired private ShardingStrategy shardingStrategy; @Override public Object intercept(Invocation invocation) throws Throwable { // 獲取原始SQL StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String originalSql = boundSql.getSql(); // 獲取當前操作的實體類 ShardingContext context = ShardingContext.getContext(); if (context != null){ Class<?> entityClass = context.getEntityClass(); Date date = context.getDate(); ShardedTable annotation = entityClass.getAnnotation(ShardedTable.class); if (annotation != null) { // 設置新的sql,替換表名 String baseTableName = annotation.prefix(); String actualTableName = shardingStrategy.getTableName(User.class, date); String modifiedSql = originalSql.replace(baseTableName, actualTableName); setSql(boundSql, modifiedSql); // 將數(shù)據(jù)保存到原表,作為備份 executeBackupInsert(statementHandler,originalSql); } } return invocation.proceed(); } private void setSql(BoundSql boundSql, String sql) throws Exception { Field field = BoundSql.class.getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, sql); } // 同時將數(shù)據(jù)保存到原表,作為備份 private void executeBackupInsert(StatementHandler statementHandler, String backupSql) throws SQLException { Connection connection = null; PreparedStatement preparedStatement = null; try { // 通過反射獲取 MappedStatement MetaObject metaObject = SystemMetaObject.forObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection(); preparedStatement = connection.prepareStatement(backupSql); // 設置參數(shù) ParameterHandler parameterHandler = statementHandler.getParameterHandler(); parameterHandler.setParameters(preparedStatement); preparedStatement.executeUpdate(); } finally { if (preparedStatement != null) { preparedStatement.close(); } if (connection != null) { connection.close(); } } } @Override public Object plugin(Object target) { // 判斷是否為StatementHandler類型 if (target instanceof StatementHandler){ return Plugin.wrap(target, this); }else { return target; } } @Override public void setProperties(Properties properties) { } }
獲取表名
@Component public class ShardingStrategy { public String getTableName(Class<?> entityClass, Date date) { ShardedTable annotation = entityClass.getAnnotation(ShardedTable.class); if (annotation == null) { throw new RuntimeException("實體類必須使用@ShardedTable注解"); } // 獲取分表前綴 String tablePrefix = annotation.prefix(); if (tablePrefix == null || tablePrefix.isEmpty()) { throw new RuntimeException("分表前綴不能為空"); } // 獲取當前日期所在的年份 int year = DateUtil.year(date); return tablePrefix + "_" + year; } }
業(yè)務處理
在需要進行業(yè)務處理的地方,將數(shù)據(jù)表對應的Entity.class
和創(chuàng)建時間
通過threadlocal放入當前線程中,后面要根據(jù)這些信息獲取實際要操作的表名
public void insert(ServiceOrderLogEntity serviceOrderLogEntity) { ShardingContext.setContext(ServiceOrderLogEntity.class, serviceOrderLogEntity.getTime() == null ? new Date() : serviceOrderLogEntity.getTime()); int result = serviceOrderLogMapper.insert(serviceOrderLogEntity); ShardingContext.clearContext(); }
到此這篇關于基于SpringBoot+Mybatis實現(xiàn)Mysql分表的文章就介紹到這了,更多相關SpringBoot Mysql分表內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Springboot使用MybatisPlus實現(xiàn)mysql樂觀鎖
- springBoot+mybatis-plus實現(xiàn)監(jiān)聽mysql數(shù)據(jù)庫的數(shù)據(jù)增刪改
- SpringBoot集成MyBatisPlus+MySQL的實現(xiàn)
- SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫分表
- mybatis+springboot中使用mysql的實例
- SpringBoot+Mybatis-Plus實現(xiàn)mysql讀寫分離方案的示例代碼
- springboot基于Mybatis mysql實現(xiàn)讀寫分離
相關文章
IDEA創(chuàng)建MyBatis配置文件模板的方法步驟
這篇文章主要介紹了IDEA創(chuàng)建MyBatis配置文件模板的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04SpringBoot使用MyBatis時的幾種傳參規(guī)范示例
使用Mybatis作為持久層框架時,對于數(shù)據(jù)庫的增刪改查等操作都需要參數(shù)的傳遞,本文就詳細的介紹了一下SpringBoot使用MyBatis時的幾種傳參規(guī)范示例,感興趣的可以了解一下2022-02-02