基于SpringBoot+Mybatis實(shí)現(xiàn)Mysql分表
基本思路
1.根據(jù)創(chuàng)建時(shí)間字段按年進(jìn)行分表,比如日志表log可以分為log_2024、log_2025
2.在需要進(jìn)行插入、更新操作的地方利用threadlocal
將數(shù)據(jù)表對應(yīng)的Entity
和創(chuàng)建時(shí)間
放入當(dāng)前的線程中,利用mybatis提供的攔截器在sql執(zhí)行前進(jìn)行攔截,將threadlocal
中的Entity類取出,根據(jù)類上標(biāo)注的注解獲取要操作的表名,再利用創(chuàng)建時(shí)間獲得最終要操作的實(shí)際表名,最后更換sql中的表名讓攔截器繼續(xù)執(zhí)行
定義注解
定義注解@ShardedTable
,將該注解標(biāo)注在數(shù)據(jù)表對應(yīng)的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ù)表對應(yīng)的實(shí)體類 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(); // 獲取當(dāng)前操作的實(shí)體類 ShardingContext context = ShardingContext.getContext(); if (context != null){ Class<?> entityClass = context.getEntityClass(); Date date = context.getDate(); ShardedTable annotation = entityClass.getAnnotation(ShardedTable.class); if (annotation != null) { // 設(shè)置新的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í)將數(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è)置參數(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("實(shí)體類必須使用@ShardedTable注解"); } // 獲取分表前綴 String tablePrefix = annotation.prefix(); if (tablePrefix == null || tablePrefix.isEmpty()) { throw new RuntimeException("分表前綴不能為空"); } // 獲取當(dāng)前日期所在的年份 int year = DateUtil.year(date); return tablePrefix + "_" + year; } }
業(yè)務(wù)處理
在需要進(jìn)行業(yè)務(wù)處理的地方,將數(shù)據(jù)表對應(yīng)的Entity.class
和創(chuàng)建時(shí)間
通過threadlocal放入當(dāng)前線程中,后面要根據(jù)這些信息獲取實(shí)際要操作的表名
public void insert(ServiceOrderLogEntity serviceOrderLogEntity) { ShardingContext.setContext(ServiceOrderLogEntity.class, serviceOrderLogEntity.getTime() == null ? new Date() : serviceOrderLogEntity.getTime()); int result = serviceOrderLogMapper.insert(serviceOrderLogEntity); ShardingContext.clearContext(); }
到此這篇關(guān)于基于SpringBoot+Mybatis實(shí)現(xiàn)Mysql分表的文章就介紹到這了,更多相關(guān)SpringBoot Mysql分表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot使用MybatisPlus實(shí)現(xiàn)mysql樂觀鎖
- springBoot+mybatis-plus實(shí)現(xiàn)監(jiān)聽mysql數(shù)據(jù)庫的數(shù)據(jù)增刪改
- SpringBoot集成MyBatisPlus+MySQL的實(shí)現(xiàn)
- SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分庫分表
- mybatis+springboot中使用mysql的實(shí)例
- SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫分離方案的示例代碼
- springboot基于Mybatis mysql實(shí)現(xiàn)讀寫分離
相關(guān)文章
Java下變量大小寫駝峰、大小寫下劃線、大小寫連線轉(zhuǎn)換
有時(shí)候需要處理對象屬性的getter、setter方法,或者將屬性與數(shù)據(jù)表字段進(jìn)行相互轉(zhuǎn)換,感興趣的可以了解一下2021-06-06Java?EasyExcel導(dǎo)出合并單元格的示例詳解
EasyExcel是阿里巴巴開源的一個(gè)excel處理框架,以使用簡單、節(jié)省內(nèi)存著稱,這篇文章主要為大家介紹了如何利用EasyExcel導(dǎo)出合并單元格,需要的可以參考下2023-09-09SpringMVC mybatis整合實(shí)例代碼詳解
這篇文章主要介紹了springmvc與mybatis實(shí)例詳解的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-04-04實(shí)例分析Java Class的文件結(jié)構(gòu)
今天把之前在Evernote中的筆記重新整理了一下,發(fā)上來供對java class 文件結(jié)構(gòu)的有興趣的同學(xué)參考一下2013-04-04IDEA創(chuàng)建MyBatis配置文件模板的方法步驟
這篇文章主要介紹了IDEA創(chuàng)建MyBatis配置文件模板的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04SpringBoot使用MyBatis時(shí)的幾種傳參規(guī)范示例
使用Mybatis作為持久層框架時(shí),對于數(shù)據(jù)庫的增刪改查等操作都需要參數(shù)的傳遞,本文就詳細(xì)的介紹了一下SpringBoot使用MyBatis時(shí)的幾種傳參規(guī)范示例,感興趣的可以了解一下2022-02-02