package com.ctrip.platform.dal.dao.client;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.ctrip.platform.dal.dao.DalClient;
import com.ctrip.platform.dal.dao.DalCommand;
import com.ctrip.platform.dal.dao.DalEventEnum;
import com.ctrip.platform.dal.dao.DalHintEnum;
import com.ctrip.platform.dal.dao.DalHints;
import com.ctrip.platform.dal.dao.DalResultSetExtractor;
import com.ctrip.platform.dal.dao.KeyHolder;
import com.ctrip.platform.dal.dao.StatementParameter;
import com.ctrip.platform.dal.dao.StatementParameters;
import com.ctrip.platform.dal.dao.configure.DalConfigure;
import com.ctrip.platform.dal.dao.helper.DalColumnMapRowMapper;
import com.ctrip.platform.dal.dao.helper.DalRowMapperExtractor;
import com.ctrip.platform.dal.exceptions.DalException;
/**
* The direct connection implementation for DalClient.
* @author jhhe
*/
public class DalDirectClient implements DalClient {
private DalStatementCreator stmtCreator = new DalStatementCreator();
private DalConnectionManager connManager;
private DalTransactionManager transManager;
public DalDirectClient(DalConfigure config, String logicDbName) {
connManager = new DalConnectionManager(logicDbName, config);
transManager = new DalTransactionManager(connManager);
}
@Override
public <T> T query(String sql, StatementParameters parameters, final DalHints hints, final DalResultSetExtractor<T> extractor)
throws SQLException {
ConnectionAction<T> action = new ConnectionAction<T>() {
@Override
public T execute() throws Exception {
conn = getConnection(hints, this);
preparedStatement = createPreparedStatement(conn, sql, parameters, hints);
DalWatcher.beginExecute();
rs = preparedStatement.executeQuery();
DalWatcher.endExectue();
return extractor.extract(rs);
}
};
action.populate(DalEventEnum.QUERY, sql, parameters);
return doInConnection(action, hints);
}
@Override
public List<?> query(String sql, StatementParameters parameters, final DalHints hints, final List<DalResultSetExtractor<?>> extractors)
throws SQLException {
ConnectionAction<List<?>> action = new ConnectionAction<List<?>>() {
@Override
public List<?> execute() throws Exception {
conn = getConnection(hints, this);
preparedStatement = createPreparedStatement(conn, sql, parameters, hints);
List<Object> result = new ArrayList<>();
DalWatcher.beginExecute();
preparedStatement.execute();
for(DalResultSetExtractor<?> extractor: extractors) {
ResultSet resultSet = preparedStatement.getResultSet();
result.add((Object)extractor.extract(resultSet));
preparedStatement.getMoreResults();
}
DalWatcher.endExectue();
return result;
}
};
action.populate(DalEventEnum.QUERY, sql, parameters);
return doInConnection(action, hints);
}
@Override
public int update(String sql, StatementParameters parameters, final DalHints hints)
throws SQLException {
final KeyHolder generatedKeyHolder = hints.getKeyHolder();
ConnectionAction<Integer> action = new ConnectionAction<Integer>() {
@Override
public Integer execute() throws Exception {
conn = getConnection(hints, this);
// For old generated free update, the parameters is nit compiled before invoke direct client
parameters.compile();
if(generatedKeyHolder == null)
preparedStatement = createPreparedStatement(conn, sql, parameters, hints);
else
preparedStatement = createPreparedStatement(conn, sql, parameters, hints, generatedKeyHolder);
DalWatcher.beginExecute();
int rows = preparedStatement.executeUpdate();
DalWatcher.endExectue();
if(generatedKeyHolder == null)
return rows;
List<Map<String, Object>> generatedKeys = generatedKeyHolder.getKeyList();
rs = preparedStatement.getGeneratedKeys();
if (rs == null)
return rows;
DalRowMapperExtractor<Map<String, Object>> rse =
new DalRowMapperExtractor<Map<String, Object>>(new DalColumnMapRowMapper());
generatedKeys.addAll(rse.extract(rs));
return rows;
}
};
action.populate(generatedKeyHolder == null ?DalEventEnum.UPDATE_SIMPLE:DalEventEnum.UPDATE_KH, sql, parameters);
return doInConnection(action, hints);
}
@Override
public int[] batchUpdate(String[] sqls, final DalHints hints) throws SQLException {
ConnectionAction<int[]> action = new ConnectionAction<int[]>() {
@Override
public int[] execute() throws Exception {
conn = getConnection(hints, this);
statement = createStatement(conn, hints);
for(String sql: sqls)
statement.addBatch(sql);
DalWatcher.beginExecute();
int[] ret = statement.executeBatch();
DalWatcher.endExectue();
return ret;
}
};
action.populate(sqls);
return executeBatch(action, hints);
}
@Override
public int[] batchUpdate(String sql, StatementParameters[] parametersList,
final DalHints hints) throws SQLException {
ConnectionAction<int[]> action = new ConnectionAction<int[]>() {
@Override
public int[] execute() throws Exception {
conn = getConnection(hints, this);
statement = createPreparedStatement(conn, sql, parametersList, hints);
DalWatcher.beginExecute();
int[] ret = statement.executeBatch();
DalWatcher.endExectue();
return ret;
}
};
action.populate(sql, parametersList);
return executeBatch(action, hints);
}
@Override
public void execute(DalCommand command, DalHints hints) throws SQLException {
final DalClient client = this;
ConnectionAction<?> action = new ConnectionAction<Object>() {
@Override
public Object execute() throws Exception {
command.execute(client);
return null;
}
};
action.populate(command);
doInTransaction(action, hints);
}
@Override
public void execute(final List<DalCommand> commands, final DalHints hints)
throws SQLException {
final DalClient client = this;
ConnectionAction<?> action = new ConnectionAction<Object>() {
@Override
public Object execute() throws Exception {
for(DalCommand cmd: commands) {
if(!cmd.execute(client))
break;
}
return null;
}
};
action.populate(commands);
doInTransaction(action, hints);
}
@Override
public Map<String, ?> call(String callString,
StatementParameters parameters, final DalHints hints) throws SQLException {
ConnectionAction<Map<String, ?>> action = new ConnectionAction<Map<String, ?>>() {
@Override
public Map<String, ?> execute() throws Exception {
List<StatementParameter> resultParameters = new ArrayList<StatementParameter>();
List<StatementParameter> callParameters = new ArrayList<StatementParameter>();
for (StatementParameter parameter : parameters.values()) {
if (parameter.isResultsParameter()) {
resultParameters.add(parameter);
} else
if(parameter.isOutParameter()){
callParameters.add(parameter);
}
}
if(hints.is(DalHintEnum.retrieveAllSpResults) && resultParameters.size() > 0)
throw new DalException("Dal hint 'autoRetrieveAllResults' should only be used when there is no special result parameter specified");
conn = getConnection(hints, this);
callableStatement = createCallableStatement(conn, callString, parameters, hints);
DalWatcher.beginExecute();
boolean retVal = callableStatement.execute();
int updateCount = callableStatement.getUpdateCount();
DalWatcher.endExectue();
Map<String, Object> returnedResults = new LinkedHashMap<String, Object>();
if (retVal || updateCount != -1) {
returnedResults.putAll(extractReturnedResults(callableStatement, resultParameters, updateCount, hints));
}
returnedResults.putAll(extractOutputParameters(callableStatement, callParameters));
return returnedResults;
}
};
action.populateSp(callString, parameters);
return doInConnection(action, hints);
}
@Override
public int[] batchCall(String callString,
StatementParameters[] parametersList, final DalHints hints)
throws SQLException {
ConnectionAction<int[]> action = new ConnectionAction<int[]>() {
@Override
public int[] execute() throws Exception {
conn = getConnection(hints, this);
callableStatement = createCallableStatement(conn, callString, parametersList, hints);
DalWatcher.beginExecute();
int[] ret = callableStatement.executeBatch();
DalWatcher.endExectue();
return ret;
}
};
action.populateSp(callString, parametersList);
return executeBatch(action, hints);
}
private Map<String, Object> extractReturnedResults(CallableStatement statement, List<StatementParameter> resultParameters, int updateCount, DalHints hints) throws SQLException {
Map<String, Object> returnedResults = new LinkedHashMap<String, Object>();
if(hints.is(DalHintEnum.skipResultsProcessing))
return returnedResults;
if(hints.is(DalHintEnum.retrieveAllSpResults))
return autoExtractReturnedResults(statement, updateCount);
if(resultParameters.size() == 0)
return returnedResults;
boolean moreResults;
int index = 0;
do {
// If resultParameters is not the same as what exactly returned, there will be exception. You just
// need to add enough result parameter to avoid this or you can set skipResultsProcessing
String key = resultParameters.get(index).getName();
Object value = updateCount == -1?
resultParameters.get(index).getResultSetExtractor().extract(statement.getResultSet()) :
updateCount;
moreResults = statement.getMoreResults();
updateCount = statement.getUpdateCount();
index++;
returnedResults.put(key, value);
}
while (moreResults || updateCount != -1);
return returnedResults;
}
private Map<String, Object> autoExtractReturnedResults(CallableStatement statement, int updateCount) throws SQLException {
Map<String, Object> returnedResults = new LinkedHashMap<String, Object>();
boolean moreResults;
int index = 0;
DalRowMapperExtractor<Map<String, Object>> extractor;
do {
extractor = new DalRowMapperExtractor<>(new DalColumnMapRowMapper());
String key = (updateCount == -1 ? "ResultSet_" : "UpdateCount_") + index;
Object value = updateCount == -1 ? extractor.extract(statement.getResultSet()) :
updateCount;
moreResults = statement.getMoreResults();
updateCount = statement.getUpdateCount();
index++;
returnedResults.put(key, value);
}
while (moreResults || updateCount != -1);
return returnedResults;
}
private Map<String, Object> extractOutputParameters(CallableStatement statement, List<StatementParameter> callParameters)
throws SQLException {
Map<String, Object> returnedResults = new LinkedHashMap<String, Object>();
for (StatementParameter parameter : callParameters) {
Object value = parameter.getName() == null ?
statement.getObject(parameter.getIndex()):
statement.getObject(parameter.getName());
parameter.setValue(value);
if (value instanceof ResultSet) {
value = parameter.getResultSetExtractor().extract(statement.getResultSet());
}
returnedResults.put(parameter.getName(), value);
}
return returnedResults;
}
private <T> T executeBatch(ConnectionAction<T> action, DalHints hints)
throws SQLException {
if(hints.is(DalHintEnum.forceAutoCommit)){
return doInConnection(action, hints);
}else{
return doInTransaction(action, hints);
}
}
private <T> T doInConnection(ConnectionAction<T> action, DalHints hints)
throws SQLException {
return connManager.doInConnection(action, hints);
}
private <T> T doInTransaction(ConnectionAction<T> action, DalHints hints)
throws SQLException {
return transManager.doInTransaction(action, hints);
}
public Connection getConnection(DalHints hints, ConnectionAction<?> action) throws SQLException {
DalWatcher.beginConnect();
action.connHolder = transManager.getConnection(hints, action.operation);
Connection conn = action.connHolder.getConn();
DalWatcher.endConnect();
return conn;
}
private Statement createStatement(Connection conn, DalHints hints) throws Exception {
return stmtCreator.createStatement(conn, hints);
}
private PreparedStatement createPreparedStatement(Connection conn, String sql, StatementParameters parameters, DalHints hints) throws Exception {
return stmtCreator.createPreparedStatement(conn, sql, parameters, hints);
}
private PreparedStatement createPreparedStatement(Connection conn, String sql, StatementParameters parameters, DalHints hints, KeyHolder keyHolder) throws Exception {
return stmtCreator.createPreparedStatement(conn, sql, parameters, hints, keyHolder);
}
private PreparedStatement createPreparedStatement(Connection conn, String sql, StatementParameters[] parametersList, DalHints hints) throws Exception {
return stmtCreator.createPreparedStatement(conn, sql, parametersList, hints);
}
private CallableStatement createCallableStatement(Connection conn, String sql, StatementParameters parameters, DalHints hints) throws Exception {
return stmtCreator.createCallableStatement(conn, sql, parameters, hints);
}
private CallableStatement createCallableStatement(Connection conn, String sql, StatementParameters[] parametersList, DalHints hints) throws Exception {
return stmtCreator.createCallableStatement(conn, sql, parametersList, hints);
}
}