This article illustrates the steps to use Oracle Sharding with the Oracle Universal Connection Pool (UCP) in MyBatis-Spring framework. However, the same technique can be used with other frameworks too.
Below are two key steps:
1. Create a custom UCP data source and override getConnection(user,password, labels) method.
2. Pass the sharding key information to the datasource using a shared thread-local object.
Assume there is a simple sharded table customer with two columns “custid” and “name” where “custid” is the sharding key column.
1. Create a custom UCP datasource by extending oracle.ucp.jdbc.PoolDataSourceImpl class and override the getConnection(user, password, labels) method like below.
The overridden getConnection method gets the sharding key value from shared thread-local variable and uses it to get the connection from UCP.
Please note that extending vendor-specific classes such as the PoolDataSourceImpl is not a recommended best practice except in specific cases such as this blog and this works with JDK8 only.
Please note that extending vendor-specific classes such as the PoolDataSourceImpl is not a recommended best practice except in specific cases such as this blog and this works with JDK8 only.
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import oracle.jdbc.OracleShardingKey;
import oracle.jdbc.OracleType;
import oracle.ucp.jdbc.PoolDataSourceImpl;
public class ShardingDataSourceImpl extends PoolDataSourceImpl {
@Override
public Connection getConnection(String username, String password,
Properties labels) throws SQLException {
String shardingkey = ShardingController.getShardingKeyContext().get();
OracleShardingKey shardKey = this.createShardingKeyBuilder()
.subkey(shardingkey, OracleType.VARCHAR2).build();
return this.createConnectionBuilder().user(username).password(password)
.labels(labels).shardingKey(shardKey).build();
}
}
2. Create a DAO class which sets the value of sharding key on the shared thread-local object before calling a method of sqlSession class which internally calls getConnection() method.
public class CustomerDaoImpl implements CustomerDao {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public Customer getCustomerDetails(String customerId) {
ShardingController.getShardingKeyContext().set(customerId);
return (Customer) sqlSession.selectOne("CustomerMapper.getCustomerDetails",
customerId);
}
}
/*
* This class uses a thread-local variable to pass sharding key information from
* application to UCP. The application code sets the value of sharding key on
* the thread-local object, which is being read by UCP’s overridden
* getConnection() method.
*/
public class ShardingController {
private static ThreadLocal<String> shardingKeyContext = new ThreadLocal<String>();
public static ThreadLocal<String> getShardingKeyContext() {
return shardingKeyContext;
}
}
The spring XML configuration file look like below:
The CustomerDaoImpl class uses sqlSessionTemplate which internally calls the Universal Connection Pool (UCP) APIs for connection check-outs and check-ins. The main application code looks like the following.
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUCPShardingWithMyBatisSpring {
public static void main(String args[]) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring-config.xml");
String customerId = “cust123”;
CustomerDaoImpl dao = (CustomerDaoImpl) ctx.getBean("customerDao");
Customer customer = dao.getCustomerDetails(customerId);
System.out.println("Customer ID = " + customer.getCustId());
System.out.println("Customer Name = " + customer.getName());
}
}
