如何在NHibernate中将Oracle序列映射至非ID列?
Oracle序列映射至非主键属性的问题解决
现有Oracle表结构
| 字段名 | 数据类型 |
|---|---|
| ID | NUMBER |
| FOO | VARCHAR2 |
| BAR | NUMBER |
对应的C#实体类
public class SomeClass { public virtual long Id { get; set; } public virtual string Foo { get; set; } public virtual long Bar { get; set; } }
问题描述
需将实体的Bar属性映射至Oracle序列ABC.Other_SEQ,但插入数据时Bar字段始终为NULL,其余数据可正常插入。
已尝试的无效方案
方案1:Formula配置(无效果无报错)
配置后插入时Bar仍为NULL,无任何报错信息:
public class SomeTableMapping { Schema("ABC"); Table("SomeTable"); // ID序列生成器工作正常 Id(p => p.Id, map => { map.Column("ID"); map.Generator(Generators.Sequence, gMap => gMap.Params(new { sequence = "MyID_SEQ" })); }); Property(p => p.Foo, map => map.Column("FOO")); // 此配置无效果,也不报错 Property(p => p.Bar, map => map.Formula("SELECT ABC.Other_SEQ.nextval BAR FROM dual")); }
注:Formula仅用于查询阶段计算字段值,不会影响插入语句的字段赋值。
方案2:添加Generated配置(实体有值但数据库无记录)
配置后实体实例的Bar会生成值,但数据库中对应记录的Bar字段仍为NULL:
Property(p => p.Bar, map => { map.Column("BAR"); map.Formula("ABC.Other_SEQ.nextval"); map.Generated(PropertyGeneration.Insert); });
注:Formula无法触发数据库插入时的序列取值,仅在内存中生成值但未写入数据库。
可行的映射方案
方案1:利用数据库列默认值(推荐,Oracle 12c+支持)
步骤1:修改Oracle表结构,给BAR列设置序列默认值
ALTER TABLE ABC.SomeTable MODIFY BAR DEFAULT ABC.Other_SEQ.nextval;
步骤2:修改NHibernate映射配置
Property(p => p.Bar, map => { map.Column("BAR"); // 指定该字段由数据库在插入时生成 map.Generated(PropertyGeneration.Insert); // 可选:禁止更新该字段,避免后续更新覆盖序列值 map.NotUpdate(true); });
插入时数据库会自动用序列值填充BAR列,NHibernate会在插入完成后将数据库生成的值同步回实体实例。
方案2:NHibernate插入时直接指定序列取值(兼容旧版Oracle)
如果无法修改表结构,可通过配置让NHibernate在插入语句中直接调用序列:
Property(p => p.Bar, map => { map.Column("BAR"); // 指定字段由数据库在插入时生成 map.Generated(PropertyGeneration.Insert); // 设置插入时的SQL表达式,直接调用序列 map.InsertSqlFormula("ABC.Other_SEQ.nextval"); });
此配置会让NHibernate生成的插入语句中,BAR字段取值为序列的nextval,插入后同步值到实体。
内容的提问来源于stack exchange,提问作者Crowcoder
相关产品推荐
相关产品推荐

