欢迎投稿

今日深度:

spring-boot mariadb读写分离,spring-bootmariadb

spring-boot mariadb读写分离,spring-bootmariadb


       在总结这篇文章之前,先把之前相关的文章列出来

       《spring-boot系列——与mybaits整合》

       《mariaDB在linux7中的安装》

《linux7下mariadb主从搭建》


     思路

     思路其实很简单,一句话能说清楚:多数据源配置+aop自定义配置切换数据源。这样就可以实现主从读写分离了。

         注意事项:线程安全


     pom

          之前文章用的是1.3.5.RELEASE版本的spring-boot。现在用更新一点的1.5.3.RELEASE版本,这样必须要依赖github的分页工具

     

<dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-web</artifactId>  
    </dependency>  
    
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	
	<dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-devtools</artifactId>  
        <optional>true</optional>  
	</dependency> 
	
	<dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-actuator</artifactId>  
    </dependency>  
    
    <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-aop</artifactId>
	</dependency>	
		  	
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>	    
    
    <dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <scope>provided</scope>	
	</dependency>	
	
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jdbc</artifactId>
	</dependency>
	
	<dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.1.1</version>
    </dependency>
    
    <dependency>
      <groupId>tk.mybatis</groupId>
      <artifactId>mapper-spring-boot-starter</artifactId>
      <version>1.1.0</version>
    </dependency>	
    
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.1.0</version>
        <exclusions>
            <exclusion>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <groupId>org.mybatis.spring.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>		 
    
    <dependency>
	    <groupId>com.alibaba</groupId>
	    <artifactId>druid</artifactId>
	    <version>1.0.24</version>
	</dependency>
	
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
	</dependency>

         多数据源配置

         1、整合阿里巴巴的连接池druid,由于druid不是spring-boot中集成的,所以需要单独配置,当然配置方式也就很灵活了,想怎么弄就怎么弄。这里举个例子

         主yml如下:

spring: 
  profiles: 
    active: dev

         开发环境yml如下:

 

server:
  port: 8080

spring:
  http:
    encoding:
      charset:
        UTF-8
      enabled:
        true
      force:
        true     
        
  output: 
    ansi: 
      enabled: always
     
#  datasource: 
#    url: jdbc:mysql://192.168.160.66:3306/demo?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&pinGlobalTxToPhysicalConnection=true&autoReconnect=true
#    username: root
#    password: nmamtf
#    driver-class-name: com.mysql.jdbc.Driver
#    type: com.alibaba.druid.pool.DruidDataSource
  
druid:  
    type: com.alibaba.druid.pool.DruidDataSource
    master:
        url: jdbc:mysql://192.168.160.66:3306/demo?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
        #url: jdbc:mysql://192.168.160.66:3306/demo?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&pinGlobalTxToPhysicalConnection=true&autoReconnect=true
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: nmamtf
        initialSize: 5
        minIdle: 1
        maxActive: 100
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,wall,log4j
        useGlobalDataSourceStat: true
    slave:
        url: jdbc:mysql://192.168.160.88:3306/demo?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true 
        #url: jdbc:mysql://192.168.160.88:3306/demo?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&pinGlobalTxToPhysicalConnection=true&autoReconnect=true
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: nmamtf
        initialSize: 5
        minIdle: 1
        maxActive: 100
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: select 1 from dual
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,wall,log4j
        useGlobalDataSourceStat: true 
        
mybatis:
    #configLocation: classpath:application_mysql.xml
    type-aliases-package: com.wlf.demo
    mapper-locations: classpath:mapping/*.xml 
    
pagehelper: 
    helperDialect: mysql
    reasonable: true
    supportMethodsArguments: true
    params: count=countSql  

       有一点需要注意,因为引入了github的分页插件,如果在mybatis再配置interceptor进行分页的话,是会报错的。所以这里注释了configLocation中的application.mysql.xml。该xml中是一个分页的interceptor


       java config如下

import java.sql.SQLException;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

	private static Logger LOGGER = LoggerFactory.getLogger(DataSourceConfig.class);
	
	@Value("${druid.type}")
	private Class<? extends DataSource> dataSourceType;
	
	@Bean(name = "masterDataSource")
	@Primary
	@ConfigurationProperties(prefix = "druid.master") 
	public DataSource masterDataSource() throws SQLException{
		DataSource masterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
		LOGGER.info("-----------------主库数据源配置-------------------", masterDataSource);
		return masterDataSource;
	}
 
	@Bean(name = "slaveDataSource")
	@ConfigurationProperties(prefix = "druid.slave")
	public DataSource slaveDataSource(){
		DataSource slaveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
		LOGGER.info("-----------------从库数据源配置--------------------", slaveDataSource);
		return slaveDataSource;
	}
	
}

       2、切换数据源的容器,注意线程安全

public class DataBaseContextHolder {

	public enum DataBaseType {
		MASTER, SLAVE
	}
	
	private static final ThreadLocal<DataBaseType> contextHolder = new ThreadLocal<DataBaseType>();
	
	public static void setDataBaseType(DataBaseType dataBaseType) {
		if(dataBaseType == null) throw new NullPointerException();
		contextHolder.set(dataBaseType);
	}
	
	public static DataBaseType getDataBaseType(){
		return contextHolder.get() == null ? DataBaseType.MASTER : contextHolder.get();
	}

	public static void clearDataBaseType(){
		contextHolder.remove();
	}
	
}

       3、数据源路由的实现

        通过继承AbstractRoutingDataSource,重写spring的数据源路由

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class RoutingDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
		return DataBaseContextHolder.getDataBaseType();
	}

}
       4、将动态路由配置到sessionFactory之中

       这里有两个思路:正常思路为直接new SqlSessionFactoryBean()的方式进行java config配置

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.apache.bcel.util.ClassLoaderRepository;
import org.aspectj.apache.bcel.util.ClassLoaderRepository.SoftHashMap;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.wlf.demo.util.DataBaseContextHolder;
import com.wlf.demo.util.RoutingDataSource;

@Configuration
@AutoConfigureAfter({DataSourceConfig.class})
public class MariadbConfig {

	private static Logger LOGGER = LoggerFactory.getLogger(MariadbConfig.class);
	
	@Resource(name="masterDataSource")
	private DataSource masterDataSource;
	
	@Resource(name="slaveDataSource")
	private DataSource slaveDataSource;
	
	@Bean
    @ConfigurationProperties(prefix = "mybatis")
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dynamicDataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        return sqlSessionFactoryBean;
    }
	
	@Bean(name = "dynamicDataSource")
    public DataSource dataSource() {
		RoutingDataSource dynamicDataSource = new RoutingDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);

        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap(2);
        dsMap.put(DataBaseContextHolder.DataBaseType.MASTER, masterDataSource);
        dsMap.put(DataBaseContextHolder.DataBaseType.SLAVE, slaveDataSource);
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }
	
}

      还有一种思路是通过MybatisAutoConfiguration类中的方法构建sessionFactory,然后进行Java config配置

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.aspectj.apache.bcel.util.ClassLoaderRepository;
import org.aspectj.apache.bcel.util.ClassLoaderRepository.SoftHashMap;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.wlf.demo.util.DataBaseContextHolder;
import com.wlf.demo.util.RoutingDataSource;

@Configuration
@AutoConfigureAfter({DataSourceConfig.class})
public class MariadbConfig extends MybatisAutoConfiguration {

	private static Logger LOGGER = LoggerFactory.getLogger(MariadbConfig.class);
	
	@Resource(name="masterDataSource")
	private DataSource masterDataSource;
	
	@Resource(name="slaveDataSource")
	private DataSource slaveDataSource;
	
	@Bean(name="sqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		return super.sqlSessionFactory(roundRobinDataSourceProxy());
	}
	
	public AbstractRoutingDataSource roundRobinDataSourceProxy(){
		
		RoutingDataSource proxy = new RoutingDataSource();
		//proxy.
		SoftHashMap targetDataSource = new ClassLoaderRepository.SoftHashMap(); 
		targetDataSource.put(DataBaseContextHolder.DataBaseType.MASTER, masterDataSource);
		targetDataSource.put(DataBaseContextHolder.DataBaseType.SLAVE, slaveDataSource);
		//默认数据源
		proxy.setDefaultTargetDataSource(masterDataSource);
		//装入两个主从数据源
		proxy.setTargetDataSources(targetDataSource);
		return proxy;
		
	}
	
}

      注意:该配置必须在数据源配置后再执行


      5、自定义注释

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyConnection {

}

      6、aop

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import com.wlf.demo.annotation.ReadOnlyConnection;
import com.wlf.demo.util.DataBaseContextHolder;

@Aspect
@Component
public class ReadOnlyConnectionInterceptor implements Ordered {

	public static final Logger LOGGER = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);
	
	@Around("@annotation(readOnlyConnection)")
	public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {
		try{
			LOGGER.info("---------------设置为从库---------------");
			DataBaseContextHolder.setDataBaseType(DataBaseContextHolder.DataBaseType.SLAVE);
			Object result = proceedingJoinPoint.proceed();
			return result;
		} finally {
			DataBaseContextHolder.clearDataBaseType();
			LOGGER.info("---------------清空数据库设置---------------");
		}
	}
	
	@Override
	public int getOrder() {
		return 0;
	}

}

       使用方式,在读从库的方法上标注自定义注释,比如:

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.wlf.demo.annotation.ReadOnlyConnection;
import com.wlf.demo.dao.UserDao;
import com.wlf.demo.model.User;
import com.wlf.demo.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDao userDao;
	
	@Override
	@ReadOnlyConnection
	public List<User> getList() {
		List<User> users = userDao.findList();
		return users;
	}

	@Override
	@Transactional
	public int insert(User user) {
		int rows = userDao.insert(user);
		return rows;
	}

}

        这样,mariadb读写分离就ok了



www.htsjk.Com true http://www.htsjk.com/mariadb/29136.html NewsArticle spring-boot mariadb读写分离,spring-bootmariadb        在总结这篇文章之前,先把之前相关的文章列出来        《spring-boot系列——与mybaits整合》        《mariaDB在linux7中的安装》 《lin...
相关文章
    暂无相关文章
评论暂时关闭