Mybatis+ MySQL5.7主从数据库复制配置

 

实现基本的mysql主数据写入,从数据库查询,从数据库简单负载均衡(均匀分配请求数)

 

1.1配置两个数据源参数

jdbc.master.type=mysql

jdbc.master.driverClassName=com.mysql.jdbc.Driver

jdbc.master.url=jdbc:mysql://192.168.113.11:3306/showbox?useUnicode=true&characterEncoding=utf-8

jdbc.master.username=root

jdbc.master.password=zhd123456

 

jdbc.slave1.type=mysql

jdbc.slave1.driverClassName=com.mysql.jdbc.Driver

jdbc.slave1.url=jdbc:mysql://192.168.113.200:3306/showbox?useUnicode=true&characterEncoding=utf-8

jdbc.slave1.username=root

jdbc.slave1.password=zhd123456

1.2 配置applicationContext-datasource.xml

<!-- 主库master  数据连接池 -->

<bean id="dataSource-master" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

           <property name="url" value="${jdbc.master.url}" />

    <property name="username" value="${jdbc.master.username}" />

    <property name="password" value="${jdbc.master.password}" />

    <property name="driverClassName" value="${jdbc.master.driverClassName}" />

           <property name="maxActive" value="200" />

           <property name="maxWait" value="60000" />

           <property name="minIdle" value="20" />

          

           <property name="testWhileIdle" value="true" />

           <property name="validationQuery" value="select 1+1 from dual" />

           <property name="removeAbandoned" value="true" />

           <property name="removeAbandonedTimeout" value="300" />

           <property name="timeBetweenEvictionRunsMillis" value="60000" />

           <property name="numTestsPerEvictionRun" value="20" />

           <property name="minEvictableIdleTimeMillis" value="300000" />

          

           <property name="poolPreparedStatements" value="true" />

           <property name="defaultAutoCommit" value="true" />

</bean>

 

<!-- 从库slave1  数据连接池 -->

<bean id="dataSource-slave1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

           <property name="url" value="${jdbc.slave1.url}" />

    <property name="username" value="${jdbc.slave1.username}" />

    <property name="password" value="${jdbc.slave1.password}" />

    <property name="driverClassName" value="${jdbc.slave1.driverClassName}" />

           <property name="maxActive" value="200" />

           <property name="maxWait" value="60000" />

           <property name="minIdle" value="20" />

          

           <property name="testWhileIdle" value="true" />

           <property name="validationQuery" value="select 1+1 from dual" />

           <property name="removeAbandoned" value="true" />

           <property name="removeAbandonedTimeout" value="300" />

           <property name="timeBetweenEvictionRunsMillis" value="60000" />

           <property name="numTestsPerEvictionRun" value="20" />

           <property name="minEvictableIdleTimeMillis" value="300000" />

          

           <property name="poolPreparedStatements" value="true" />

           <property name="defaultAutoCommit" value="true" />

</bean>

 

<!--主从库选择 -->

    <bean id="dynamicDataSource" class="com.flame.common.dataSource.DynamicDataSource">

        <property name="master" ref="dataSource-master" />

        <property name="slaves">

            <list>

                <ref bean="dataSource-slave1" />

            </list>

        </property>

    </bean>

          

<!-- 事务管理器 -->

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

           <property name="dataSource" ref="dynamicDataSource" />

</bean>

 

<!-- 注解式事务配置 -->

<tx:annotation-driven transaction-manager="transactionManager" />

 

1.3配置applicationContext-datasource.xml

<!-- MyBatis会话工厂 -->

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

           <property name="dataSource" ref="dynamicDataSource" />

           <property name="databaseIdProvider" ref="databaseIdProvider" />

           <property name="configLocation" value="classpath:/META-INF/mybatis/mybatis-config.xml" />

           <property name="mapperLocations">

                    <array>

                             <value>classpath:/META-INF/mybatis/mybatis-pager.xml</value>

                             <value>classpath*:com/flame/dao/mapper/*.xml</value> 

                             <value>classpath*:com/flame/dao/custom/*.xml</value>  

                    </array>

           </property>

</bean>

1.4DynamicDataSource 动态切换数据源代码

package com.flame.common.dataSource;

 

import java.util.HashMap;

import java.util.LinkedList;

import java.util.List;

import java.util.Map;

import java.util.concurrent.atomic.AtomicLong;

 

import javax.sql.DataSource;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

 

public class DynamicDataSource extends AbstractRoutingDataSource {

         private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

 

         private DataSource master; // 主库,只允许有一个

         private List<DataSource> slaves; // 从库,允许有多个

         private AtomicLong slaveCount = new AtomicLong();

         private int slaveSize = 0;

 

         private Map<Object, Object> dataSources = new HashMap<Object, Object>();

 

         private static final String DEFAULT = "master";

         private static final String SLAVE = "slave";

 

         private static final ThreadLocal<LinkedList<String>> datasourceHolder = new ThreadLocal<LinkedList<String>>() {

 

                   @Override

                   protected LinkedList<String> initialValue() {

                            return new LinkedList<String>();

                   }

 

         };

 

         /**

          * 初始化

          */

         @Override

         public void afterPropertiesSet() {

                   if (null == master) {

                            throw new IllegalArgumentException("Property 'master' is required");

                   }

                   dataSources.put(DEFAULT, master);

                   if (null != slaves && slaves.size() > 0) {

                            for (int i = 0; i < slaves.size(); i++) {

                                     dataSources.put(SLAVE + (i + 1), slaves.get(i));

                            }

                            slaveSize = slaves.size();

                   }

                   this.setDefaultTargetDataSource(master);

                   this.setTargetDataSources(dataSources);

                   super.afterPropertiesSet();

         }

 

         /**

          * 选择使用主库,并把选择放到当前ThreadLocal的栈顶

          */

         public static void useMaster() {

                   if (LOGGER.isDebugEnabled()) {

                            LOGGER.debug("use datasource :" + datasourceHolder.get());

                   }

                   LinkedList<String> m = datasourceHolder.get();

                   m.offerFirst(DEFAULT);

         }

 

         /**

          * 选择使用从库,并把选择放到当前ThreadLocal的栈顶

          */

         public static void useSlave() {

                   if (LOGGER.isDebugEnabled()) {

                            LOGGER.debug("use datasource :" + datasourceHolder.get());

                   }

                   LinkedList<String> m = datasourceHolder.get();

                   m.offerFirst(SLAVE);

         }

 

         /**

          * 重置当前栈

          */

         public static void reset() {

                   LinkedList<String> m = datasourceHolder.get();

                   if (LOGGER.isDebugEnabled()) {

                            LOGGER.debug("reset datasource {}", m);

                   }

                   if (m.size() > 0) {

                            m.poll();

                   }

         }

 

         /**

          * 如果是选择使用从库,且从库的数量大于1,则通过取模来控制从库的负载, 计算结果返回AbstractRoutingDataSource

          */

         @Override

         protected Object determineCurrentLookupKey() {

                   LinkedList<String> m = datasourceHolder.get();

                   String key = m.peekFirst() == null ? DEFAULT : m.peekFirst();

 

                   if (null != key) {

                            if (DEFAULT.equals(key)) {

                                     LOGGER.debug("current datasource (default):" + key);

                                     return key;

                            } else if (SLAVE.equals(key)) {

                                     if (slaveSize > 1) {// Slave loadBalance

                                               long c = slaveCount.incrementAndGet();

                                               c = c % slaveSize;

                                               LOGGER.debug("current datasource (slaves>1) :" + SLAVE + (c + 1));

                                               return SLAVE + (c + 1);

                                     } else {

                                               LOGGER.debug("current datasource (slaves=1):" + SLAVE + "1");

                                               return SLAVE + "1";

                                     }

                            }

                            return null;

                   } else {

                            return null;

                   }

         }

 

         public DataSource getMaster() {

                   return master;

         }

 

         public List<DataSource> getSlaves() {

                   return slaves;

         }

 

         public void setMaster(DataSource master) {

                   this.master = master;

         }

 

         public void setSlaves(List<DataSource> slaves) {

                   this.slaves = slaves;

         }

 

}

1.5 注解DataSourceChange

 package com.flame.common.annotation;

 

import static java.lang.annotation.ElementType.METHOD;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

 

import java.lang.annotation.Inherited;

import java.lang.annotation.Retention;

import java.lang.annotation.Target;

 

@Inherited

@Retention(RUNTIME)

@Target(METHOD)

public @interface DataSourceChange {

boolean slave() default false;

}

1.6配置AOP

1. DataSourceAspect切面

package com.flame.common.dataSource;

 

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.stereotype.Component;

 

import com.flame.common.annotation.DataSourceChange;

import com.flame.common.exception.DataSourceAspectException;

 

/**

 * 有{@link com.wangzhixuan.commons.annotation.DataSourceChange}注解的方法,调用时会切换到指定的数据源

 *

 * @author tanghd

 */

@Aspect

@Component

public class DataSourceAspect {

private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceAspect.class);

 

@Around("@annotation(dataSourceChange)")

public Object doAround(ProceedingJoinPoint pjp, DataSourceChange dataSourceChange) {

           LOGGER.debug("AOP切换dataSource");

           Object retVal = null;

           boolean selectedDataSource = false;

           try {

                    if (null != dataSourceChange) {

                             selectedDataSource = true;

                             if (dataSourceChange.slave()) {

                                       DynamicDataSource.useSlave();

                             } else {

                                       DynamicDataSource.useMaster();

                             }

                    }

                    retVal = pjp.proceed();

           } catch (Throwable e) {

                    LOGGER.warn("数据源切换错误", e);

                    throw new DataSourceAspectException("数据源切换错误", e);

           } finally {

                    if (selectedDataSource) {

                             DynamicDataSource.reset();

                    }

           }

           return retVal;

}

}

 

2.修改applicationContext.xml

<context:component-scan base-package="com.flame">

                   <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" /> 

                 <context:include-filter type="annotation" expression="org.springframework.stereotype.Service" /> 

                 <context:include-filter type="annotation" expression="org.springframework.stereotype.Component" /> 

</context:component-scan>

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

3、service上添加注解

package com.flame.service.impl;

 

import java.util.List;

 

import javax.annotation.Resource;

 

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

 

import com.flame.base.dao.BaseDao;

import com.flame.base.model.DataGrid;

import com.flame.base.model.Page;

import com.flame.base.model.Params;

import com.flame.base.service.BaseServiceImpl;

import com.flame.common.annotation.DataSourceChange;

import com.flame.dao.ArticleDao;

import com.flame.dto.ArticleDto;

import com.flame.entity.Article;

import com.flame.service.ArticleService;

 

@Service("articleService")

public class ArticleServiceImpl extends BaseServiceImpl<Article, Long> implements ArticleService {

 

@Resource

private ArticleDao articleDao;

 

@Override

protected BaseDao<Article, Long> getBaseDao() {

           return this.articleDao;

}

 

@Override

public List<Article> getResourceByDeviceId(String deviceId) {

           // TODO Auto-generated method stub

           return articleDao.getResourceByDeviceId(deviceId);

}

 

@Override

@DataSourceChange(slave = true)

public DataGrid<ArticleDto> paging(Params params, Page page) {

           // TODO 自动生成的方法存根

           return articleDao.paging(params, page);

}

 

@Override

@Transactional

public void deleteBatchDo(Long[] id) {

           // TODO 自动生成的方法存根

           for (Long i : id) {

                    articleDao.delete(i);

           }

}

 

}

 

 

详细配置下载:https://download.csdn.net/download/jiangshanmnaa/10829634

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

触屏精灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值