mysql+spring+mybatis实现数据库读写分离[代码配置]

场景:一个读数据源一个读写数据源。


原理:借助spring的【org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource】这个抽象类实现,看名字可以了解到是一个路由数据源的东西,这个类中有一个方法

	/**
	 * Determine the current lookup key. This will typically be
	 * implemented to check a thread-bound transaction context.
	 * 

Allows for arbitrary keys. The returned key needs * to match the stored lookup key type, as resolved by the * {@link #resolveSpecifiedLookupKey} method. */ protected abstract Object determineCurrentLookupKey();


每次去连数据库的时候,spring会调用这个方法去找对应的数据源。返回值即对应的数据源的LookUpKey.那么这个LookUpKey在哪定义的呢?看下面的dataBase.xml的配置


	
	
		
		
			user=${jdbc.username},password=${jdbc.password}
			
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	
	   
    
        
        
            user=${jdbc.r.username},password=${jdbc.r.password}
            
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    
    
    
    
        
        
            
                
                
            
        
            
    

	
	
		
		
	

动态数据源dynamicDataSource中的dataSourceKeyRW、dataSourceKeyR就是

protected abstract Object determineCurrentLookupKey();
这个方法要返回的值。那么如何设置,让这个方法的返回值是根据我们的需要返回dataSourceKeyRW、dataSourceKeyR呢?由于这个方法没有入参,并且是spring自动调用的,因此考虑使用静态变量存储dataSource的key,在调用sql语句前设置静态变量的值,然后在这个方法中得到静态变量的值,返回。又考虑到多线程,同时可能会有很多请求,为避免线程之间相互干扰,考虑使用threadLocal。

先看存储dataSourceKey的容器类。

public class DBContextHolder {

    /**
     * 线程threadlocal
     */
    private static ThreadLocal contextHolder = new ThreadLocal<>();

    private String DB_TYPE_RW = "dataSourceKeyRW";
    private String DB_TYPE_R = "dataSourceKeyR";

    public String getDbType() {
        String db = contextHolder.get();
        if (db == null) {
            db = DB_TYPE_RW;// 默认是读写库
        }
        return db;
    }

    /**
     * 
     * 设置本线程的dbtype
     * 
     * @param str
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public void setDbType(String str) {
        contextHolder.set(str);
    }

    /**
     * clearDBType
     * 
     * @Title: clearDBType
     * @Description: 清理连接类型
     */
    public static void clearDBType() {
        contextHolder.remove();
    }
}

动态数据源的实现类。


public class DynamicDataSource extends AbstractRoutingDataSource {

    /*
     * (non-Javadoc)
     * @see javax.sql.CommonDataSource#getParentLogger()
     */
    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * 
     * override determineCurrentLookupKey
     * 

* Title: determineCurrentLookupKey *

*

* Description: 自动查找datasource *

* * @return */ @Override protected Object determineCurrentLookupKey() { return DBContextHolder.getDbType(); } }

在DAO层中设置数据库类型。

/**
	 * 添加邮件
	 * 
	 * @param sms
	 * @return
	 */
	public boolean insertEmail(Email email) {
	    
	    //根据具体需要设置不同的数据库
	    DBContextHolder.setDbType(DBContextHolder.DB_TYPE_RW);
	    //DBContextHolder.setDbType(DBContextHolder.DB_TYPE_R);
		int result = this.getSqlSession().insert(STATEMENT + ".addEntity",
				email);
		return result == 1;
	}




在本例中,我们是在DAO中指定数据库,我们也可以根据需要在service或者controller中指定DB类型,需要记住的是setDbType是针对线程维度的。要考虑多线程的问题。






你可能感兴趣的:(java历程,spring)