Java (Spring & Non Spring) replication-datasource

When you need database replication, you have to route read/write connections to appropriate databases.

There are two ways of implementing replication datasources in Java environment. (actually there are at least two more ways, Database Proxy server like MySQL Proxy or MaxScale and MySql Replication JDBC Driver).

I introduce two pure java ways, the first one is only for Spring framework and the second one is for general java applications.

You can test these two ways with ReplicationRoutingDataSourceIntegrationTest and LazyReplicationConnectionDataSourceProxySpringIntegrationTest.

If you need Spring Boot example, please refer to kwon37xi/replication-datasource-boot

Spring's LazyConnectionDataSourceProxy + AbstractRoutingDataSource

Refer to ReplicationRoutingDataSource and WithRoutingDataSourceConfig. You can make replication data source with only spring framework's two basic classes - LazyConnectionDataSourceProxy and AbstractRoutingDataSource.

This works very nicely with Spring's TransactionSynchronizationManager.

If you use Spring framework for your application, this is enough for your database replication.

You just need to set @Transactional(readOnly = true|false).

public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "read" : "write";
    }
}

@Bean
public DataSource writeDataSource() {
    ...
}

@Bean
public DataSource readDataSource() {
    ...
}

@Bean
public DataSource routingDataSource(
    @Qualifier("writeDataSource") DataSource writeDataSource,
    @Qualifier("readDataSource") DataSource readDataSource) {
    ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource();

    Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
    dataSourceMap.put("write", writeDataSource);
    dataSourceMap.put("read", readDataSource);
    routingDataSource.setTargetDataSources(dataSourceMap);
    routingDataSource.setDefaultTargetDataSource(writeDataSource);

    return routingDataSource;
}

@Bean
public DataSource dataSource(@Qualifier("routingDataSource") DataSource routingDataSource) {
    return new LazyConnectionDataSourceProxy(routingDataSource);
}

// in Service class.

// working with read database
@Transactional(readOnly = true)
public Object readQuery() {
    ....
}

// working with write database
@Transactional(readOnly = false)
public void writeExection() {
    ....
}

LazyReplicationConnectionDataSourceProxy

I refered to Spring framework's LazyConnectionDataSourceProxy and modified a little for supporting replication to make LazyReplicationConnectionDataSourceProxy.

It's enough to copy & paste LazyReplicationConnectionDataSourceProxy to make a replication datasource.

This has features of LazyConnectionDataSourceProxy and support database replication(master/slave | read/write) routing.

This also does not depend on Spring framework. So you can use this code with any Java applications. But you have to remember to call connection.setReadOnly(true|false) for replication before executing statements. And You cannot reuse the connection for different readOnly status, you have to close and get again another connection for a new jdbc statement.

@Bean
public DataSource writeDataSource() {
    ...
}

@Bean
public DataSource readDataSource() {
    ...
}

@Bean
public DataSource dataSource(DataSource writeDataSource, DataSource readDataSource) {
    return new LazyReplicationConnectionDataSourceProxy(writeDataSource, readDataSource);
}

when you use with spring framework

// in Service class.

// Spring's @Transaction AOP automatically call connection.setReadOnly(true|false).
// But Spring prior to 4.1.x JPA does not call setReadOnly method.
 // In this situation you'd better use LazyConnectionDataSourceProxy + AbstractRoutingDataSource.
// working with read database
@Transactional(readOnly = true)
public Object readQuery() {
    ....
}

// working with write database
@Transactional(readOnly = false)
public void writeExection() {
    ....
}

when you use without spring framwork

Connection readConn = dataSource.getConnection();
readConn.setReadOnly(true);

// ... working with readConn...

readConn.close();

Connection writeConn = dataSource.getConnection();
writeConn.setReadOnly(false);

// ... working with writeConn...

writeConn.close();