http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/Important.markdownjava
結合spring使用mybatis pagehelper只能配置一個bean,當一個項目使用多種數據庫時就比較糾結了,下面是我參考spring支持多數據源的思想封裝一個支持多數據源的PageHelper。mysql
實現一個mybatis插件,參考mybatis pagehelper,實現根據不一樣的數據源切換使用不一樣的pagehelper。git
@SuppressWarnings({"rawtypes", "unchecked"}) @Intercepts(@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})) public class CustomPageHelper implements Interceptor { private PageHelper mysqlPageHelper = new PageHelper(); private PageHelper oraclePageHelper = new PageHelper(); private PageHelper postgresqlPageHelper = new PageHelper(); private Map<Object, PageHelper> targetPageHelper = new HashMap<>(); private PageHelper defaultPageHelper; @Override public Object intercept(Invocation invocation) throws Throwable { return determinePageHelper().intercept(invocation); } @Override public Object plugin(Object target) { /*return determinePageHelper().plugin(target);*/ //determinePageHelper(); if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { targetPageHelper.put(Dialect.mysql.name(), mysqlPageHelper); targetPageHelper.put(Dialect.oracle.name(), oraclePageHelper); targetPageHelper.put(Dialect.postgresql.name(), postgresqlPageHelper); //數據庫方言 String dialect = properties.getProperty("dialect"); if(Dialect.oracle.equals(Dialect.valueOf(dialect.toLowerCase()))) { defaultPageHelper = oraclePageHelper; } else if(Dialect.postgresql.equals(Dialect.valueOf(dialect.toLowerCase()))) { defaultPageHelper = postgresqlPageHelper; } else { defaultPageHelper = mysqlPageHelper; } properties.put("dialect", Dialect.mysql.name()); mysqlPageHelper.setProperties(properties); properties.put("dialect", Dialect.oracle.name()); oraclePageHelper.setProperties(properties); properties.put("dialect", Dialect.postgresql.name()); postgresqlPageHelper.setProperties(properties); properties.put("dialect", dialect); } private PageHelper determinePageHelper() { String pageType = PageHelperHolder.getPagerType(); PageHelper pageHelper = targetPageHelper.get(pageType); if (pageHelper != null) { return pageHelper; } else { return defaultPageHelper; } } }
<!-- Oracle配置數據源--> <bean id="oracleDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${oracle.jdbc.driver}"/> <property name="jdbcUrl" value="${oracle.jdbc.url}"/> <property name="user" value="${oracle.jdbc.user}"/> <property name="password" value="${oracle.jdbc.password}"/> <property name="minPoolSize" value="${oracle.jdbc.minPoolSize}"/> <property name="maxPoolSize" value="${oracle.jdbc.maxPoolSize}"/> <property name="initialPoolSize" value="${oracle.jdbc.initialPoolSize}"/> <property name="maxIdleTime" value="${oracle.jdbc.maxIdleTime}"/> <property name="acquireIncrement" value="${oracle.jdbc.acquireIncrement}"/> <property name="acquireRetryAttempts" value="${oracle.jdbc.acquireRetryAttempts}"/> <property name="acquireRetryDelay" value="${oracle.jdbc.acquireRetryDelay}"/> <property name="testConnectionOnCheckin" value="${oracle.jdbc.testConnectionOnCheckin}"/> <property name="automaticTestTable" value="${oracle.jdbc.automaticTestTable}"/> <property name="idleConnectionTestPeriod" value="${oracle.jdbc.idleConnectionTestPeriod}"/> <property name="checkoutTimeout" value="${oracle.jdbc.checkoutTimeout}"/> </bean> <!-- Mysql配置數據源--> <bean id="mysqlDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="${mysql.jdbc.driver}"/> <property name="jdbcUrl" value="${mysql.jdbc.url}"/> <property name="user" value="${mysql.jdbc.user}"/> <property name="password" value="${mysql.jdbc.password}"/> <property name="minPoolSize" value="${mysql.jdbc.minPoolSize}"/> <property name="maxPoolSize" value="${mysql.jdbc.maxPoolSize}"/> <property name="initialPoolSize" value="${mysql.jdbc.initialPoolSize}"/> <property name="maxIdleTime" value="${mysql.jdbc.maxIdleTime}"/> <property name="acquireIncrement" value="${mysql.jdbc.acquireIncrement}"/> <property name="acquireRetryAttempts" value="${mysql.jdbc.acquireRetryAttempts}"/> <property name="acquireRetryDelay" value="${mysql.jdbc.acquireRetryDelay}"/> <property name="testConnectionOnCheckin" value="${mysql.jdbc.testConnectionOnCheckin}"/> <property name="automaticTestTable" value="${mysql.jdbc.automaticTestTable}"/> <property name="idleConnectionTestPeriod" value="${mysql.jdbc.idleConnectionTestPeriod}"/> <property name="checkoutTimeout" value="${mysql.jdbc.checkoutTimeout}"/> </bean> <bean id="pgDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本屬性 url、user、password --> <property name="driverClassName" value="${pg.db.drivername}"/> <property name="url" value="${pg.db.url}" /> <property name="username" value="${pg.db.username}" /> <property name="password" value="${pg.db.password}" /> <property name="dbType" value="postgresql" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="50" /> <!-- 配置獲取鏈接等待超時的時間 --> <property name="maxWait" value="60000" /> <!-- 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一個鏈接在池中最小生存的時間,單位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="SELECT 1" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="true" /> <!-- 打開PSCache,而且指定每一個鏈接上PSCache的大小 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 配置監控統計攔截的filters --> <property name="filters" value="stat" /> </bean> <bean id="multipleDataSource" class="com.common.support.MultipleDataSource"> <property name="defaultTargetDataSource" ref="mysqlDataSource"/> <property name="targetDataSources"> <map> <entry key="mySqlDataSource" value-ref="mysqlDataSource"/> <entry key="passSqlDataSource" value-ref="passsqlDataSource"/> <entry key="oracleDataSource" value-ref="oracleDataSource"/> <entry key="greenplumDataSource" value-ref="greenplumDataSource"/> </map> </property> </bean> <!-- 配置sql會話工廠:SqlSessionFactoryBean --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="multipleDataSource" /> <!--<property name="dataSource" ref="oracleDataSource"/>--> <property name="plugins"> <array> <bean class="com.common.support.CustomPageHelper" scope="singleton"> <property name="properties"> <value>dialect=mysql</value> </property> </bean> </array> </property> </bean>
最後定義一個PageHelperHolder,在數據源切換時把當前使用的數據庫類型放進來覺得Pagehelper動態切換數據源。spring
public final class PageHelperHolder { // public enum PagerType { // MySql, Oracle // } private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setPagerType(Dialect Dialect) { contextHolder.set(Dialect.name()); } public static String getPagerType() { return contextHolder.get(); } public static void clearPaerType() { contextHolder.remove(); } }
自定義註解,用來加到mapper上面,aop攔截mybatis mapper方法時根據註解來判斷數據源類型從而決定用哪一個分頁sql
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented public @interface DataSourceSelector { DataSourceType value(); }
public enum DataSourceType { MYSQL_DATASOURCE("mySqlDataSource", Dialect.mysql), ORACLE_DATASOURCE("oracleDataSource", Dialect.oracle), GREENPLUM_DATASOURCE("pgDataSource", Dialect.postgresql), private String type; private Dialect dialect; DataSourceType(String type, Dialect dialect) { this.type = type; this.dialect = dialect; } public String getType() { return type; } public Dialect getDialect() { return dialect; } public static DataSourceType getType(String type) { for(DataSourceType dataSourceType : DataSourceType.values()) { if(dataSourceType.type.equals(type)) { return dataSourceType; } } return null; } }
下面是spring aop攔截mapper的重點代碼片斷數據庫
@Aspect public class MultipleDataSourceAspectAdvice { @Around("execution(* com.mapper.*.*(..))") public Object doAround(ProceedingJoinPoint jp) throws Throwable { return DataSourceHolder.getDynaDataSource(jp); } }
//作mapper使用數據源類型緩存,不用每次都要從註解中讀取 private transient static Map<Class, String> dataSourceHolder = new HashMap<>(); public static Object getDynaDataSource(ProceedingJoinPoint jp) throws Throwable { Object result = null; Class targetClass = jp.getThis().getClass(); try { log.debug("MultipleDataSourceAspectAdvice: {}, {}", jp.getThis(), jp.getTarget()); //獲取咱們寫的mapper類,以便讀取到類上面的註解 Class[] interfaces = targetClass.getInterfaces(); if (interfaces != null && interfaces.length > 0) { targetClass = interfaces[0]; } String targetDataSource = dataSourceHolder.get(targetClass); boolean isHold = false; if (StringUtils.isBlank(targetDataSource)) { //讀取註解上聲明要使用的數據源類型 DataSourceSelector dss = (DataSourceSelector) targetClass.getAnnotation(DataSourceSelector.class); if (dss != null) { targetDataSource = dss.value().getType(); } } else { isHold = true; } if (StringUtils.isNotBlank(targetDataSource)) { DataSourceType dataSourceType = DataSourceType.getType(targetDataSource); if(dataSourceType != null) { DataSourceHolder.setCustomerType(dataSourceType.getType()); PageHelperHolder.setPagerType(dataSourceType.getDialect()); if (!isHold) { dataSourceHolder.put(targetClass, targetDataSource); } } else { log.warn("{}-{} not found available dataSourceType, use default.", targetClass, targetDataSource); } } log.debug("{}: {}", targetClass, dataSourceHolder.get(targetClass)); result = jp.proceed(); } catch (Exception ex) { log.error("deal with dynamic datasource error. datasource: {}", DataSourceHolder.getCustomerType()); dataSourceHolder.remove(targetClass); throw ex; } finally { DataSourceHolder.clearCustomerType(); PageHelperHolder.clearPaerType(); } return result; }
mapper類緩存
@DataSourceSelector(DataSourceType.MYSQL_DATASOURCE) public interface SysRoleMapper { …… }
大致實現就是這樣子了,要注意的是spring數據源的配置bean的id和DataSourceType中定義的名稱了一致,要否則key不同會取不到哦。markdown