0

我编写了一个小程序来将一些记录插入到 oracle 数据库中。如果我直接连接到 oracle,程序运行良好。但是,如果我尝试通过 ODBC 连接,该程序将无法运行。它能够执行SELECT语句,但不能执行INSERT语句。

当我开发程​​序时,我在我的 NHibernate 配置中使用了这些设置:

<property name="connection.provider">
    NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<property name="connection.connection_string">
    Data Source=192.168.1.43:1521/xxxx;User ID=xxxx;Password=xxxx
</property>
<property name="connection.driver_class">
    NHibernate.Driver.OracleClientDriver
</property>
<property name="hbm2ddl.keywords">auto-quote</property>

通过这些设置,程序可以工作。

对于生产,我使用以下设置:

<property name="connection.provider">
    NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
<property name="connection.connection_string">
    DSN=TRUST;Server=oracle;UID=xxxx;PWD=xxxx
</property>
<property name="connection.driver_class">
    NHibernate.Driver.OdbcDriver
</property>
<property name="hbm2ddl.keywords">auto-quote</property>

使用生产设置,当我尝试插入数据时出现此异常:

NHibernate.Exceptions.GenericADOException: could not insert: [Domain.Phoenix.StructureAssign#35159][SQL: INSERT INTO STRUCTURE_ASSIGN (CLIENT_ID, STRUCTURE, NAME_CODE, START_DATE, FINISH_DATE, MEMO_NUMBER, ALTERNATE, ALTERNATE_NAME_CODE, ADMIN_NAME, AUDIT_NAME, VALID_DATE, AUDIT_ACTION, DIRECTOR_CLASS, STAT_SEQ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)] ---> System.Data.Odbc.OdbcException
   at System.Data.Odbc.OdbcConnection.HandleError(OdbcHandle hrHandle, RetCode retcode)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader, Object[] methodArguments, SQL_API odbcApiMethod)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, String method, Boolean needReader)
   at System.Data.Odbc.OdbcCommand.ExecuteNonQuery()
   at NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)
   at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
   --- End of inner exception stack trace ---
   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
   at NHibernate.Impl.StatelessSessionImpl.Insert(String entityName, Object entity)
   at NHibernate.Impl.StatelessSessionImpl.Insert(Object entity)
   at Domain.Repositories.StatelessRepository.Add[T](T model) in c:\Users\Owner\Dev\\Domain\Repositories\StatelessRepository.cs:line 112
   at PatchImport.Program.InsertBeneficiaries() in c:\Users\Owner\Dev\PatchImport\Program.cs:line 232
   at PatchImport.Program.Main(String[] args) in c:\Users\Owner\Dev\PatchImport\Program.cs:line 60

内部异常没有附加任何消息。

我查看了 NHibernate 生成的 SQL。使用开发设置,为输入生成的 SQL 如下所示:

NHibernate: select STRUCTURE_SEQ.nextval from dual
NHibernate: INSERT INTO STRUCTURE_ASSIGN 
    (CLIENT_ID, STRUCTURE, NAME_CODE, START_DATE, STAT_SEQ) 
    VALUES (:p0, :p1, :p2, :p3, :p4);
    :p0 = 'MAST17' [Type: String (6)],
    :p1 = 'BENEFICIARY' [Type: String (11)],
    :p2 = 27845 [Type: Int64 (0)],
    :p3 = 28/10/2008 00:00:00 [Type: DateTime (0)],
    :p4 = 42830 [Type: Int32 (0)]

但是,使用生产设置生成的 SQL 如下所示:

NHibernate: select STRUCTURE_SEQ.nextval from dual
NHibernate: INSERT INTO STRUCTURE_ASSIGN 
    (CLIENT_ID, STRUCTURE, NAME_CODE, START_DATE, STAT_SEQ) 
    VALUES (?, ?, ?, ?, ?);
    p0 = 'SCAR03' [Type: String (6)],
    p1 = 'BENEFICIARY' [Type: String (11)],
    p2 = 21525 [Type: Int64 (0)],
    p3 = 07/11/1977 00:00:00[Type: DateTime (0)],
    p4 = 35159 [Type: Int32 (0)]

代码中没有其他任何内容发生变化——只有配置文件。

有谁知道这里出了什么问题?我怀疑它要么是使用序列来生成表(STAT_SEQ列)的 ID,要么是导致第二组 SQL 中的参数为?而不是p0等的任何原因p1

4

2 回答 2

1

NHibernate OdbcDriver使用位置参数。通过查看DriverBase您可以看到,即使它使用的是“?” 在 SQL 语句 NHibernate 中将参数命名为 p0、p1、p2 等......不幸的是,Oracle ODBC 驱动程序不喜欢这个,这可以从对这个问题的响应中得到证明。

解决问题的最简单方法是创建基于DriverBase的自定义方言,但我在下面进行了更改。这种方言将生成名称为空字符串的参数,根据我的研究,这是 Oracle ODBC 驱动程序所需的格式。

请注意,我不能 100% 确定这是正确的格式,我已经看到了一些参数名称格式为 ?0、?1、?2 等的示例……但如果是这种情况,您可以只是修改ToParameterName返回StringHelper.SqlParameter + index

另请注意,您不能只继承DriverBase和覆盖必要的属性,因为所需的更改之一是静态属性。

我还建议在JIRA上创建票证,因为这种行为绝对是一个错误。

public class OracleOdbcDriver : IDriver, ISqlParameterFormatter
{
  ....
  public bool UseNamedPrefixInSql
  {
    get { return false; }
  }

  public bool UseNamedPrefixInParameter
  {
    get { return false; }
  }

  public string NamedPrefix
  {
    get { return string.Empty; }
  }
  ....
  private static string ToParameterName(int index)
  {
    return String.Empty;
  }
  ....
}
于 2013-04-26T18:41:13.057 回答
0

哇,这肯定是一个很难确定的问题。

据我所知,错误在这一行:

p2 = 21525 [Type: Int64 (0)],

特别是,如果我将类型更改为NameCodeaint而不是 a long,则插入成功。不知何故,ODBC 遇到了 64 位整数。它也没有任何描述性的错误消息就中断了,这非常令人沮丧。由于此问题仅在使用 ODBC 时出现,而不是在正常运行时出现OracleClientDriver,因此它必须是 ODBC 中的错误,而不是我的数据库配置的问题。

我也尝试直接使用 ODBC:

var queryString = "insert into STRUCTURE_ASSIGN (CLIENT_ID, \"STRUCTURE\", NAME_CODE, STAT_SEQ, "
    + "START_DATE, FINISH_DATE, MEMO_NUMBER, ALTERNATE, ALTERNATE_NAME_CODE, ADMIN_NAME, "
    + "AUDIT_NAME, VALID_DATE, AUDIT_ACTION, DIRECTOR_CLASS) "
    + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?);";

using (var odbcConnection = new OdbcConnection(connectionString))
{
    OdbcCommand com = new OdbcCommand(queryString, odbcConnection);

    com.Parameters.AddWithValue("?", "aaa");
    com.Parameters.AddWithValue("?", "aaa");
    com.Parameters.AddWithValue("?", 1234);
    com.Parameters.AddWithValue("?", 100000);

    com.Parameters.AddWithValue("?", DateTime.Now);
    com.Parameters.AddWithValue("?", DateTime.Now);
    com.Parameters.AddWithValue("?", 1234);
    com.Parameters.AddWithValue("?", 'N');
    com.Parameters.AddWithValue("?", 1234);
    com.Parameters.AddWithValue("?", "TRUST");

    com.Parameters.AddWithValue("?", "TRUST");
    com.Parameters.AddWithValue("?", DateTime.Now);
    com.Parameters.AddWithValue("?", 'C');
    com.Parameters.AddWithValue("?", 'A');

    odbcConnection.Open();
    var rd = com.ExecuteNonQuery();
    odbcConnection.Close();
}

上面的代码有效。但是,如果您像这样更改其中一行:

com.Parameters.AddWithValue("?", (long)1234);

ODBC 会抛出与 NHibernate 包装相同的无用错误消息,因此这不是 NHibernate 中的错误。

概括

如果您遇到类似问题,请尝试将任何long变量更改为int. 您可能还想尝试切换其他字段的类型。

于 2013-04-29T16:07:28.340 回答