我在Nhibernate和一个非常简单的父子关系中遇到以下问题。
我有三个听众:保存,更新,删除。如果持久化的对象实现IAuditCreate接口,我将分配CreatedDate字段。
我的父映射
<hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2">
<class name="SecSession" table="SEC_SESSION" lazy="true" >
<id name="SecSessionId">
<column name="SEC_SESSION_ID" sql-type="bigint" not-null="true" />
<generator class="identity" />
</id>
<property name="CreatedDate">
<column name="CREATED_DATE" sql-type="datetime" not-null="true" />
</property>
<bag name="SecSessionLogs" inverse="true" cascade="all-delete-orphan" >
<key column="SEC_SESSION_ID" />
<one-to-many class="SecSessionLog" />
</bag>
</class>
</hibernate-mapping>
我的孩子映射
<hibernate-mapping assembly="Model" namespace="Model" xmlns="urn:nhibernate-mapping-2.2">
<class name="SecSessionLog" table="SEC_SESSION_LOG" lazy="true" >
<id name="SecSessionLogId">
<column name="SEC_SESSION_LOG_ID" sql-type="bigint" not-null="true" />
<generator class="identity" />
</id>
<many-to-one lazy="false" name="SecSession">
<column name="SEC_SESSION_ID" sql-type="bigint" not-null="true" />
</many-to-one>
<property name="LogMessage" type="StringClob">
<column name="LOG_MESSAGE" sql-type="nvarchar(max)" not-null="true" />
</property>
<property name="CreatedDate">
<column name="CREATED_DATE" sql-type="datetime" not-null="true" />
</property>
</class>
</hibernate-mapping>
所以我创建了一个SecSession对象,然后调用SecSession.AddLog(new SecSessionLog)。
坚持,我做:
using (var dataSession = DataStore.OpenDataSession())
using (var transaction = dataSession.BeginTransaction())
{
var id = (PK)dataSession.Save(secSession);
transaction.Commit();
return id;
}
我没有显式发送SecSessionLog来保存,因为映射说:CASCADE = ALL-DELETE-ORPHAN。
所以这里的问题是没有为子Log对象调用监听器,因此CreatedDate字段为空,我在数据库中得到一个空值异常。
映射中是否缺少配置?在听众中?
非常感谢您的帮助!
全部谢谢
答案 0 :(得分:2)
我也使用级联到应用程序中。在我的情况下,我需要拦截命令“insert”,“delete”和“update”,以确保在执行的操作范围内只保留特定的实体子集。
就我而言,以下活动有效:IPreInsertEventListener
,IPreUpdateEventListener
,IPreDeleteEventListener
。
我之前的回答是错的!
正确答案:
您不能使用'IPreInsertEventListener',那时,更改实体的属性无效。它就是这样发生的,因为NHibernate已经捕获了用于运行SQL命令的数据。我测试了它。
我希望尽快得到答案。
但我认为你的问题应该涉及以下听众:“merge”,“save-update”,“save”,“update”,“pre-collection-recreate”,“pre-collection-remove”和“pre” -collection更新”。
我之前的回答是错误的!
我已经测试并发现使用上面列出的事件*需要大量编码,我无法使其在“SecSession”中使用“merge”,然后插入和更新级联到“SecSessionLogs”。
有一种方法可以在侦听器中修改后切换回NHibernate属性值。
*“合并”,“保存更新”,“保存”,“更新”,“预收集 - 重新创建”,“预收集 - 删除”和“预收集更新”
此代码显示:
public class AuditCreateListener : IPreInsertEventListener, IPreUpdateEventListener
{
public bool OnPreInsert(PreInsertEvent @event)
{
return this.OnEventCommon(
@event,
new Action<object[]>(
delegate(Object[] newState)
{
newState.CopyTo(@event.State, 0);
}));
}
public bool OnPreUpdate(PreUpdateEvent @event)
{
return this.OnEventCommon(
@event,
new Action<object[]>(
delegate(Object[] newState)
{
newState.CopyTo(@event.State, 0);
}));
}
private bool OnEventCommon(AbstractPreDatabaseOperationEvent @event, Action<object[]> setStateCallback)
{
IAuditCreate auditCreate = @event.Entity as IAuditCreate;
if (auditCreate != null && auditCreate.CreatedDate == null)
{
auditCreate.CreatedDate = DateTime.Now;
if (setStateCallback != null)
{
Object[] newState = @event.Persister.GetPropertyValues(auditCreate, EntityMode.Poco);
setStateCallback(newState);
}
}
return false;
}
}
很快我会发布完整的消息来源。
再次编辑:
概念证明:
AuditCreateListenerBrokenTet
:测试事件:“save”,“save-update”,“update”,“merge”,“flush-entity”,“pre-collection-recreate”,“pre-collection -remove“和”pre-collection-update“。AuditCreateListenerTet
:对事件进行测试:“预更新”和“预插入”。表格内容:
SEC_SESSION:
#|SEC_SESSION_ID|CREATED_DATE
-+--------------+----------------
1|1 |2012-07-10 16:53
2|2 |2012-07-11 16:53
3|3 |2012-07-12 16:53
4|4 |2012-07-13 16:53
SEC_SESSION_LOG:
#|SEC_SESSION_LOG_ID|LOG_MESSAGE |CREATED_DATE |SEC_SESSION_ID
-+------------------+-------------+----------------+--------------
1|1 |LOG_MESSAGE_1|2012-07-10 16:53|1
2|2 |LOG_MESSAGE_2|2012-07-11 16:53|2
3|3 |LOG_MESSAGE_3|2012-07-12 16:53|3
4|4 |LOG_MESSAGE_4|2012-07-13 16:53|4
我正在使用:
完整的来源是:Q11495204.7z
注意: