#region Apache Notice
/*****************************************************************************
* $Header: $
* $Revision: 591573 $
* $Date$
*
* iBATIS.NET Data Mapper
* Copyright (C) 2005 - The Apache Software Foundation
*
*
* 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.
*
********************************************************************************/
#endregion
using System;
using System.Collections.Specialized;
using System.Data;
using System.Reflection;
using System.Text;
using Apache.Ibatis.Common.Logging;
using Apache.Ibatis.Common.Utilities.Objects;
using Apache.Ibatis.DataMapper.MappedStatements;
using Apache.Ibatis.DataMapper.Model.ParameterMapping;
using Apache.Ibatis.DataMapper.Model.Statements;
using Apache.Ibatis.DataMapper.Exceptions;
using Apache.Ibatis.DataMapper.Scope;
using Apache.Ibatis.DataMapper.Session;
using Apache.Ibatis.Common.Data;
namespace Apache.Ibatis.DataMapper.Data
{
///
/// Summary description for DefaultPreparedCommand.
///
public class DefaultPreparedCommand : IPreparedCommand
{
private static readonly ILog log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType );
#region IPreparedCommand Members
///
/// Create an IDbCommand for the current session and statement then fill in its IDataParameters based on parameterObject.
///
///
/// The SqlMapSession
/// The IStatement
///
/// The parameter object that will fill the sql parameter
///
///
/// The constructed IDbCommand is available from request.IDbCommand
///
public void Create(RequestScope request, ISession session, IStatement statement, object parameterObject )
{
// the IDbConnection & the IDbTransaction are assign in the CreateCommand
IDbProvider dbProvider = session.SessionFactory.DataSource.DbProvider;
request.IDbCommand = new DbCommandDecorator(CreateCommandAndEnlistTransaction(dbProvider, statement.CommandType, session), request);
request.IDbCommand.CommandText = request.PreparedStatement.PreparedSql;
if (log.IsDebugEnabled)
{
log.Debug("Preparing to apply parameter information to Statement Id: [" + statement.Id + "] based off of PreparedStatement: [" + request.IDbCommand.CommandText + "]");
}
ApplyParameterMap( session.SessionFactory.DataSource.DbProvider , request.IDbCommand, request, statement, parameterObject );
}
#endregion
///
/// Creates an IDbCommand fills its parameters based on the parameterObject.
///
///
///
/// An IDbCommand with all the IDataParameter filled.
public IDbCommand CreateCommand(IMappedStatement mappedStatement, object parameterObject)
{
IDbProvider dbProvider = mappedStatement.ModelStore.SessionFactory.DataSource.DbProvider;
IStatement statement = mappedStatement.Statement;
RequestScope request = statement.Sql.GetRequestScope(mappedStatement, parameterObject, null);
request.IDbCommand = CreateCommandAndEnlistTransaction(dbProvider, statement.CommandType, null);
request.IDbCommand.CommandText = request.PreparedStatement.PreparedSql;
request.IDbCommand.CommandType = statement.CommandType;
if (log.IsDebugEnabled)
{
log.Debug("Preparing to apply parameter information to Statement Id: [" + statement.Id + "] based off of PreparedStatement: [" + request.IDbCommand.CommandText + "]");
}
ApplyParameterMap(dbProvider, request.IDbCommand, request, statement, parameterObject);
return request.IDbCommand;
}
///
/// Creates the command.
///
/// The dbProvider.
/// If session is not null, attaches the session's Connection and Transaction to the command and sets the command's Timeout property.
/// Type of the command.
/// the command
protected virtual IDbCommand CreateCommandAndEnlistTransaction(IDbProvider dbProvider, CommandType commandType, ISession session)
{
IDbCommand command = dbProvider.CreateCommand();
command.CommandType = commandType;
if (session != null)
{
command.Connection = session.Connection;
SetCommandTimeout(command, session.SessionFactory.DataSource.CommandTimeout);
// Assign transaction
if (session.Transaction != null)
{
session.Transaction.Enlist(command);
}
}
return command;
}
///
/// Sets the command timeout.
///
/// The CMD.
/// The command timeout.
protected virtual void SetCommandTimeout(IDbCommand cmd, int commandTimeout)
{
if (commandTimeout >= 0)
{
try
{
cmd.CommandTimeout = commandTimeout;
}
catch (Exception e)
{
if (log.IsWarnEnabled)
{
log.Warn("Unable to set the IDbCommand.CommandTimeout property to [" + commandTimeout + "]. Cause: " + e.Message, e);
}
}
}
}
///
/// Applies the parameter map.
///
/// The dbProvider.
/// The command.
/// The request.
/// The statement.
/// The parameter object.
protected virtual void ApplyParameterMap
(IDbProvider dbProvider, IDbCommand command,
RequestScope request, IStatement statement, object parameterObject )
{
StringCollection properties = request.PreparedStatement.DbParametersName;
IDbDataParameter[] parameters = request.PreparedStatement.DbParameters;
StringBuilder paramLogList = new StringBuilder(); // Log info
StringBuilder typeLogList = new StringBuilder(); // Log info
int count = properties.Count;
for ( int i = 0; i < count; ++i )
{
IDbDataParameter sqlParameter = parameters[i];
IDbDataParameter parameterCopy = command.CreateParameter();
ParameterProperty property = request.ParameterMap.GetProperty(i);
#region Logging
if (log.IsDebugEnabled)
{
paramLogList.Append(sqlParameter.ParameterName);
paramLogList.Append("=[");
typeLogList.Append(sqlParameter.ParameterName);
typeLogList.Append("=[");
}
#endregion
if (command.CommandType == CommandType.StoredProcedure)
{
#region store procedure command
// A store procedure must always use a ParameterMap
// to indicate the mapping order of the properties to the columns
if (request.ParameterMap == null) // Inline Parameters
{
throw new DataMapperException("A procedure statement tag must alway have a parameterMap attribute, which is not the case for the procedure '"+statement.Id+"'.");
}
// Parameters via ParameterMap
if (property.DirectionAttribute.Length == 0)
{
property.Direction = sqlParameter.Direction;
}
sqlParameter.Direction = property.Direction;
#endregion
}
#region Logging
if (log.IsDebugEnabled)
{
paramLogList.Append(property.PropertyName);
paramLogList.Append(",");
}
#endregion
request.ParameterMap.SetParameter(property, parameterCopy, parameterObject );
parameterCopy.Direction = sqlParameter.Direction;
// With a ParameterMap, we could specify the ParameterDbTypeProperty
if (request.ParameterMap != null)
{
if (!string.IsNullOrEmpty(property.DbType))
{
string dbTypePropertyName = dbProvider.ParameterDbTypeProperty;
object propertyValue = ObjectProbe.GetMemberValue(sqlParameter, dbTypePropertyName, request.DataExchangeFactory.AccessorFactory);
ObjectProbe.SetMemberValue(parameterCopy, dbTypePropertyName, propertyValue,
request.DataExchangeFactory.ObjectFactory, request.DataExchangeFactory.AccessorFactory);
}
}
#region Logging
if (log.IsDebugEnabled)
{
if (parameterCopy.Value == DBNull.Value)
{
paramLogList.Append("null");
paramLogList.Append("], ");
typeLogList.Append("System.DBNull, null");
typeLogList.Append("], ");
}
else
{
paramLogList.Append(parameterCopy.Value.ToString());
paramLogList.Append("], ");
// sqlParameter.DbType could be null (as with Npgsql)
// if PreparedStatementFactory did not find a dbType for the parameter in:
// line 225: "if (property.DbType.Length >0)"
// Use parameterCopy.DbType
//typeLogList.Append( sqlParameter.DbType.ToString() );
typeLogList.Append(parameterCopy.DbType.ToString());
typeLogList.Append(", ");
typeLogList.Append(parameterCopy.Value.GetType().ToString());
typeLogList.Append("], ");
}
}
#endregion
ApplyDbProviderParameterSettings(dbProvider, sqlParameter, parameterCopy);
parameterCopy.ParameterName = sqlParameter.ParameterName;
command.Parameters.Add( parameterCopy );
}
#region Logging
if (log.IsDebugEnabled && properties.Count>0)
{
log.Debug("Statement Id: [" + statement.Id + "] Parameters: [" + paramLogList.ToString(0, paramLogList.Length - 2) + "]");
log.Debug("Statement Id: [" + statement.Id + "] Types: [" + typeLogList.ToString(0, typeLogList.Length - 2) + "]");
}
#endregion
}
///
/// Applies IDbProvider specific settings to the dbParameter by first checking values on the templateParameter.
///
///
/// source
/// destination
protected virtual void ApplyDbProviderParameterSettings(IDbProvider dbProvider, IDbDataParameter templateParameter, IDbDataParameter dbParameter)
{
// JIRA-49 Fixes (size, precision, and scale)
if (dbProvider.SetDbParameterSize)
{
if (templateParameter.Size > 0)
{
dbParameter.Size = templateParameter.Size;
}
}
if (dbProvider.SetDbParameterPrecision)
{
dbParameter.Precision = templateParameter.Precision;
}
if (dbProvider.SetDbParameterScale)
{
dbParameter.Scale = templateParameter.Scale;
}
}
}
}