/*
* Copyright 2008-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.hasor.data.jdbc.core;
import net.hasor.core.Hasor;
import net.hasor.data.datasource.ConnectionProxy;
import net.hasor.data.jdbc.ConnectionCallback;
import net.hasor.data.transaction.TranManager;
import javax.sql.DataSource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
*
* @version : 2013-10-16
* @author 赵永春(zyc@hasor.net)
*/
public class JdbcConnection extends JdbcAccessor {
/*JDBC查询和从结果集里面每次取设置行数,循环去取,直到取完。合理设置该参数可以避免内存异常。
* 如果这个变量被设置为非零值,它将被用于设置 statements 的 fetchSize 属性。*/
private int fetchSize = 0;
/*从 JDBC 中可以查询的最大行数。
* 如果这个变量被设置为非零值,它将被用于设置 statements 的 maxRows 属性。*/
private int maxRows = 0;
/*从 JDBC 中可以查询的最大行数。
* 如果这个变量被设置为非零值,它将被用于设置 statements 的 queryTimeout 属性。*/
private int queryTimeout = 0;
//
/**
* Construct a new JdbcConnection for bean usage.
* <p>Note: The DataSource has to be set before using the instance.
* @see #setDataSource
*/
public JdbcConnection() {
}
/**
* Construct a new JdbcConnection, given a DataSource to obtain connections from.
* <p>Note: This will not trigger initialization of the exception translator.
* @param dataSource the JDBC DataSource to obtain connections from
*/
public JdbcConnection(final DataSource dataSource) {
this.setDataSource(dataSource);
}
/**
* Construct a new JdbcConnection, given a Connection to obtain connections from.
* <p>Note: This will not trigger initialization of the exception translator.
* @param conn the JDBC Connection
*/
public JdbcConnection(final Connection conn) {
this.setConnection(conn);
}
//
public int getFetchSize() {
return this.fetchSize;
}
public void setFetchSize(final int fetchSize) {
this.fetchSize = fetchSize;
}
public int getMaxRows() {
return this.maxRows;
}
public void setMaxRows(final int maxRows) {
this.maxRows = maxRows;
}
public int getQueryTimeout() {
return this.queryTimeout;
}
public void setQueryTimeout(final int queryTimeout) {
this.queryTimeout = queryTimeout;
}
public <T> T execute(final ConnectionCallback<T> action) throws SQLException {
Hasor.assertIsNotNull(action, "Callback object must not be null");
//
Connection localConn = this.getConnection();
DataSource localDS = this.getDataSource();//获取数据源
boolean usingDS = localConn == null;
if (logger.isDebugEnabled()) {
logger.debug("database connection using DataSource = {}", usingDS);
}
//
ConnectionProxy useConn = null;
try {
if (usingDS) {
localConn = TranManager.currentConnection(localDS);
useConn = this.newProxyConnection(localConn, localDS);//代理连接
} else {
useConn = this.newProxyConnection(localConn, null);//代理连接
}
return action.doInConnection(useConn);
} finally {
if (usingDS) {
if (localConn != null) {
localConn.close();
}
}
}
}
/**对Statement的属性进行设置。设置 JDBC Statement 对象的 fetchSize、maxRows、Timeout等参数。*/
protected void applyStatementSettings(final Statement stmt) throws SQLException {
int fetchSize = this.getFetchSize();
if (fetchSize > 0) {
stmt.setFetchSize(fetchSize);
}
int maxRows = this.getMaxRows();
if (maxRows > 0) {
stmt.setMaxRows(maxRows);
}
int timeout = this.getQueryTimeout();
if (timeout > 0) {
stmt.setQueryTimeout(timeout);
}
}
/**获取与本地线程绑定的数据库连接,JDBC 框架会维护这个连接的事务。开发者不必关心该连接的事务管理,以及资源释放操作。*/
private ConnectionProxy newProxyConnection(final Connection target, final DataSource targetSource) {
Hasor.assertIsNotNull(target, "Connection is null.");
CloseSuppressingInvocationHandler handler = new CloseSuppressingInvocationHandler(target, targetSource);
return (ConnectionProxy) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(), new Class[] { ConnectionProxy.class }, handler);
}
/**Connection 接口代理,目的是为了控制一些方法的调用。同时进行一些特殊类型的处理。*/
private class CloseSuppressingInvocationHandler implements InvocationHandler {
private final Connection target;
private final DataSource targetSource;
public CloseSuppressingInvocationHandler(final Connection target, final DataSource targetSource) {
this.target = target;
this.targetSource = targetSource;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
// Invocation on ConnectionProxy interface coming in...
if (method.getName().equals("getTargetConnection"))
// Handle getTargetConnection method: return underlying Connection.
return this.target;
else if (method.getName().equals("getTargetSource"))
// Handle getTargetConnection method: return underlying DataSource.
return this.targetSource;
else if (method.getName().equals("equals"))
// Only consider equal when proxies are identical.
return proxy == args[0];
else if (method.getName().equals("hashCode"))
// Use hashCode of PersistenceManager proxy.
return System.identityHashCode(proxy);
else if (method.getName().equals("close"))
return null;
// Invoke method on target Connection.
try {
Object retVal = method.invoke(this.target, args);
// If return value is a JDBC Statement, apply statement settings (fetch size, max rows, transaction timeout).
if (retVal instanceof Statement)
JdbcConnection.this.applyStatementSettings((Statement) retVal);
return retVal;
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}