CUBA : entity inheritance - cuba-platform

Provided sample 'Entity Inheritance' has the following entity model:
- Customer
- Company extends Customer
- Person extends Customer
- Order
The OrderEdit screen show how to handle the inheritance for fields associated with a Customer that could be a Company or a Person. This is perfectly clear.
However, edit screens for Company and Person do not take inheritance into account : they simply duplicate 'email' field which is commonly inherited from Customer.
Given all inputs I had at this point, if I had to design these screens I would propose the following way.
1) CustomerEditFrame : with the email field, no datasource defined
2) PersonEditScreen:
- Person datasource
- map lastName and firstName fields on Person datasource
- embed CustomerEditFrame
- inject Person datasource in the CustomerEditFrame
3) CompanyEditScreen:
- Company datasource
- map industry field to Company datasource
- embed CustomerEditFrame
- inject Company datasource in the CustomerEditFrame
Then the CustomerEditFrame is responsible for editing the subset of fields it is aware of in a datasource referring either of the two subclasses. Would this design work ?
For the sake of completeness of documentation I think this should be covered by the sample, as it is common case. In addition, it would be a good sample for frame manipulation.

You are absolutely right that screens should take entity inheritance into account to eliminate duplication of code. I've forked the sample project here to demonstrate how it can be done using frames.
customer-frame.xml contains fields of the base entity and a datasource for it:
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
caption="msg://editCaption"
class="com.company.entityinheritance.gui.customer.CustomerFrame"
focusComponent="fieldGroup"
messagesPack="com.company.entityinheritance.gui.customer">
<dsContext>
<datasource id="customerDs"
class="com.company.entityinheritance.entity.Customer"
view="_local"/>
</dsContext>
<layout spacing="true">
<fieldGroup id="fieldGroup"
datasource="customerDs">
<column width="250px">
<field id="name"/>
<field id="email"/>
</column>
</fieldGroup>
</layout>
</window>
In the CustomerFrame controller there is a public method to set an instance to the datasource:
public class CustomerFrame extends AbstractFrame {
#Inject
private Datasource<Customer> customerDs;
public void setCustomer(Customer customer) {
customerDs.setItem(customer);
}
}
The Company editor company-edit.xml includes the frame instead of Customer fields:
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
caption="msg://editCaption"
class="com.company.entityinheritance.gui.company.CompanyEdit"
datasource="companyDs"
focusComponent="customerFrame"
messagesPack="com.company.entityinheritance.gui.company">
<dsContext>
<datasource id="companyDs"
class="com.company.entityinheritance.entity.Company"
view="_local"/>
</dsContext>
<layout expand="windowActions"
spacing="true">
<frame id="customerFrame"
screen="demo$Customer.frame"/>
<fieldGroup id="fieldGroup"
datasource="companyDs">
<column width="250px">
<field id="industry"/>
</column>
</fieldGroup>
<frame id="windowActions"
screen="editWindowActions"/>
</layout>
</window>
In the Company editor controller, the frame is injected and an edited instance is passed to it:
public class CompanyEdit extends AbstractEditor<Company> {
#Inject
private CustomerFrame customerFrame;
#Override
protected void postInit() {
customerFrame.setCustomer(getItem());
}
}

Related

Ensure a one to one (distinct) relation when using one to many (subclassed)

My scenario is that I have a User class, and that class has to be extended with related data, but without being subclassed.
For example the user might have lots of different profile data: AddressProfileData, FavoritesProfileData, etc etc.
I have decided to go with an abstract class and many implementations, kind of like in this post: inheritance mapping
However, I can't find a way to ensure (using nhibernate and not programmatically) that each item, for example AddressProfileData occurs only once per user.
Is this possible? If not, is there another solution for this problem which is more proper? I feel that sharing a common abstract class is building my app around NHibernate, and not the other way round.
AddressProfileData and FavoritesProfileData are likely to share almost nothing common, except for the fact that they both are extra information you attach to a User, so I don't think it makes sense to make them part of some inheritance hierarchy. Instead, I would go with something like this:
public class User
{
// ... other properties ...
public virtual AddressProfileData Address { get; set; }
public virtual FavoritesProfileData Favorites { get; set; }
}
public class AddressProfileData
{
// ... other properties ...
public virtual User User { get; set; }
}
<class name="User">
<!-- ... other properties ... -->
<one-to-one name="Address" property-ref="User" />
<one-to-one name="Favorites" property-ref="User" />
</class>
<class name="AddressProfileData">
<!-- ... other properties ... -->
<many-to-one name="User" column="User_id" unique="true" not-null="true" />
</class>
create table AddressProfileData (
/* ... other columns ... */
User_id int not null,
unique (User_id),
foreign key (User_id) references User (Id)
);
I'm sure you can imagine what FavoritesProfileData looks like.
With this setup, you ensure that each type of profile data only occurs once per user, and you also don't wind up in a weird place where you have to test which type of ProfileData you're dealing with before you can do anything with it. You always know exactly what kind of profile data you're touching.

Fluent NHibernate HasMany relation with different subtypes of same superclass

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();

One-to-many relationship not saved

I've got this relationship between a ReportRow (parent) and a Mark (child)
<class name="ReportRow">
<bag name="Marks" cascade="save-update" inverse="true">
<key column="ReportRowId"/>
<one-to-many class="Mark"/>
</bag>
</class>
// C# code
public virtual IList<Mark> Marks { get; set; }
But it's not being saved (in the Mark table, ReportRowId is always null).
I know these relationships always have to be bidirectional because of the NHibernate 'quirk' so for my Mark class I have:
<many-to-one name="ReportRow" class="ReportRow" column="ReportRowId" />
// C#
public virtual ReportRow ReportRow { get; set; }
I've even got some other examples of this kind of relationship working elsewhere in my project, but this one isn't working and I can't see any difference apart from...
... both Mark and ReportRow both have subclasses (e.g. ModuleMark and ModuleReportRow), which I'm using the joined-subclass strategy to implement the inheritance.
Would that have something to do with it? For both ends of the relationship, the mappings are defined in the parent class mapping rather than nested inside the <joined-subclass> mappings.
Thanks
How are you adding Marks to the collection? Because the collection is the inverse side of the relationship, you need to set the reference to the parent object on the child when adding it to the collection. A common approach is to use methods in the parent object to maintain the relationship, e.g.:
public void AddMark(Mark mark)
{
mark.ReportRow = this;
Marks.Add(mark); // better yet map the collection as a private field
}
I think of "inverse" as "who wears the pants in this relationship".

NHibernate Collection Mapping - Read Only Properties

I have the following class
public class Person
{
private IList<Person> _children;
public IEnumerable<Person> Children { get; }
public void AddChild(Person child)
{
// Some business logic and adding to the internal list
}
}
What changes would I have to make for NHibenrate to be able to persist the Child collection (apart from making everything virtual, I know that one).
Do I have to add a setter to the children property which does something like a _children.Clear(); _children.AddRange(value). Currently the model expresses my intent quite nicely but I'm not sure how much alteration is need for NH to be able to help me out with persistence.
NHibernate is able to map private fields. Access and naming strategies are discussed in the property section of the reference documentation.
Making your public members virtual is required for proxies to work. These will usually be runtime-generated subclasses of your entity classes.
In this example mapping the field _children will be Children in HQL and Criteria queries.
<class name="Person" table="person">
<bag name="Children" access="field.camelcase-underscore">
<key column="parentid" />
<one-to-many class="Person" />
</bag>
</class>

How to Map Enum in NHibernate to Properly Create DB Field on Schema Export?

I've seen several questions related to properly mapping an enum type using NHibernate.
This article by Jeff Palermo showed me how to do that properly by creating a custom type. I use Schema Export to create my DB during my dev cycles, but this method breaks my export statement. Is there a way to specify the type of the column on export?
Here is my enum code:
public enum OperatorCode
{
CodeA,
CodeB,
CodeC,
CodeD
}
Here is my custom type:
public class OperatorCodeType:EnumStringType
{
public OperatorCodeType():base(typeof(OperatorCode),20)
{
}
}
Here is my property in my mapping file:
<property name="OperatorCode" column="OperatorCode" type="OperatorCodeType" />
And finally here is my class declaration for that property:
public virtual OperatorCode OperatorCode { get; set; }
Is it even possible to do this?
I have not tested it, but you can use the Column declaration within a property to specify the sql type. Example from the docs:
<property name="Foo" type="String">
<column name="foo" length="64" not-null="true" sql-type="text"/>
</property>
Granted this is a string, but you may want to try it with the type of OperatorCodeType, column sql-type as text or nvarchar or whatever works.
If you try it, let me know? Not near my dev machine at the moment.