咱們在設計表結構的時候,每每會額外添多以下幾個字段java
create_time【表字段】-- createTime【實體字段】 : 建立時間mysql
update_time【表字段】-- updateTime【實體字段】:更新時間spring
create_by【表字段】-- createBy 【實體字段】: 建立人sql
update_by【表字段】--updateBy 【實體字段】:更新人數據庫
del_flag【表字段】-- delFlag【實體字段】 : 邏輯刪除標識【0:未刪除、1:已刪除】apache
在編寫實體類【與數據映射】的時候,不能總在每一個類上都編寫上述幾個屬性,顯得有些冗餘,因而咱們將這幾個屬性提取出來網絡
並在對應的屬性上標註mybatis-plus的填充註解,那麼全部繼承BaseEntity的類將會擁有createTime,updateTime屬性,繼承DefaultEntity的類將會擁有createTime、updateTime、createBy、updateBy、delFlag屬性。session
至於爲何會設計兩個類,是由於有些中間表可能不須要用到createBy、updateBy、delFlag屬性,只須要createTime,updateTime屬性。mybatis
<dependencies> <!--mybatis--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies>
package com.cloud.pango.common.core.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import java.util.Date; @Data @ToString @EqualsAndHashCode public class BaseEntity{ @TableField(fill = FieldFill.INSERT)//插入時填充 private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)//插入貨更新時填充 private Date updateTime; }
package com.cloud.pango.common.core.entity; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @Data @ToString @EqualsAndHashCode public class DefaultEntity extends BaseEntity{ @TableField(fill = FieldFill.INSERT) //插入時填充 private String createBy; @TableField(fill = FieldFill.INSERT_UPDATE)//插入或更新時填充 private String updateBy; @TableField(fill = FieldFill.INSERT) //插入時填充 @TableLogic//邏輯刪除 private Integer delFlag; }
package com.cloud.pango.common.mybatis; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; @Slf4j public class EntityMetaObjectHanlder implements MetaObjectHandler { private final static String CREATE_TIME = "createTime"; private final static String UPDATE_TIME = "updateTime"; private final static String CREATE_BY = "createBy"; private final static String UPDATE_BY = "updateBy"; private final static String DEL_FLAG = "delFlag"; @Autowired private MetaObjectOperator metaObjectOperator; private void setMeta(MetaObject metaObject,String field,Object defaultValue){ if(metaObject.hasGetter(field)){ Object value = metaObject.getValue(field); if(null != value){ this.setFieldValByName(field,value,metaObject); }else { this.setFieldValByName(field,defaultValue,metaObject); } } } private void setCreateTime(MetaObject metaObject){ setMeta(metaObject,CREATE_TIME,new Date()); } private void setUpdateTime(MetaObject metaObject){ setMeta(metaObject,UPDATE_TIME,new Date()); } private void setCreateBy(MetaObject metaObject){ Object createBy = metaObjectOperator.getCreateBy(); if(null == createBy){ return ; } setMeta(metaObject,CREATE_BY,createBy); } private void setUpdateBy(MetaObject metaObject){ Object updateBy = metaObjectOperator.getUpdateBy(); if(null == updateBy){ return ; } setMeta(metaObject,UPDATE_BY,updateBy); } private void setDelFlag(MetaObject metaObject){ setMeta(metaObject,DEL_FLAG,0); } @Override public void insertFill(MetaObject metaObject) { String name = metaObject.getOriginalObject().getClass().getName(); if(log.isDebugEnabled()){ log.debug("{} 自動裝配 createTime、updateTime、createBy、updateBy、delFlag",name); } setCreateTime(metaObject); setUpdateTime(metaObject); setCreateBy(metaObject); setUpdateBy(metaObject); setDelFlag(metaObject); } @Override public void updateFill(MetaObject metaObject) { String name = metaObject.getOriginalObject().getClass().getName(); if(log.isDebugEnabled()){ log.debug("{} 自動裝配 updateTime、updateBy",name); } setUpdateTime(metaObject); setUpdateBy(metaObject); } }
在分佈式縱橫的年代,人們千方百計提升應用程序的性能,將數據庫拆分爲讀寫分離模式【主從複製】是一項提供性能的一種方案。app
固然這種方案只針對對數據一致性沒那麼高要求的程序【弱一致性】。由於在主數據庫同步給從數據庫時候有可能會出現一些額外狀況,如網絡緣由等,致使主從數據庫數據不一致。
本案例採用動態數據源及mybatis-plus來實現讀寫分離
<dependencies> <!--mybatis--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.1.1</version> </dependency> </dependencies>
配置名爲master的主數據庫【用於寫操做】及名爲slave的從數據庫【讀操做】
spring: datasource: dynamic: druid: # 全局druid參數,絕大部分值和默認保持一致。(現已支持的參數以下,不清楚含義不要亂設置) # 鏈接池的配置信息 # 初始化大小,最小,最大 initial-size: 5 min-idle: 5 maxActive: 20 # 配置獲取鏈接等待超時的時間 maxWait: 60000 # 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一個鏈接在池中最小生存的時間,單位是毫秒 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false # 打開PSCache,而且指定每一個鏈接上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 # 配置監控統計攔截的filters,去掉後監控界面sql沒法統計,'wall'用於防火牆 filters: stat,wall,slf4j # 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄 connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000 datasource: master: url: ${MYSQL_URL:jdbc:mysql://localhost:3306/master?characterEncoding=UTF-8&useUnicode=true&useSSL=false} username: ${MYSQL_USER:root} password: ${MYSQL_PASS:123456} driver-class-name: com.mysql.cj.jdbc.Driver slave: url: ${MYSQL_URL:jdbc:mysql://localhost:3306/slave?characterEncoding=UTF-8&useUnicode=true&useSSL=false} username: ${MYSQL_USER:root} password: ${MYSQL_PASS:123456} driver-class-name: com.mysql.cj.jdbc.Driver
顯式使用
只需在對應的方法上加上@DS註解並指明對應的數據源便可
package com.cloud.pango.uc.service.impl; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.cloud.pango.uc.demo.DemoEntity; import com.cloud.pango.uc.mapper.DemoMapper; import com.cloud.pango.uc.service.DemoService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class DemoServiceImpl extends ServiceImpl<DemoMapper, DemoEntity> implements DemoService { @DS("master")//標明使用master數據源 @Override public Boolean insert(DemoEntity demo) { save(demo); return true; } @DS("slave")//標明使用slave數據源 @Override public DemoEntity findById(String id){ DemoEntity demo = getById(id); return demo; } }
隱式使用
在未使用註解時,使用mybatis-plus攔截器,將讀請求轉移到slave數據源處理,將寫請求轉移到master數據源處理
package com.cloud.pango.uc.common; import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Properties; @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) @Slf4j @Component public class DynamicInterceptor implements Interceptor { private static final String MASTER = "master"; private static final String SLAVE = "slave"; @Override public Object intercept(Invocation invocation) throws Throwable { boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive(); if(!synchronizationActive) { Object[] objects = invocation.getArgs(); MappedStatement ms = (MappedStatement) objects[0]; String currentDataSource = DynamicDataSourceContextHolder.peek(); if(StringUtils.isEmpty(currentDataSource)){ if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)) { DynamicDataSourceContextHolder.push(SLAVE); }else{ DynamicDataSourceContextHolder.push(MASTER); } Object proceed = invocation.proceed(); DynamicDataSourceContextHolder.clear(); return proceed; } } return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { } }