jdbc动态数据源实现操作数据库读写分离
以下一、二缺一不可
mysql主从复制配置,这里以两台虚拟机为例进行配置:
环境准备
1.克隆一个centos
网络配置
2.mysql主从复制配置:
2.1主节点
2.1.1创建用户
create user 'taibai'@'192.168.204.%' identified by '123456';
2.1.2赋予权限
grant replication slave on *.* to 'taibai'@'192.168.204.%' identified by '123456';
如果创建要通用权限用户
create user 'taibai' identified by '123456'; grant all on *.* to 'taibai'@'%' identified by '123456';
2.1.3启动binlog日志(my.cnf配置文件中加入)
server-id=1 //随便指定一个id 不能与其他主机冲突 log-bin=/var/lib/mysql/mysql-bin
2.1.4重启
service mysqld restart
2.2从节点
2.2.1my.cnf配置文件中加入
server-id=2 relay-log=/var/lib/mysql/relay-bin relay-log-index=/var/lib/mysql/relay-bin.index
2.2.2登录mysql执行(建立关系)
change master to master_host='192.168.204.101',master_port=3306,master_user='repl',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=0;
注:master_log_file='mysql-bin.000001',master_log_pos=0;这两个值有时需要根据master的信息写
查看命令:
show master status
2.2.3开始复制
start slave;
2.2.4查看状态
show slave status\G
搭建注意点:
1.关闭防火墙或开放端口
2.修改 /var/lib/mysql/auto.cnf文件 将uuid随便修改一下(如果是克隆虚拟机的话,会出现要UUID一致的情况)3.修改配置文件重启
pom.xml
4.0.0
com.xiaxin
master-slave
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-parent
2.1.5.RELEASE
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.1
mysql
mysql-connector-java
com.alibaba
druid
1.1.14
org.springframework.boot
spring-boot-starter-jdbc
org.projectlombok
lombok
1.启动类
@SpringBootApplication
//@EnableAspectJAutoProxy(proxyTargetClass = true)
@MapperScan("com.xiaxin.mapper")
public class MybatisMasterSlaveApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisMasterSlaveApplication.class);
}
}
2.数据源配置
@Configuration
public class DruidConfig {
public final static String MAPPER_XML_PATH = "classpath:mybatis/mapper/*.xml";
@ConfigurationProperties(prefix = "master.datasource")
@Bean(name = "masterDataSource")
public DataSource masterDataSource() {
return new DruidDataSource();
}
@Bean
public PlatformTransactionManager txManager(DataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
@ConfigurationProperties(prefix = "slave.datasource")
@Bean
public DataSource slaveDataSource(){
return new DruidDataSource();
}
@Bean
public DynamicDataSource dynamicDataSource(){
DynamicDataSource dynamicDataSource=new DynamicDataSource();
Map
3.动态数据源配置
/**
* spring的jdbc提供了动态数据源的入口
* 继承AbstractRoutingDataSource覆盖determineCurrentLookupKey()方法 返回当前使用数据库
*/
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
log.info("当前使用数据库:{}",DbUtil.getDb());
return DbUtil.getDb();
}
}
工具类用来设置和获取当前使用数据源
public class DbUtil {
public static String master="master";
public static String slave="slave";
private static ThreadLocal threadLocal=new ThreadLocal();
public static void setDb(String db){
threadLocal.set(db);
}
public static String getDb(){
return threadLocal.get();
}
}
问题:这个数据源类型不能写死,那么在哪里设置当前的数据库操作需要访问哪个数据源呢?可以使用注解和aop
4.设置当前访问操作数据库的数据源类型
自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MasterDataSource {
String value() default "";
}
aop:
@Aspect
@Component
public class DatabaseAOP {
@Pointcut(value = "execution(* com.xiaxin.mapper..*.*(..))")
public void pointCut() {
}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
boolean isExist = method.isAnnotationPresent(MasterDataSource.class);
if (!isExist) {
DbUtil.setDb(DbUtil.slave);
return;
}
DbUtil.setDb(DbUtil.master);
}
}
注:如果访问方法上加了注解,那么就使用主库用来写入数据
5.业务代码:
controller层:
@RestController
@RequestMapping("/city")
public class CityController {
@Autowired
private CityService cityService;
@GetMapping("/add")
public String addCity(String cityName) {
cityService.addCity(new City().setCityName(cityName));
return "success";
}
@GetMapping("/getCityByCityName")
public List getCityByCityName(String cityName) {
return cityService.getCityByCityName(cityName);
}
}
service层:
//接口
public interface CityService {
void addCity(City city);
List getCityByCityName(String cityName);
}
//实现类
@Service
public class CityServiceImpl implements CityService {
@Autowired
CityMapper cityMapper;
@Override
public void addCity(City city) {
cityMapper.insertCity(city);
}
@Override
public List getCityByCityName(String cityName) {
return cityMapper.selectByName(cityName);
}
}
dao层:
@Mapper
@Component
public interface CityMapper {
// @Insert("INSERT into city (id,city_name) VALUES (#{id,jdbcType=INTEGER}, #{cityName,jdbcType=VARCHAR});")
@MasterDataSource
void insertCity(City city);
/**
* 根据城市名称,查询城市信息
*
* @param cityName 城市名
*/
@Select("select * from city where city_name like CONCAT('%', #{cityName},'%')")
List selectByName(@Param("cityName") String cityName);
}
mapper配置文件
id,city_name
INSERT into city (id,city_name) VALUES (#{id,jdbcType=INTEGER}, #{cityName,jdbcType=VARCHAR});
entity实体类:
@Data
@Accessors(chain = true)
public class City {
private Long id;
private String cityName;
}
查询和写入分别显示如下结果:
至此演示完毕