I have a situation where I'm using a table per subclass for association. The mappings are fine until I try and delete a record from the subclass table. The delete cause's not only the subclass to be deleted, but also the parent. I can understand this feature may be by design, but is there anyway to just delete the subclass?
Here's my sample code.
public class ParentClassMap : ClassMap<Parent>
{
public ParentClassMap ()
{
Table("tblParent");
Id(x => x.ParentId).Column("ParentId").GeneratedBy.Identity()
... other properties
}
}
public class ChildClassMap : SubClassMap<Child>
{
public ChildClassMap()
{
Table("tblChild");
KeyColumn("ParentId");
... other properties
}
}
Now when I query for a record, everything seems fine
Child child = session.CreateCriteria<Parent>().Add(Restrictions.Eq("ParentId", 1)).UniqueResult<Parent>();
But when I delete the child, the sql executed includes updates to all tables that reference either Parent or Child, then the deletion of Child then Parent.
session.Delete(child);
I only want to delete the Child Object, is this possible?
No, it's not possible.
Think about it in OOP terms: if you have an object of the class Dog that inherits from Animal, does it makes sense to "delete the dog, but leave the animal"?
Which brings me to the next point: if you can delete "part" of an object, then you should not be using a subclass, but an association (probably one-to-one or many-to-one)
Related
I´m using Fluent Nhibernate with automapping and having problem setting up a bi-directional HasMany relationship because of my current inheritance.
I simplified version of my code looks like this
public abstract class BaseClass
{
public BaseClass Parent { get; set; }
}
public class ClassA : BaseClass
{
public IList<ClassB> BChilds { get; protected set; }
public IList<ClassC> CChilds { get; protected set; }
}
public class ClassB : BaseClass
{
public IList<ClassD> DChilds { get; protected set; }
}
public class ClassC : BaseClass
{
}
public class ClassD : BaseClass
{
}
Every class can have one parent and some parents can have childs of two types. I´m using table-per-type inheritance which result in the tables
"BaseClass"
"ClassA"
"ClassB"
"ClassC"
"ClassD"
To get a working bi-directional mapping I have made the following overrides
(one example from ClassA)
mapping.HasMany<BaseType>(x => x.BChilds).KeyColumn("Parent_Id");
mapping.HasMany<BaseType>(x => x.CChilds).KeyColumn("Parent_Id");
This works fine on classes with only one type of children, but ClassA with two child types will get all subtypes of BaseType in each list which ofcourse will end up in an exception. I have looked at two different workarounds tho none of them feels really sufficient and I really believe there is a better way to solve it.
Workaround 1: Point to the concrete subtype in the HasMany mapping. (Updated with more info)
mapping.HasMany<ClassB>(x => x.BChilds).KeyColumns("Parent_Id");
(BaseType replaced with ClassB)
With this mapping NHibernate will in some cases look in the ClassB table for a column named Parent_Id, obviously there is no such column as it belongs to the BaseClass table. The problem only occurs if you add a statement based on BChilds during a ClassA select. e.g loading an entity of ClassA then calling ClassA.BChilds seems to work, but doing a query (using NhibernateLinq) something like
Query<ClassA>().Where(c => c.BChilds.Count == 0)
the wrong table will be used. Therefore I have to manually create a new column in this table with the same name and copy all the values. It works but it´s risky and not flexible at all.
Workaround 2: Add a column to the BaseClass that tells the concrete type and add a where statement to the HasMany mapping.
(after my update to workaround1 I´m no longer sure if this could be a workable solution)
By adding a column they same way as it´s done when using table-per-hierarchy inheritance with a discriminatorValue. i.e BaseType table will get a new column with a value of ClassA, ClassB... Tho given how well NHibernate handles the inheritance overall and by reading the NHibernate manual I believe that the discriminator shouldn´t be needed in a table-per-type scenario, seems like Nhibernate already doing the hardpart and should be able to take care of this in a clean way to without adding a new column, just can´t figure out how.
What's your base class mapping and what does your subclass map look like?
You should be able to do
mapping.HasMany(x => x.BChilds);
And with the correct mapping, you shouldn't have a problem.
If it's fluent nhibernate, look into
UseUnionSubclassForInheritanceMapping();
I've searched so many places and still can't find the a answer I'm looking for.
I'm using NHibernate 3.2 - Mapping by Code
I Have the following Mapping files:
public class ParentMapping: EntityMapping<Parent>
{
public ParentMapping()
{
Set(x => x.Children, map =>
{
map.Cascade(Cascade.All);
map.Inverse(true);
}, r => r.OneToMany());
}
}
public class ChildMapping: JoinedSubclassMapping<Child> // This is a subclass of something else.
{
public RequiredSkillMapping()
{
ManyToOne(x => x.Parent, map => { map.NotNullable(true); });
}
}
The cascade save works fine.
session.Save(parent) will save the children and associate them correctly.
When I try to call:
var parent = session.Get<Parent>(1);
parent.Children.Clear();
session.Save(parent); or session.SaveOrUpdate(parent) or session.Update(parent)
The entities stay associated to the parent.
I had it working by calling:
foreach(var child in parent.Children)
{
session.Delete(child);
}
parent.Children.Clear();
I was hoping that there would be a way to just save the parent?
Cheers,
James
Cascade means that operations on the parent are cascaded to the child.
When inserting the parent, the children are inserted too.
When deleting the parent, the children are deleted too.
Update is special in NH, but it also means when calling update with the parent, that the children are "updated" too.
and so on
The collection itself belongs to the parent, so changes on the collection are stored with the parent. But there is no reason to delete the children when they are not in the collection anymore. NH can't know if you need them for anything else.
There is cascade-delete-orphans.
map.Cascade(Cascade.All | Cascade.DeleteOrphans)
It means that items that had been in the collection, and are removed from the collection get deleted. It may be useful in your case. Note that it is not possible to use items for anything after removing from the collection. You can't even add them to another parent.
To make NH remove unused items automatically correctly in every case would require persistent garbage collection. This is highly inefficient and is not implemented by NH.
"foreach child delete" is OK. That's something you may have to do to tell NH when the children are not used anymore and need deletion.
I am unable to get Eclipselink to delete a privately owned entity that is mapped as a one-to-many relationship. When I remove the privately owned entity from the one-to-many collection and set the reference to its parent to NULL and then save the parent, the privately owned entity is not removed from the database. However, it is removed from the object and during the remainder of the session, it appears that the privately owned entity was in fact removed.
Here's one mapping
#OneToMany(mappedBy="parent", cascade={CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, fetch=FetchType.EAGER)
#PrivateOwned // should prevent orhpans, but doesn't.
List<PrivatelyOwnedEntity> privatelyOwnedEntities;
I have tried adding cascade all, and also added #CascadeOnDelete, and every combination thereof, which one would think would not have an impact, and it didn't.
otherside:
#ManyToOne
private Parent parent;
I have tried adding JoinColumn(nullable=false) and also removing the #ManyToOne annotation, and every combination of the presence/removal of these annotaitons. None of the changes had any impact.
Removal code:
public boolean removePrivatelyOwnedEntity(int i) {
PrivatelyOwnedEntity privatelyOwnedEntityToBeRemoved = this.privatelyOwnedEntities.get(i);
//privatelyOwnedEntityToBeRemoved.setParent(null);
return this.privatelyOwnedEntities.remove(privatelyOwnedEntityToBeRemoved );
}
Code that updates:
projectManager.saveProject(project);
//so this privatelyOwnedEntity is deep within this object.
// Its project.site.siteMap.parents.privatelyOwnedEntities.
// SiteMap is embedded in Site.
// On save it calls a dao, and the dao's abstract class handles the update
// as shown below using Springs JpaTemplate
Save code:
public T save(T object) {
return getJpaTemplate().merge(object);
}
Is the parent part of the object's id?
Try not setting the parent to null, you should not do this for private owned objects, just remove them from the collection.
If I have two entities, Parent and Child, Parent needs to know about all of its Child children, and every Child instance needs to know about its parent Parent instance, how do I do this properly (in terms of DDD etc)?
The easy way would be to do parent.addChild(new Child(parent)), but this seems ugly - as well as:
parent.addChild(new Child()); // Then call some setParent method on child, which needs to be public
Do I need to use a factory here? And if so, how?
Thanks
One option would be to have a relevant instance method on the Parent class. A static Factory is probably not necessary here since you are operating on existing objects and simply need to connect object with another.
public class Parent {
// ...
public Child createChild() {
Child c = new Child(this);
this.addChild(c);
return c;
}
protected void addChild(c) {
// ...
}
// ...
}
public class Child {
public Child(Parent p) {
// ...
this.addParent(p);
}
protected addParent(Parent p) {
// ...
}
}
If the Child constructor needs arguments, you can pass those to the createChild method.
You are not limited to a single one approach. You should use the approach that fits you.
In SWT the child is linked to parent in constructor:
new Label(parentComposite, SWT.NONE);
After that parentComposite knows its child.
Note: SWT requires parent on creation which limits some functionality - you can't create child without specifying a child. This is limitation of the SWT.
In Swing you can create child widget and then add it to the parent.
Those above just the examples. Your personal solution will be based on your needs.
I would consider to use less methods, more consistency (don't leave your childs unlinked from parent)
Talking about code I would use following method:
Parent {
addChild(Child child) {
children.add(child);
child.setParent(this);
}
}
Hope that helps. Happy designing!
How about to do something like this:
public class Child {
Child (Parent parent) {
...
this.parent = parent;
parent.addChild(this);
}
}
So you can set parent to Child only while creating child.
I'm working on a project with NHibernate that classes similar to the following:
public class Parent {
public IList Children {get;set;}
// ...
}
public class Child {
// ...
}
I've got the Children property set to cascade all / delete orphan. Since I'm using the aggregate pattern and instances of Child class will only ever be referenced in the context of a Parent, I don't have a ChildRepository to delete the children directly - only a ParentRepository. However, when I retrieve the Parent object and call Parent.Children.Clear(), the children from the database are never deleted. How can I achieve this?
Deleting child entities is this easy - just remove them from the collection and then save the parent entity. The collection should be mapped with all-delete-orphans.
parent.Children.Clear();
session.Save( parent );
// or
parent.Children.RemoveAt(0);
session.Save( parent );
You can do this without Save() calls as well, unless your FlushMode is Never.
session.BeginTransaction();
parent.Children.Clear();
session.Transaction.Commit();
Using #Chris's UnitOfWork abstractionm this could look like:
using (var uow = new UnitOfWork()) {
parent.Children.Clear();
}