I've been spiking NHibernate for a large enterprise application. This means I'm a total newbie. We need to integrate a legacy database into our system.
I have a class DraftAuthorization wich has a collection of PermissionOnFunctionality object (composite id in the DB). Each PermissionOnFunctionality object also has a collection of PermissionOnSecurityToken objects (also a composite id in the DB). When I remove a PermissionOnFunctionality object, I want to also remove all of it's containing PermissionOnSecurityToken objects. I was hoping that this could be achieved by a one-shot-delete, but instead, NHibernate deletes each PermissionOnSecurityToken one by one.
Is a one-shot delete possible, and if so, what am I doing wrong? I'm using NHibernate 1.2.1.
Here is the mapping file:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="NHibernateSpike.Domain.Model.ActiveAuthorization, NHibernateSpike"
table="Authorization"
lazy="false">
<id name="Id" type="Int64" unsaved-value="-1" access="field.camelcase-underscore">
<column name="Id" sql-type="decimal" not-null="true" unique="true"/>
<generator class="native"/>
</id>
<many-to-one name="Draft"
class="NHibernateSpike.Domain.Model.DraftAuthorization, NHibernateSpike"
column="Draft"
access="field.camelcase-underscore"
cascade="all"/>
</class>
<class name="NHibernateSpike.Domain.Model.DraftAuthorization, NHibernateSpike"
table="Authorization"
lazy="false">
<id name="Id" type="Int64" unsaved-value="-1" access="field.camelcase-underscore">
<column name="Id" sql-type="decimal" not-null="true" unique="true"/>
<generator class="native"/>
</id>
<set name="PermissionsOnFunctionality" access="field.camelcase-underscore"
lazy="false" cascade="all-delete-orphan" inverse="true">
<key>
<column name="AuthorizationId"/>
</key>
<one-to-many class="NHibernateSpike.Domain.Model.PermissionOnFunctionality, NHibernateSpike"/>
</set>
</class>
<class name="NHibernateSpike.Domain.Model.PermissionOnFunctionality, NHibernateSpike"
table="PermissionOnFunctionality" lazy="false">
<composite-id name="PofKey" class="NHibernateSpike.Domain.Model.PofKey, NHibernateSpike" access="field.camelcase-underscore">
<key-property name="AuthorizationId" access="field.camelcase-underscore" column="AuthorizationId" type="Int64"/>
<key-property name="FunctionalityCode" access="field.camelcase-underscore" column="FunctionalityCode" type="String"/>
</composite-id>
<timestamp name="LastChange" column="LastChange"/>
<many-to-one name="Functionality" column="FunctionalityCode" class="NHibernateSpike.Domain.Model.Functionality, NHibernateSpike"
access="field.camelcase-underscore" lazy="false" cascade="none" insert="false" update="false"/>
<property name="AccessCode" column="AccessCode" not-null="true"/>
<property name="Creation" column="CreationDate" type="DateTime" not-null="true"/>
<property name="CreatedBy" column="CreationUser" type="String" not-null="true"/>
<property name="LastChangedBy" column="LastChangeUser" type="String" not-null="true"/>
<set name="PermissionsOnSecurityToken" access="field.camelcase-underscore"
lazy="false" cascade="all-delete-orphan" inverse="true" optimistic-lock="false">
<key>
<column name="AuthorizationId"/>
<column name="FunctionalityCode"/>
</key>
<one-to-many class="NHibernateSpike.Domain.Model.PermissionOnSecurityToken, NHibernateSpike"/>
</set>
</class>
<class name="NHibernateSpike.Domain.Model.PermissionOnSecurityToken, NHibernateSpike"
table="PermissionOnSecurityToken" lazy="false">
<composite-id name="PosKey" class="NHibernateSpike.Domain.Model.PosKey, NHibernateSpike" access="field.camelcase-underscore">
<key-property name="AuthorizationId" column="AuthorizationId" type="Int64" access="field.camelcase-underscore"/>
<key-property name="FunctionalityCode" column="FunctionalityCode" type="String" access="field.camelcase-underscore"/>
<key-property name="SecurityToken" column="SecurityToken" type="String" access="field.camelcase-underscore"/>
</composite-id>
<timestamp name="LastChange" column="LastChange"/>
<many-to-one name="Functionality" column="FunctionalityCode" class="NHibernateSpike.Domain.Model.Functionality, NHibernateSpike"
access="field.camelcase-underscore" lazy="false" cascade="none" insert="false" update="false"/>
<property name="AccessCode" column="AccessCode" not-null="true"/>
<property name="Creation" column="CreationDate" type="DateTime" not-null="true"/>
<property name="CreatedBy" column="CreationUser" type="String" not-null="true"/>
<property name="LastChangedBy" column="LastChangeUser" type="String" not-null="true"/>
</class>
<class name="NHibernateSpike.Domain.Model.Functionality, NHibernateSpike"
table="Functionality"
lazy="false">
<id name="Code" type="String" access="field.camelcase-underscore">
<column name="Code" sql-type="char" length="20" not-null="true" unique="true"/>
<generator class="assigned"/>
</id>
</class>
</hibernate-mapping>
And here is the code (please ignore the quality: it's part of a spike, so its throw-away code):
Code:
//
// Authorization
//
public abstract class Authorization
{
private readonly Int64 _id;
private ISet<PermissionOnFunctionality> _permissionsOnFunctionality;
protected void AddPermissionOnFunctionality(PermissionOnFunctionality permissionOnFunctionality)
{
_permissionsOnFunctionality.Add(permissionOnFunctionality);
}
protected void RemovePermissionOnFunctionality(PermissionOnFunctionality permissionOnFunctionality)
{
_permissionsOnFunctionality.Remove(permissionOnFunctionality);
}
public Int64 Id
{
get { return _id; }
}
public IEnumerable<PermissionOnFunctionality> PermissionsOnFunctionality
{
get
{
if(null == _permissionsOnFunctionality)
{
_permissionsOnFunctionality = new HashedSet<PermissionOnFunctionality>();
}
return _permissionsOnFunctionality;
}
}
}
//
// ActiveAuthorization
//
public class ActiveAuthorization : Authorization
{
private DraftAuthorization _draft;
public DraftAuthorization Draft
{
get { return _draft; }
}
}
//
// DraftAuthorization
//
public class DraftAuthorization : Authorization
{
private DateTime _expirationDate;
private PermissionOnFunctionality FindPermissionOnFunctionality(Functionality functionality)
{
return Algorithms.FindFirstWhere(PermissionsOnFunctionality,
delegate(PermissionOnFunctionality permissionOnFunctionality)
{
return permissionOnFunctionality.Functionality.Code ==
functionality.Code;
});
}
private PermissionOnSecurityToken FindPermissionOnSecurityToken(PermissionOnFunctionality permissionOnFunctionality,
String securityToken)
{
return Algorithms.FindFirstWhere(permissionOnFunctionality.PermissionsOnSecurityToken,
delegate(PermissionOnSecurityToken permissionOnSecurityToken)
{
return permissionOnSecurityToken.SecurityToken == securityToken;
});
}
public DraftAuthorization()
{
_expirationDate = DateTime.Today.AddDays(1);
}
public DateTime ExpirationDate
{
get { return _expirationDate; }
set { _expirationDate = value; }
}
public void GrantPermissionTo(Functionality functionality, Int32 accessCode)
{
PermissionOnFunctionality permissionOnFunctionality = FindPermissionOnFunctionality(functionality);
if(null == permissionOnFunctionality)
{
PermissionOnFunctionality newPermissionOnFunctionality = new PermissionOnFunctionality(this, functionality, accessCode);
newPermissionOnFunctionality.ValidFrom = DateTime.Now;
newPermissionOnFunctionality.ValidTo = DateTime.Now.AddDays(6);
newPermissionOnFunctionality.Creation = DateTime.Now;
newPermissionOnFunctionality.CreatedBy = "SOMEUSER";
newPermissionOnFunctionality.LastChangedBy = "SOMEUSER";
AddPermissionOnFunctionality(newPermissionOnFunctionality);
}
else
{
permissionOnFunctionality.AccessCode = accessCode;
}
}
public void GrantPermissionTo(String securityToken, Functionality functionality, Int32 accessCode)
{
PermissionOnFunctionality permissionOnFunctionality = FindPermissionOnFunctionality(functionality);
if(null != permissionOnFunctionality)
{
PermissionOnSecurityToken permissionOnSecurityToken = FindPermissionOnSecurityToken(permissionOnFunctionality, securityToken);
if(null == permissionOnSecurityToken)
{
PermissionOnSecurityToken newPermissionOnSecurityToken = new PermissionOnSecurityToken(this, functionality, securityToken, accessCode);
newPermissionOnSecurityToken.Creation = DateTime.Now;
newPermissionOnSecurityToken.CreatedBy = "SOMEUSER";
newPermissionOnSecurityToken.LastChangedBy = "SOMEUSER";
permissionOnFunctionality.GrantPermissionOnSecurityToken(newPermissionOnSecurityToken);
}
else
{
permissionOnSecurityToken.AccessCode = accessCode;
}
}
}
public void RevokePermissionTo(Functionality functionality)
{
PermissionOnFunctionality permissionOnFunctionality = FindPermissionOnFunctionality(functionality);
if(null != permissionOnFunctionality)
{
permissionOnFunctionality.RevokeAllPermissionsOnSecurityToken();
RemovePermissionOnFunctionality(permissionOnFunctionality);
}
}
public void RevokePermissionTo(String securityToken, Functionality functionality)
{
PermissionOnFunctionality permissionOnFunctionality = FindPermissionOnFunctionality(functionality);
if(null != permissionOnFunctionality)
{
PermissionOnSecurityToken permissionOnSecurityToken = FindPermissionOnSecurityToken(permissionOnFunctionality, securityToken);
if(null != permissionOnSecurityToken)
{
permissionOnFunctionality.RevokePermissionOnSecurityToken(permissionOnSecurityToken);
}
}
}
}
public interface IAuditable
{
DateTime Creation
{ get; set; }
String CreatedBy
{ get; set; }
DateTime LastChange
{ get; set; }
String LastChangedBy
{ get; set; }
}
public abstract class Permission : IAuditable
{
private Int32 _accessCode;
private readonly Functionality _functionality;
private DateTime _lastChange = DateTime.MinValue;
private DateTime _creation;
private String _createdBy;
private String _lastChangedBy;
protected internal Permission()
{}
protected Permission(Functionality functionality, Int32 accessCode)
{
_functionality = functionality;
_accessCode = accessCode;
}
public Int32 AccessCode
{
get { return _accessCode; }
set { _accessCode = value; }
}
public Functionality Functionality
{
get { return _functionality; }
}
#region IAuditable Members
public DateTime Creation
{
get { return _creation; }
set { _creation = value; }
}
public String CreatedBy
{
get { return _createdBy; }
set { _createdBy = value; }
}
public DateTime LastChange
{
get { return _lastChange; }
set { _lastChange = value; }
}
public String LastChangedBy
{
get { return _lastChangedBy; }
set { _lastChangedBy = value; }
}
#endregion
}
public class PofKey
{
private readonly Int64 _authorizationId;
private readonly String _functionalityCode;
protected internal PofKey()
{}
public PofKey(Int64 authorizationId, String functionalityCode)
{
_authorizationId = authorizationId;
_functionalityCode = functionalityCode;
}
public Int64 AuthorizationId
{
get { return _authorizationId; }
}
public String FunctionalityCode
{
get { return _functionalityCode; }
}
public override Boolean Equals(Object obj)
{
// Check for null reference or different type
PofKey other = obj as PofKey;
if(null == other || GetType() != other.GetType())
{
return false;
}
// Check for same object reference
if(this == other)
{
return true;
}
return (_functionalityCode == other.FunctionalityCode) &&
(_authorizationId == other.AuthorizationId);
}
public override Int32 GetHashCode()
{
return FunctionalityCode.GetHashCode() ^ AuthorizationId.GetHashCode();
}
}
public class PermissionOnFunctionality : Permission
{
private readonly PofKey _pofKey;
private DateTime _validFrom;
private DateTime _validTo;
private ISet<PermissionOnSecurityToken> _permissionsOnSecurityToken;
internal void GrantPermissionOnSecurityToken(PermissionOnSecurityToken permissionOnSecurityToken)
{
_permissionsOnSecurityToken.Add(permissionOnSecurityToken);
}
internal void RevokePermissionOnSecurityToken(PermissionOnSecurityToken permissionOnSecurityToken)
{
_permissionsOnSecurityToken.Remove(permissionOnSecurityToken);
}
internal PermissionOnFunctionality()
{}
public PermissionOnFunctionality(Authorization authorization, Functionality functionality, Int32 accessCode)
: base(functionality, accessCode)
{
_pofKey = new PofKey(authorization.Id, functionality.Code);
}
public IEnumerable<PermissionOnSecurityToken> PermissionsOnSecurityToken
{
get
{
if(null == _permissionsOnSecurityToken)
{
_permissionsOnSecurityToken = new HashedSet<PermissionOnSecurityToken>();
}
return _permissionsOnSecurityToken;
}
}
public DateTime ValidFrom
{
get { return _validFrom.Date; }
set { _validFrom = value.Date; }
}
public DateTime ValidTo
{
get { return _validTo.Date; }
set { _validTo = value.Date; }
}
public override Boolean Equals(Object obj)
{
// Check for null reference or different type
PermissionOnFunctionality other = obj as PermissionOnFunctionality;
if(null == other || GetType() != other.GetType())
{
return false;
}
// Check for same object reference
if(this == other)
{
return true;
}
return _pofKey.Equals(other._pofKey);
}
public override Int32 GetHashCode()
{
return _pofKey.GetHashCode();
}
public void RevokeAllPermissionsOnSecurityToken()
{
ISet<PermissionOnSecurityToken> temp = new HashedSet<PermissionOnSecurityToken>(_permissionsOnSecurityToken);
_permissionsOnSecurityToken.RemoveAll(temp);
}
}
public class PosKey : PofKey
{
private readonly String _securityToken;
protected internal PosKey()
{}
public PosKey(Int64 authorizationId, String functionalityCode, String securityToken)
: base(authorizationId, functionalityCode)
{
_securityToken = securityToken;
}
public String SecurityToken
{
get { return _securityToken; }
}
public override Boolean Equals(Object obj)
{
// Check for null reference or different type
PosKey other = obj as PosKey;
if(null == other || GetType() != other.GetType())
{
return false;
}
// Check for same object reference
if(this == other)
{
return true;
}
return base.Equals(other) &&
(_securityToken == other.SecurityToken);
}
public override Int32 GetHashCode()
{
return base.GetHashCode() ^ SecurityToken.GetHashCode();
}
}
public class PermissionOnSecurityToken : Permission
{
private readonly PosKey _posKey;
internal PermissionOnSecurityToken()
{ }
public PermissionOnSecurityToken(Authorization authorization, Functionality functionality, String securityToken, Int32 accessCode)
: base(functionality, accessCode)
{
_posKey = new PosKey(authorization.Id, functionality.Code, securityToken);
}
public String SecurityToken
{
get { return _posKey.SecurityToken; }
}
public override Boolean Equals(Object obj)
{
// Check for null reference or different type
PermissionOnSecurityToken other = obj as PermissionOnSecurityToken;
if(null == other || GetType() != other.GetType())
{
return false;
}
// Check for same object reference
if(this == other)
{
return true;
}
return _posKey.Equals(other._posKey);
}
public override Int32 GetHashCode()
{
return _posKey.GetHashCode();
}
}
//
// AuthorizationService
//
public class AuthorizationService
{
private readonly IAuthorizationRepository _authorizationRepository;
private readonly IFunctionalityRepository _functionalityRepository;
public AuthorizationService(IAuthorizationRepository authorizationRepository,
IFunctionalityRepository functionalityRepository)
{
_authorizationRepository = authorizationRepository;
_functionalityRepository = functionalityRepository;
}
public void SaveAuthorization()
{
ActiveAuthorization activeAuthorization = _authorizationRepository.GetActiveAuthorizationFor(1);
Functionality functionality1 = _functionalityRepository.GetFunctionalityFor("F1000");
//activeAuthorization.Draft.GrantPermissionTo(functionality1, 1);
//activeAuthorization.Draft.RevokePermissionTo(functionality1);
//activeAuthorization.Draft.GrantPermissionTo("SEC_Token1", functionality1, 2);
//activeAuthorization.Draft.GrantPermissionTo("SEC_Token2", functionality1, 4);
Functionality functionality3 = _functionalityRepository.GetFunctionalityFor("F1001");
//activeAuthorization.Draft.GrantPermissionTo(functionality3, 3);
activeAuthorization.Draft.RevokePermissionTo(functionality3);
//activeAuthorization.Draft.GrantPermissionTo("Token2", functionality3, 4);
//activeAuthorization.Draft.GrantPermissionTo("Token3", functionality3, 2);
//activeAuthorization.Draft.GrantPermissionTo("Token1", functionality3, 3);
//activeAuthorization.Draft.RevokePermissionTo("Token1", functionality3);
//activeAuthorization.Draft.RevokePermissionTo("Token2", functionality3);
_authorizationRepository.Save(activeAuthorization);
}
}
//
// AuthorizationRepository
//
public class AuthorizationRepository : IAuthorizationRepository
{
private readonly ISessionManager _sessionManager;
public AuthorizationRepository(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
_sessionManager.DefaultFlushMode = FlushMode.Never;
}
public ActiveAuthorization GetActiveAuthorizationFor(Int64 id)
{
ISession session = _sessionManager.OpenSession();
return session.Get<ActiveAuthorization>(id);
}
public void Save(ActiveAuthorization activeAuthorization)
{
ISession session = _sessionManager.OpenSession();
session.SaveOrUpdate(activeAuthorization);
session.Flush();
}
}
I'm using a SqLite database (for testing purposes).
[code]CREATE TABLE [Authorization] (
[Id] INTEGER NULL,
[ExpirationDate] DATE NULL,
[Draft] INTEGER NULL
);
CREATE TABLE [Functionality] (
[code] VARCHAR(30) NOT NULL PRIMARY KEY
);
CREATE TABLE [PermissionOnFunctionality] (
[AuthorizationId] INTEGER NOT NULL,
[FunctionalityCode] VARCHAR(30) NOT NULL,
[AccessCode] INTEGER NULL,
[LastChange] TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
[LastChangeUser] VARCHAR(30) NOT NULL,
[CreationDate] TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
[CreationUser] VARCHAR(30) NOT NULL,
PRIMARY KEY ([AuthorizationId],[FunctionalityCode])
)
CREATE TABLE [PermissionOnSecurityToken] (
[AuthorizationId] INTEGER NOT NULL,
[FunctionalityCode] VARCHAR(30) NOT NULL,
[SecurityToken] VARCHAR(30) NOT NULL,
[AccessCode] INTEGER NOT NULL,
[LastChange] TIMESTAMP NOT NULL,
[LastChangeUser] VARCHAR(30) NOT NULL,
[CreationDate] TIMESTAMP NOT NULL,
[CreationUser] VARCHAR(30) NOT NULL,
PRIMARY KEY ([AuthorizationId],[FunctionalityCode],[SecurityToken])
)[/code]
This is a piece of the SQL that gets generated:
[code]...
NHibernate: DELETE FROM PermissionOnSecurityToken WHERE AuthorizationId = @p0 AN
D FunctionalityCode = @p1 AND SecurityToken = @p2 AND LastChange = @p3; @p0 = '2
', @p1 = 'F1001', @p2 = 'Token3', @p3 = '23/03/2008 1:36:56'
NHibernate: DELETE FROM PermissionOnSecurityToken WHERE AuthorizationId = @p0 AN
D FunctionalityCode = @p1 AND SecurityToken = @p2 AND LastChange = @p3; @p0 = '2
', @p1 = 'F1001', @p2 = 'Token1', @p3 = '23/03/2008 1:36:56'
NHibernate: DELETE FROM PermissionOnSecurityToken WHERE AuthorizationId = @p0 AN
D FunctionalityCode = @p1 AND SecurityToken = @p2 AND LastChange = @p3; @p0 = '2
', @p1 = 'F1001', @p2 = 'Token2', @p3 = '23/03/2008 1:36:56'
NHibernate: DELETE FROM PermissionOnFunctionality WHERE AuthorizationId = @p0 AN
D FunctionalityCode = @p1 AND LastChange = @p2; @p0 = '2', @p1 = 'F1001', @p2 =
'23/03/2008 1:36:56'[/code]
Notice that there are three deletes on PermissionOnSecurityToken before the delete on PermissionOnFunctionality. I guess this could be done with a single delete statement for PermissionOnSecurityToken. Any suggestions?
Thanks in advance,
Jan