Sharding JDBC源码分析-JdbcMethodInvocation类的作用
来源:互联网 发布:淘宝门头在线制作 编辑:程序博客网 时间:2024/06/10 05:06
摘要
当当的Sharding JDBC是在JDBC规范上进行封装来实现数据库分表分库分表功能的。其整体结构非常清晰,主线就是将JDBC规范中的DataSource、Connection、Statement、PreparedStament分别封装为ShardingDataSource、ShardingConnection、ShardingStatement、ShardingPreparedStament类。然后将对象的方法进行重写,而ShardingDataSource、ShardingConnection、ShardingStatement、ShardingPreparedStament类可以看作一个代理对象,它们内部完成了根据逻辑sql语句,转化成真实sql语句,路由找到对应数据源,并根据数据源产生真实的Connection对象,然后执行真实sql语句,并将结果归并返回给应用层的功能。今天来分析一下Sharding JDBC中JdbcMethodInvocation类的作用。
一般情况在调用Connection对象的setAutoCommit方法
默认情况下执行完的操作会被自动提交,当我们要改变时可以在执行sql前调用Connection类的setAutoCommit方法
try{ con = getConnection(); con.setAutoCommit(false); /* * do what you want here. */ con.commit(); }catch(Throwable e){ if(con!=null){ try { con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } throw new RuntimeException(e); }finally{ if(con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } }而ShardingJDBC的ShardingConnection类封装了JDBC中的Connection接口,ShardingConnection只是多个Connection对象的一个代理,直接调用他的重写的setAutoCommit方法无法达到实际的效果,为此ShardingJDBC中引入了JdbcMethodInvocation对ShardingConnection、ShardingStatement、ShardingPreparedStatement类中类似setAutoCommit方法的方法调用进行记录。
JdbcMethodInvocation作用分析
通过ShardingDataSource类得到的ShardingConnection不是真的JDBC Connection对象,其内部有一个Map<String, Connection> connectionMap key为数据源名,value为真实的Connection。当调用Connection类的一些方法时,实际是调用了ShardingConnection 重写后的方法,而此时connectionMap的还是空,达不到调用真实Connection对象方法的功能,为此在ShardingConnection 重写后的方法方法中将调用的方法与方法的参数记录到JdbcMethodInvocation对象中。只有当ShardingPreparedStatement或者ShardingStatement调用对应数据操作函数时(executeQuery,execute,executeUpdate)才会调用ShardingConnection的getContetion方法产生真实的Connection对象,同时把这些对象放入connectionMap。为此在调用ShardingConnection 的setAutoCommint方法、setReadOnly方法、setTransactionIsolation方法时,要记录一下所有调用过的方法的名称与参数。当通过ShardingPreparedStatement或者ShardingStatement产生一个实际的Connection时,再通过反射的方式让真实的Connection对象调用JdbcMethodInvocation记录的方法与方法的参数。
ShardingJDBC框架通过配置文件指定ShardingDataSource与外部应用程序交互,达到访问真实数据源的目的。其有三个成员变量
- private final ShardingProperties shardingProperties;
- private final ExecutorEngine executorEngine;
- private final ShardingContext shardingContext;
shardingProperties为一些属性,executorEngine为一个多线程的执行引擎其内部采用Guava的ListeningExecutorService实现。shardingContext为上下文信息,记录了分库分表规则配置、执行引擎、sql路由引擎这些信息。以便ShardingConnection 使用。
利用ShardingJDBC框架后通过DataSource得到的Connection实际上是包装后的ShardingConnection。ShardingDataSource对DataSource的所有方法进行了重写,其中getConnection如下:
@Override public ShardingConnection getConnection() throws SQLException { MetricsContext.init(shardingProperties); return new ShardingConnection(shardingContext); }得到得到的Connection实际上是包装后的ShardingConnection,而Sharding上下文对象在创建ShardingConnection时被传入。按照上面的一般情况的示例,当我们调用con.setAutoCommit方法时,实际是调用了ShardingDataSource的getConnection方法产生的ShardingConnection对象的setAutoCommit方法。
ShardingConnection自的成员变量如下:
- private final ShardingContext shardingContext;
- private final Map<String, Connection> connectionMap = new HashMap<>();
shardingContext为Sharding上下文对象在创建ShardingConnection时被传入,而connectionMap 为保存真实Connection的Map,其key为配置文件中真实dataSource Bean的id。connectionMap 的初始值为空。在调用ShardingConnection的getConnection方法得到真实的Connection对象后,才会将其实放connectionMap 中。代码如下:
public Connection getConnection(final String dataSourceName, final SQLStatementType sqlStatementType) throws SQLException { Connection result = getConnectionInternal(dataSourceName, sqlStatementType); replayMethodsInvocation(result);//对记录的要执行的调用方法,在这里通过反射进行执行 return result;}
private Connection getConnectionInternal(final String dataSourceName, final SQLStatementType sqlStatementType) throws SQLException { if (connectionMap.containsKey(dataSourceName)) { return connectionMap.get(dataSourceName); } Context metricsContext = MetricsContext.start(Joiner.on("-").join("ShardingConnection-getConnection", dataSourceName)); DataSource dataSource = shardingContext.getShardingRule().getDataSourceRule().getDataSource(dataSourceName); if (dataSource instanceof MasterSlaveDataSource) { dataSource = ((MasterSlaveDataSource) dataSource).getDataSource(sqlStatementType); } Connection result = dataSource.getConnection(); MetricsContext.stop(metricsContext); connectionMap.put(dataSourceName, result);//将真实的Connction对象放入connectionMap中 return result; }按照上面的一般情况的示例,当我们调用con.setAutoCommit方法时,实际是调用ShardingConnection重写后的setAutoCommit方法。该方法在其父类AbstractConnectionAdapter中完成重写,代码如下:
@Override public final void setAutoCommit(final boolean autoCommit) throws SQLException { this.autoCommit = autoCommit; //getConnections方法实际ShardingConnection对象中的coonectionMap对象的value集合, //为空时记录调用过的方法与对应参数,当在产生真正的Connection对象时, //再利用反射的方式让真正的Connection对象调用该方法 if (getConnections().isEmpty()) { recordMethodInvocation(Connection.class, "setAutoCommit", new Class[] {boolean.class}, new Object[] {autoCommit}); return; } for (Connection each : getConnections()) { each.setAutoCommit(autoCommit); } }
AbstractConnectionAdapter的setAutoCommit方法中的getConnections得到了实现是ShardingConnection对象中的coonectionMap对象的value集合。ShardingConnection对AbstractConnectionAdapter的getConnections方法的重写:
public Collection<Connection> getConnections() { return connectionMap.values(); }而调用setAutoCommit时coonectionMap还是空。不是达到真实Connection对象的setAutoCommit的目的,为此先对其进行记录,当在产生真实的Connection对象时,再利用反射的方法让其调用recordMethodInvocation方法记录的方法。其中recordMethodInvocation方法继续自WrapperAdapter类。记录了调用的方法与传入方法的参数,实际是将JdbcMethodInvocation对象放入WrapperAdapter的成员变量
private final Collection<JdbcMethodInvocation> jdbcMethodInvocations = new ArrayList<>();
中。WrapperAdapter中的recordMethodInvocation方法如下:
protected final void recordMethodInvocation(final Class<?> targetClass, final String methodName, final Class<?>[] argumentTypes, final Object[] arguments) { try { jdbcMethodInvocations.add(new JdbcMethodInvocation(targetClass.getMethod(methodName, argumentTypes), arguments)); } catch (final NoSuchMethodException ex) { throw new ShardingJdbcException(ex); }}
jdbcMethodInvocations记录了所有调用的方法与参数。
@RequiredArgsConstructorpublic final class JdbcMethodInvocation { private final Method method;//调用的方法 private final Object[] arguments;//调用方法时传入的参数 /** * 调用方法. * * @param target 目标对象 */ public void invoke(final Object target) { try { method.invoke(target, arguments); } catch (final IllegalAccessException | InvocationTargetException ex) { throw new ShardingJdbcException("Invoke jdbc method exception", ex); } }}
现在来看一下,当真实的Connection对象产生时,通过反射的方法调用jdbcMetiondInvoctions记录的要调用的方法与参数。真实的Connection的产生就是通过我们上面提到的ShardingConnection的getConnection方法来达到的。
public Connection getConnection(final String dataSourceName, final SQLStatementType sqlStatementType) throws SQLException { Connection result = getConnectionInternal(dataSourceName, sqlStatementType); replayMethodsInvocation(result);//对记录的要执行的调用方法,在这里通过反射进行执行 return result;}
private Connection getConnectionInternal(final String dataSourceName, final SQLStatementType sqlStatementType) throws SQLException { if (connectionMap.containsKey(dataSourceName)) { return connectionMap.get(dataSourceName); } Context metricsContext = MetricsContext.start(Joiner.on("-").join("ShardingConnection-getConnection", dataSourceName)); DataSource dataSource = shardingContext.getShardingRule().getDataSourceRule().getDataSource(dataSourceName); if (dataSource instanceof MasterSlaveDataSource) { dataSource = ((MasterSlaveDataSource) dataSource).getDataSource(sqlStatementType); } Connection result = dataSource.getConnection(); MetricsContext.stop(metricsContext); connectionMap.put(dataSourceName, result);//将真实的Connction对象放入connectionMap中 return result; }
在 return result之前执行replayMethodsInvocations方法,该方法实际通过反射的方式对之前记录的方法进行了调用,这个例子中是对setAutoCommit方法进行了调用。replayMethodsInvocations继承自WrapperAdapter类,代码如下:
protected final void replayMethodsInvocation(final Object target) { for (JdbcMethodInvocation each : jdbcMethodInvocations) { each.invoke(target); } }
除了setAutoCommit方法外以下的方式也是通过这JdbcMethodInvocation实现的。
AbstractConnectionAdapter类:
- mmitsetReadOnly
- setTransactionIsolation
- setPoolable
- setFetchSize
- setEscapeProcessing
- setCursorName
- setMaxFieldSize
- setMaxRows
- setQueryTimeout
因调用这些方法时真实的Connection对象、Statement对象、PreparedStatement对象还没有产生,所以要先记录下,当在真实的对象产生时,再通过反射的方式调用在JdbcMethodInvocation中记录的方法。
后面还会分享更多ShardingJDBC源码的分析,不正确的地方欢迎指正,也欢迎一同分享。
2 0
- Sharding JDBC源码分析-JdbcMethodInvocation类的作用
- 【源码解析】Sharding-Jdbc模块分析
- 数据库中间件 Sharding-JDBC 源码分析 —— 结果归并
- 数据库中间件 Sharding-JDBC 源码分析 —— JDBC实现与读写分离
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(一)之语法解析
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(二)之SQL解析
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(三)之查询SQL
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(四)之插入SQL
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(六)之删除SQL
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 路由(一)分库分表配置
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 路由(二)之分库分表路由
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 改写
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— 分布式主键
- 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
- 数据库中间件 Sharding-JDBC 源码分析 —— 事务(一)之BED
- Sharding-JDBC 源码分析 —— SQL 路由(一)之分库分表配置
- sharding-jdbc源码阅读之Adapter
- sharding-jdbc源码阅读之soft transaction
- 机房那些事儿---为梦想时刻准备着之机房准备
- ContentProvider从入门到精通
- RedHat删除自带openJdk
- C++ 类访问控制(public/protected/private)
- 11.2考试整理
- Sharding JDBC源码分析-JdbcMethodInvocation类的作用
- LeetCode No.24 Swap Nodes in Pairs
- Fiddler 教程
- 51nod 1489 蜥蜴和地下室
- 位运算
- 矩阵快速幂
- QNX UI 程序全屏显示设置
- MyEclipse部分快捷键用法
- SSM搭建-Mybatis基于注解实现增删查改和多参数列表查询(20)