在查询上生成错误的列

我们遇到了NHibernate的间歇性问题,它偶尔会在SQL上生成一个错误列的查询。 如果我们重新启动应用程序,问题就不再发生(有时需要多次重启)。 出现问题时,在该进程的生命周期内,它始终会为受影响的实体生成错误的SQL。 它并不总是受影响的实体。

这是一个ASP.NET应用程序,其中SessionFactory是在Application_Start事件期间创建的。 所有配置和映射都由代码完成。

我们没有更多的想法如何测试或调试应用程序,我开始假设NHibernate中有一些错误,因为应用程序在重新启动时修复了自己。 任何想法/提示将不胜感激!

这是一个例子:

实体

namespace Example.Clinicas { public partial class Clinica : Entidade // Abstract base class that has a property Handle { public virtual string Ddd { get; set; } public virtual string Ddd2 { get; set; } public virtual long? Duracao { get; set; } public virtual string Numero { get; set; } public virtual string Numero2 { get; set; } public virtual string Prefixo { get; set; } public virtual string Prefixo2 { get; set; } public virtual long? HandlePrestador { get; set; } public virtual Example.Prestadores.Prestador Prestador { get; set; } } } 

制图

 namespace Example.Clinicas.Mappings { public class ClinicaMapping : ClassMapping { public ClinicaMapping() { Table("CLI_CLINICA"); Id(x => x.Handle, map => { map.Column("HANDLE"); map.Generator(Generators.Sequence, g => g.Params(new { sequence = "SEQ_AUTO1816" })); }); Property(x => x.Ddd, map => map.Column( c=> { c.Name("DDD1"); c.Length(4); })); Property(x => x.Ddd2, map => map.Column( c=> { c.Name("DDD2"); c.Length(4); })); Property(x => x.Duracao, map => map.Column("INTERVALOAGENDA")); Property(x => x.Numero, map => map.Column( c=> { c.Name("NUMERO1"); c.Length(5); })); Property(x => x.Numero2, map => map.Column( c=> { c.Name("NUMERO2"); c.Length(5); })); Property(x => x.Prefixo, map => map.Column( c=> { c.Name("PREFIXO1"); c.Length(5); })); Property(x => x.Prefixo2, map => map.Column( c=> { c.Name("PREFIXO2"); c.Length(5); })); Property(x => x.HandlePrestador, map => map.Column("PRESTADOR")); ManyToOne(x => x.Prestador, map => { map.Column("PRESTADOR"); map.Insert(false); map.Update(false); }); } } } 

命令

 Session.Query().FirstOrDefault(); 

生成的SQL

 select HANDLE489_, DDD2_489_, DDD3_489_, INTERVAL4_489_, NUMERO5_489_, NUMERO6_489_, PREFIXO7_489_, FATURADE8_489_, PRESTADOR489_ from (select clinica0_.HANDLE as HANDLE489_, clinica0_.DDD1 as DDD2_489_, clinica0_.DDD2 as DDD3_489_, clinica0_.INTERVALOAGENDA as INTERVAL4_489_, clinica0_.NUMERO1 as NUMERO5_489_, clinica0_.NUMERO2 as NUMERO6_489_, clinica0_.PREFIXO1 as PREFIXO7_489_, clinica0_.FATURADEPARCELAMENTO as FATURADE8_489_, clinica0_.PRESTADOR as PRESTADOR489_ from CLI_CLINICA clinica0_) where rownum <= 1 

例外

 ORA-00904: "CLINICA0_"."FATURADEPARCELAMENTO": invalid identifier 

有趣的观察:

  • 它更可能影响更大的实体(具有更多数量的属性),但偶尔也会影响较小的实体;
  • 生成的SQL始终具有与映射属性相同的列数;
  • SQL上的列与映射类上的映射属性的顺序相同;
  • 错误的列将替换现有的列;
  • 错误的列是不同映射实体中的有效列;
  • 受影响的实体与具有错误列的实体之间没有关系;

其他详情:

  • .NET版本: 4.0
  • NHibernate版本: 3.3.3.400
  • 按代码映射: NHibernate.Mapping.ByCode
  • 按代码配置: NHibernate.Cfg

加载映射

 var mapper = new ModelMapper(); foreach (var assembly in resolver.GetAssemblies()) // resolver is a class that gets all the assemblies for the current application mapper.AddMappings(assembly.GetExportedTypes()); var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); return mapping; 

SessionFactory配置

 var configure = new Configuration(); configure.DataBaseIntegration(x => { x.Dialect(); // Custom class x.ConnectionString = ConnectionString; x.BatchSize = 100; x.Driver(); // Custom class x.MaximumDepthOfOuterJoinFetching = 10; x.Timeout = 250; x.PrepareCommands = true; x.HqlToSqlSubstitutions = "true 'S', false 'N', yes 'S', no 'N'"; x.LogFormattedSql = true; x.LogSqlInConsole = true; x.AutoCommentSql = true; x.IsolationLevel = IsolationLevel.ReadCommitted; x.ConnectionProvider(); // Custom class }); configure.Properties.Add(new KeyValuePair("hibernate.command_timeout", "250")); configure.Proxy(x => x.ProxyFactoryFactory()); configure.LinqToHqlGeneratorsRegistry(); configure.CurrentSessionContext(); var mapping = GetMappings(); // Method showed above mapping.autoimport = false; configure.AddMapping(mapping); var listener = new AuditEventListener(); configure.EventListeners.PostInsertEventListeners = new IPostInsertEventListener[] { listener }; configure.EventListeners.PostUpdateEventListeners = new IPostUpdateEventListener[] { listener }; configure.SessionFactory().GenerateStatistics(); return configure; 

我在NHibernate用户Google网上论坛上提出了同样的问题,有人认为他们已经找到了根本原因(并且还提出了解决方案):

https://groups.google.com/forum/#!topic/nhusers/BZoBoyWQEvs

问题代码在PropertyPath.Equals(PropertyPath)中,它尝试仅使用哈希代码来确定相等性。 这适用于较小的代码库,因为默认的Object.GetHashCode()返回顺序对象索引。 但是,在垃圾收集之后,这些索引会被重用,因为删除了最终对象并创建了新对象…这导致多个对象获得相同的哈希码…一旦垃圾收集开始,属性路径就有机会共享相同的哈希码,这意味着他们最终将混合他们的定制器用于碰撞属性,因此错误的列名…

如果你想修复这个bug,你可以修补NH源代码:

如果您有自己的NH源副本,可以通过更改NHibernate / Mapping / ByCode / PropertyPath.cs第66行来修复该错误:

return hashCode == other.GetHashCode();

至:

return hashCode == other.GetHashCode() && ToString() == other.ToString();

有关此问题的详细信息,请查看Google网上论坛。

看起来像“信用卡支付”FATURADEPARCELAMENTO是您的“贷方”对象PRESTADOR的属性,如果是这种情况,它需要是一个参考而不是映射中的属性。 希望有助于或至少让您指出正确的方向

引用将代替你的行属性(x => x.HandlePrestador,map => map.Column(“PRESTADOR”)); 并且会接近References(x => x.HandlePrestador)

检查你的querylog以查看它的runnig是什么类型的查询,在你的sql中,你可以发现问题。