咱们在设计表结构的时候,每每会额外添多以下几个字段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) { } }