Springboot Multi-data source read-write separation and main library data source service layer transaction control

Source: Internet
Author: User
Tags aop int size
Read and write separation if the framework is nothing more than the implementation of multiple data sources, the main library with a written data source, from the library with a read data source.

Because I want to study the database reading and writing separation and the design of the Sub-Library table, so I set up a SPRINGBOOT+DRUID+MYBATIS+AOP to achieve a master multi-slave design.
First step: First, you need to customize the data source configuration items, springboot default parsing is prefixed with Spring.datasource. The following configuration items, in order to not conflict, define the DataSource directly. As our prefix,
@ConfigurationProperties (prefix = "datasource.write") can be used to load configuration items with the specified prefix, which is very convenient
Because you use Druid, you need to specify a type when you need to generate datasource.

Datasourcebuilder.create (). Type (DatasourceType). Build ()

ReadSize is used to define the size from the library, how many from the library will be configured from the library datasource
The second step: Load balancing from the library, mainly Myabstractroutingdatasource this class
The third step is to create Sqlsessionfactory method from the Mybatisautoconfiguration class that writes the Springboot-mybatis rack package, Replace the data source with our custom Abstractroutingdatasource
Step four. Custom Transaction Mydatasourcetransactionmanagerautoconfiguration

Complete code and Unit tests:
Github:https://github.com/ggj2010/javabase.git

The main rack Package

<!--JDBC driver begin-->
        <dependency>
            <groupId>com.alibaba</groupId>
            < artifactid>druid</artifactid>
        </dependency>
        <dependency>
            <groupid>mysql </groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</ scope>
        </dependency>
        <!--mybatis springboot-->
        <dependency>
            <groupid >org.mybatis.spring.boot</groupId>
            <artifactid>mybatis-spring-boot-starter</artifactid >
        </dependency>
        <!--jdbc driver end-->
        <dependency>
            <groupId> org.aspectj</groupid>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

Custom data Source Configuration items:

#多数据源 1 main 2 from DataSource: #从库数量 readsize:2 # using Druid data source Type:com.alibaba.druid.pool.DruidDataSource #主库 write: Url:jdbc:mysql://localhost:3306/master?useunicode=true&characterencoding=utf-8 Username:root Password:roo
    T driver-class-name:com.mysql.jdbc.driver filters:stat maxactive:20 initialsize:1 maxwait:60000 Minidle:1 timebetweenevictionrunsmillis:60000 minevictableidletimemillis:300000 validationQueryTimeout: 900000 validationquery:select sysdate () from dual testwhileidle:true testonborrow:false testonreturn:f Alse poolpreparedstatements:true maxopenpreparedstatements:20 Read1:url:jdbc:mysql://localhost:3306/slav E1?useunicode=true&characterencoding=utf-8 username:root Password:root Driver-class-name:com.mysql.jdbc . Driver filters:stat maxactive:20 initialsize:1 maxwait:60000 minidle:1 timebetweenevictionrun smillis:60000 MinEvictableidletimemillis:300000 validationquerytimeout:900000 validationquery:select SYSDATE () from dual test Whileidle:true testonborrow:false testonreturn:false poolpreparedstatements:true MaxOpenPreparedStatem Ents:20 Read2:url:jdbc:mysql://localhost:3306/slave2?useunicode=true&characterencoding=utf-8 Username:ro
    OT password:root driver-class-name:com.mysql.jdbc.driver filters:stat maxactive:20 initialsize:1 maxwait:60000 minidle:1 timebetweenevictionrunsmillis:60000 minevictableidletimemillis:300000 Vali 
    dationquerytimeout:900000 Validationquery:select sysdate () from dual testwhileidle:true Testonborrow:false Testonreturn:false poolpreparedstatements:true maxopenpreparedstatements:20

Resolving configuration items:

@Configuration @Slf4j public class Datasourceconfiguration {@Value ("${datasource.type}") private class<? exte

    NDS datasource> DatasourceType; @Bean (name = "Writedatasource") @Primary @ConfigurationProperties (prefix = "datasource.write") public datasour
        Ce Writedatasource () {log.info ("--------------------writedatasource init---------------------");
    Return Datasourcebuilder.create (). Type (DatasourceType). build (); }/** * How many * @return */@Bean (name = "ReadDataSource1") to configure from the library @ConfigurationProperties (pr Efix = "Datasource.read1") public datasource Readdatasourceone () {log.info ("--------------------readdatasour
        Ceone init---------------------");
    Return Datasourcebuilder.create (). Type (DatasourceType). build (); } @Bean (name = "ReadDataSource2") @ConfigurationProperties (prefix = "datasource.read2") public datasource Rea Ddatasourcetwo () {Log.info ("--------------------readdatasourcetwo init---------------------");
    Return Datasourcebuilder.create (). Type (DatasourceType). build (); }
}

Overriding sqlsessionfactory

@Configuration @AutoConfigureAfter ({datasourceconfiguration.class}) @Slf4j public class Mybatisconfiguration extends

    mybatisautoconfiguration {@Value ("${datasource.readsize}") Private String datasourcesize; @Bean public Sqlsessionfactory Sqlsessionfactorys () throws Exception {Log.info ("--------------------overloads the parent class SQL
        Sessionfactory init---------------------");
    Return Super.sqlsessionfactory (Roundrobindatasouceproxy ()); }/** * How many data sources will be configured to configure how many beans * @return */@Bean public Abstractroutingdatasource Roundrobindatas
        Ouceproxy () {int size = Integer.parseint (datasourcesize);
        Myabstractroutingdatasource proxy = new Myabstractroutingdatasource (size);
        Map<object, object> targetdatasources = new Hashmap<object, object> ();
        DataSource Writedatasource = Springcontextholder.getbean ("Writedatasource"); Write Targetdatasources.put (DataSourceType.write.getType (), SpringcontexthoLder.getbean ("Writedatasource"));  for (int i = 0; i < size; i++) {Targetdatasources.put (I, Springcontextholder.getbean ("Readdatasource" + (i
        + 1));
        } proxy.setdefaulttargetdatasource (Writedatasource);
        Proxy.settargetdatasources (targetdatasources);
    return proxy; }

}

Local Thread Global variables

public class Datasourcecontextholder {
    private static final threadlocal<string> local = new threadlocal< String> ();

    public static threadlocal<string> getlocal () {
        return local;
    }

    /**
     * Read may be multiple libraries
     *
    /public static void Read () {
        local.set (DataSourceType.read.getType ());
    }

    /**
     * Write only one library */public
    static void Write () {
        local.set (DataSourceType.write.getType ());
    }

    public static String Getjdbctype () {
        return local.get ();
    }
}

Multi-data source switching

public class Myabstractroutingdatasource extends Abstractroutingdatasource {
    private final int datasourcenumber;
    Private Atomicinteger count = new Atomicinteger (0);

    Public myabstractroutingdatasource (int datasourcenumber) {
        this.datasourcenumber = datasourcenumber;
    }

    @Override
    protected Object Determinecurrentlookupkey () {
        String Typekey = Datasourcecontextholder.getjdbctype ();
        if (Typekey.equals (DataSourceType.write.getType ()))
            return DataSourceType.write.getType ();
        Read simple load balancer
        int number = Count.getandadd (1);
        int lookupkey = number% Datasourcenumber;
        return new Integer (LookupKey);
    }
}

Enum type

public enum DatasourceType {
    read ("read", "from Library"), Write ("write", "Main Library");
    @Getter
    private String type;
    @Getter
    private String name;

    DatasourceType (String type, string name) {
        this.type = type;
        this.name = name;
    }
}

AOP interception set Local thread variables

@Aspect
@Component
@Slf4j public
class Datasourceaop {

    @Before ("Execution (* Com.ggj.encrypt.modules.*.dao. *.find* (..)) or Execution (* Com.ggj.encrypt.modules.*.dao). *.get* (..)) ")
    public void Setreaddatasourcetype () {
        datasourcecontextholder.read ();
        Log.info ("DataSource Switch to: Read");
    }

    @Before ("Execution (* Com.ggj.encrypt.modules.*.dao). *.insert* (..)) or Execution (* Com.ggj.encrypt.modules.*.dao). *.update* (..)) ")
    public void Setwritedatasourcetype () {
        datasourcecontextholder.write ();
        Log.info ("DataSource Switch to: Write");
    }
}

Custom Transactions

 @Configuration @EnableTransactionManagement @Slf4j public class Mydatasourcetransactionmanagerautoconfiguration extends Datasourcetransactionmanagerautoconfiguration {/** * custom Transaction * MyBatis automatically participates in spring transaction management without additional configuration,
     Transaction management will not work as long as the data source referenced by Org.mybatis.spring.SqlSessionFactoryBean is consistent with the data source referenced by Datasourcetransactionmanager.
        * @return */@Bean (name = "TransactionManager") public Datasourcetransactionmanager transactionmanagers () {
        Log.info ("--------------------transactionmanager init---------------------");
    return new Datasourcetransactionmanager (Springcontextholder.getbean ("Roundrobindatasouceproxy")); }
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.