Using ValueDataSource to show skalar value - cuba-platform

I want to show a skalar values by using a label. The following example shows the sum of customer order amounts.
https://doc.cuba-platform.com/manual-6.8/value_datasources.html
<dsContext>
<valueCollectionDatasource id="salesDs">
<query>
<![CDATA[select o.customer, sum(o.amount) from demo$Order o group by o.customer]]>
</query>
<properties>
<property class="com.company.demo.entity.Customer" name="customer"/>
<property datatype="decimal" name="sum"/>
</properties>
</valueCollectionDatasource>
here I want to bind the loaded data to the label:
<label datasource="salesDs"
property="name"/>
but nothing is shown.
Why is the label value empty? (salesDs is correctly loaded, I can step through with intelliJ...)
I also tried to get the loaded data from the datasource, but I can't find the correct way.
salesDs.getItem() //returns null
salesDs.getItems() //retruns a collection of KeyValueEntries
But, what is the correct way to get my data from a KeyValueEntry?

It would work if you selected an item in the collection datasource (no matter ValueCollectionDatasource or a normal one). It can be done programmatically via setItem() or by some visual component also connected to the same datasource.
For example, in the below screen the label shows the name of the customer currently selected in the table:
<dsContext>
<valueCollectionDatasource id="customersDs">
<query>
<![CDATA[select e.name, e.email
from sales$Customer e]]>
</query>
<properties>
<property datatype="string"
name="name"/>
<property datatype="string"
name="email"/>
</properties>
</valueCollectionDatasource>
</dsContext>
<layout expand="customersTable" spacing="true">
<table id="customersTable" width="100%">
<columns>
<column id="name"/>
<column id="email"/>
</columns>
<rows datasource="customersDs"/>
</table>
<groupBox caption="Label">
<label id="nameLab"
datasource="customersDs"
property="name"/>
</groupBox>
</layout>

Related

Another NHibernate Mapping Mystery! Getting count in hbm file

I am trying to retrieve the count of items allocated to a container in my hbm file. I've done a bit of digging and managed to get my hbm code this far (below!). I want the count to be retrieved every time a container object is queried. I could use an interceptor but I assume there's a better way. Am I on the right track or should I use a different strategy to get the count loaded up?
Thanks.
P.S. We're using NH v2.2
<?xml version="1.0" encoding="utf-8"?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false" assembly="MyEntities" namespace="Entities.Containers"> <class name="Container" table="[Container]" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<property name="Capacity" column="Capacity">
<column name="Capacity" />
</property>
<property name="Description" column="Description" length="50" type="String">
<column name="Description" />
</property>
<loader query-ref="sqCurrentContainerAllocation"/> </class>
<sql-query name="sqCurrentContainerAllocation">
<return-scalar column="AllocatedItemsCount" type="int"></return-scalar>
SELECT COUNT(*) FROM [ContainerTracking]
WHERE [ContainerId] = :Id </sql-query>
</hibernate-mapping>
If you need to get some calculated property, you can use mapping with formula.
Let's extend your C# class:
public class Container
{
... // ID, Capacity, Description
public virtual int MyCount { get; set; }
And extend your mapping
<class name="Container" table="[Container]"
...
<property name="MyCount" insert="false" update="false" >
<formula>
(
SELECT count(*)
FROM [ContainerTracking] as ct
WHERE ct.[ContainerId] = Id
)
</formula>
</property>
the Id will be replaced with somethink like '_this.Id', the name of the column Id and its alias
This will of course load the count all the time (except projections) so think twice before use it

coldfusion ORM: many-to-many conditional property

I am trying to add a where clause to a many-to-many property I have defined in one of my objects. I must be doing something wrong though because I keep getting hibernate errors saying that the column doesn't exist.
in the template cfc:
<cfproperty
name="Settings"
fieldtype="many-to-many"
cfc="Setting"
linktable="settings_templates"
fkcolumn="templateID"
inversejoincolumn="settingsId"
where="deleted='false'"
>
In the settings cfc:
<cfproperty
name="templates"
fieldtype="many-to-many"
cfc="Template"
linktable="settings_templates"
fkcolumn="settingsId"
inversejoincolumn="templateID"
where="deleted='false'"
>
The error I am getting is:
08/02 16:06:27 [jrpp-170] HIBERNATE ERROR - [Macromedia][SQLServer
JDBC Driver][SQLServer]Invalid column name 'deleted'.
Can anyone see what I am doing wrong? there is a deleted column in both tables, but not in the link table.
The where property behavior for many-to-many is very strange...
In order to debug this, activate the Hibernate logging is primordial.
Refer you to this post: http://www.rupeshk.org/blog/index.php/2009/07/coldfusion-orm-how-to-log-sql/
Take this example:
Article.cfc
/**
* #output false
* #persistent true
* #table article
*/
component {
property name="id" fieldtype="id";
property name="title";
property
name="tags" singularname="tag"
fieldtype="many-to-many" cfc="Tag" linktable="link_article_tag" fkcolumn="articleId"
inversejoincolumn="tagId" where=" deleted = 0 "
;
}
Tag.cfc
/**
* #output false
* #persistent true
* #table tag
*/
component {
property name="id" fieldtype="id";
property name="name";
property name="deleted" dbdefault="0";
property
name="articles" singularname="article"
fieldtype="many-to-many" cfc="Article" linktable="link_article_tag" fkcolumn="tagId"
inversejoincolumn="articleId"
;
}
When I try to list all articles like this ormExecuteQuery('from Article'), see what is appened in the hibernate logs:
select
tags0_.articleId as articleId6_1_,
tags0_.tagId as tagId1_,
tag1_.id as id0_0_,
tag1_.name as name0_0_,
tag1_.deleted as deleted0_0_
from
link_article_tag tags0_
inner join
tag tag1_
on tags0_.tagId=tag1_.id
where
tags0_.deleted = 0
Damned! WTF :| tags0_ == join table ... You see what's wrong?
In order to understand this behavior, I'm going to activate HBMXML generation in Application.cfc with this.ormSettings.saveMapping = true
Here is the files:
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class entity-name="Tag" lazy="true"
name="cfc:www.app.models.test.Tag" table="tag">
<id name="id" type="string">
<column length="255" name="id"/>
</id>
<property name="name" type="string">
<column name="name"/>
</property>
<property name="deleted" type="string">
<column default="0" name="deleted"/>
</property>
<bag name="articles" table="link_article_tag">
<key column="tagId"/>
<many-to-many class="cfc:www.app.models.test.Article" column="articleId"/>
</bag>
</class>
</hibernate-mapping>
What we can see: The where attribute is set at the bag level, not at the to many-to-many. If I edit like this:
<bag name="tags" table="link_article_tag">
<key column="articleId"/>
<many-to-many class="cfc:www.app.models.test.Tag" column="tagId" where=" deleted = 0 "/>
</bag>
The generated SQL become:
select
tags0_.articleId as articleId62_1_,
tags0_.tagId as tagId1_,
tag1_.id as id59_0_,
tag1_.name as name59_0_,
tag1_.deleted as deleted59_0_
from
link_article_tag tags0_
inner join
tag tag1_
on tags0_.tagId=tag1_.id
where
tag1_.deleted = 0
This method works but is not good for code maintenance and readability.
If anyone has a better solution, I'm interested.
So far, the only way around this that I have found is to override the getter using ORMExecuteQuery(). There I can query the objects directly and look for things such as this. Personally, I prefer to work in cfscript, so my code would look something like this:
public array function getSettings() {
return ORMExecuteQuery("SELECT s FROM template t JOIN t.settings s WHERE t.id=:id AND s.deleted=:deleted", {
deleted=false, id=this.getId()
});
}
N.B. – EXAMPLE NOT TESTED
Of course, you don't have to use the parameters as I have, you could just use deleted=false, but that's up to you (I like using parameters, personally).
Also, don't forget the singularName attribute on many-to-many properties.
On a related note, I've had issues using properties on both objects. You really only need to use them on the template object, in this case. Think about it this way: A template needs to know its settings, but the settings don't need to know about the template they belong to – using properties on both objects in this way can lead to errors, in some cases.

many-to-one and where clause

I am working on a legacy database which is quite intricate.
The table customers is shared with the suppliers and who created this structure used a flag to identify the customers.
Since I am only interested in working with records defined as customers I've added a where clause to my mapping:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="MyAssembly.Domain">
<class name="Customer" table="ANSADID" mutable="false" where="ANFCLI = 'Y'">
<composite-id>
<key-property name="CustomerCode" column="ANCOCO" type="String" length="10"></key-property>
<key-property name="Company" column="ANCOSO" type ="String" length="5"></key-property>
</composite-id>
<property name="Name" column="ANINCO" type="String" length="100"></property>
</class>
</hibernate-mapping>
As you can see I've pre-filtered all my customers with this clause: ANFCLI = 'Y'
Everything works perfectly fine if I query customers (the where clause is used):
var customers = session.QueryOver<Domain.Customer>()
.Where(t => t.Company == "ABC01")
.List();
But if I query the orders table - where I've got a many-to-one association:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="MyAssembly" namespace="MyAssembly.Domain">
<class name="Order" table="OCSAORH" mutable="false" where="OCHAMND = 0">
<composite-id>
<key-property name="Number" column="OCHORDN" type="String" length="10"></key-property>
<key-property name="Ver" column="OCHAMND" type="Int32"></key-property>
<key-property name="Company" column="OCHCOSC" type="String" length="5"></key-property>
</composite-id>
<many-to-one name="Customer" class="Customer" lazy="proxy" fetch="join">
<column name="OCHCLII" not-null="true"/>
<column name="OCHCOSC" not-null="true"/>
</many-to-one>
</class>
</hibernate-mapping>
the filter on the entity customers is lost.
I was reading somewhere that the where clause doesn't work on a association and you have to use a where clause on the collection (bag, set, etc etc) but, how can I do that with a many-to-one?
Thanks for you help.
What about mapping Customer using a discriminator using ANFCLI and then setting the discriminator value to 'Y'. I think NHibernate will treat this a little more rigourously than a where clause.
<class name="Customer" table="ANSADID" mutable="false" discriminator-value="Y">
<composite-id>
<key-property name="CustomerCode" column="ANCOCO" type="String" length="10" />
<key-property name="Company" column="ANCOSO" type ="String" length="5" />
</composite-id>
<discriminator column="ANFCLI" />
<property name="Name" column="ANINCO" type="String" length="100" />
</class>
I think degorolls is right:
You would need to have a super class called "Person", and two sub types called "Customer" and "Supplier".
Then you set your mapping so it uses the ANFCLI field as a discriminator, with the Y value for Customer and the N value for Supplier.
This way, you'll have a nice and transparent polymorphism.
(you'll be able to do stuff like "from Customer", or "from Supplier", and that will automagicaly add the where clause).
Hope that helps!
I'm also a newbie using NHibernate but perhaps you can map that relation (Order to Customer) using a bag (as if it would be one to many)!

StoredProcedure returning multiple resultset Sql To Linq using designer

I want to get multiple resultsets from a storedProc using sql to linq. I was not able to generate it from designer so I wrote below code in designer.cs file. But whenever I add something to designer, it refreshes the designer with the markup in .dbml file and hence it removes the below code every time I add something. I have to copy it every time. If I can get corresponding dbml markup for this, it would be great.
[Function(Name = "dbo.GetAllModulesAndOptions")]
[ResultType(typeof(Module))]
[ResultType(typeof(ModuleOption))]
public IMultipleResults GetAllModules()
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())));
return ((IMultipleResults)(result.ReturnValue));
}
I've already defined Module and ModuleOption as tables.
Now when I add below markup in .dbml file it complains
DBML1114: The Name attribute 'Module' of the Type element is already used by another type.
<Function Name="dbo.GetAllModulesAndOptions" Method="GetAllModules">
<ElementType Name="Module">
<Column Name="ModuleId" Type="System.Int64" DbType="BigInt NOT NULL" CanBeNull="false" />
<Column Name="ModuleName" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
<Column Name="Description" Type="System.String" DbType="VarChar(255)" CanBeNull="true" />
<Column Name="SalesDesc" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
<Column Name="ParentModuleId" Type="System.Int32" DbType="Int" CanBeNull="true" />
</ElementType>
<ElementType Name="ModuleOption">
<Column Name="ModuleOptionId" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
<Column Name="ModuleOptionName" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
<Column Name="ModuleOptionDesc" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
<Column Name="DefaultPrice" Type="System.Decimal" DbType="Money" CanBeNull="true" />
<Column Name="ModuleId" Type="System.Int64" DbType="BigInt" CanBeNull="true" />
<Column Name="InUse" Type="System.Int32" DbType="Int" CanBeNull="true" />
</ElementType>
</Function>
I'm using Visual Studio 2008 SP1
Add the method to the partial class for the data context.
You achieve this by adding a file with the same name as the data context alongside the dbml file, and use the class declaration:
public partial class YourDataContext
{
[Function(Name = "dbo.GetAllModulesAndOptions")]
[ResultType(typeof(Module))]
[ResultType(typeof(ModuleOption))]
public IMultipleResults GetAllModules()
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo (MethodInfo.GetCurrentMethod())));
return ((IMultipleResults)(result.ReturnValue));
}
}
I'm replying my own answer.
One cannot use the already defined type for result set of stored procedure. So I had to change the name of ElementType to ModuleResult and ModuleOptionResult.
<Function Name="dbo.GetAllModulesAndOptions" Method="GetAllModules">
<ElementType Name="ModuleResult">
<Column Name="ModuleId" Type="System.Int64" DbType="BigInt NOT NULL" CanBeNull="false" />
<Column Name="ModuleName" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
<Column Name="Description" Type="System.String" DbType="VarChar(255)" CanBeNull="true" />
<Column Name="SalesDesc" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
<Column Name="ParentModuleId" Type="System.Int32" DbType="Int" CanBeNull="true" />
</ElementType>
<ElementType Name="ModuleOptionResult">
<Column Name="ModuleOptionId" Type="System.Int32" DbType="Int NOT NULL" CanBeNull="false" />
<Column Name="ModuleOptionName" Type="System.String" DbType="VarChar(50)" CanBeNull="true" />
<Column Name="ModuleOptionDesc" Type="System.String" DbType="VarChar(MAX)" CanBeNull="true" />
<Column Name="DefaultPrice" Type="System.Decimal" DbType="Money" CanBeNull="true" />
<Column Name="ModuleId" Type="System.Int64" DbType="BigInt" CanBeNull="true" />
<Column Name="InUse" Type="System.Int32" DbType="Int" CanBeNull="true" />
</ElementType>
</Function>
Here are the steps which I took to solve the above problem.
Delete the .designer.cs file
Add above markup to the .dbml file
Exclude .dbml and .dbml.layout file
Include .dbml and .dbml.layout file (this will generate .designer.cs file again but will not include it in the project).
Include .designer file in project.
Get the list of Module type and ModuleOption type as below.
var modules = from row in results.GetResult<ModuleResult>().ToList()
select new Module
{
ModuleId = row.ModuleId,
ModuleName = row.ModuleName,
Description = row.Description,
SalesDesc = row.SalesDesc,
ParentModuleId = row.ParentModuleId
};
var moduleOptions = from row in results.GetResult<ModuleOptionResult>().ToList()
select new ModuleOption
{
ModuleOptionId = row.ModuleOptionId,
ModuleOptionName = row.ModuleOptionName,
ModuleOptionDesc = row.ModuleOptionDesc,
DefaultPrice = row.DefaultPrice,
ModuleId = row.ModuleId,
InUse = row.InUse
};
UPDATE
Still a better way.
Right click the dbml file in solution explorer and select open with. Choose XML Editor and when you save the file within visual studio it automatically generates the designer.cs file.

Hibernate can't load Custom SQL collection

There is a table Item like,
code,name
01,parent1
02,parent2
0101,child11
0102,child12
0201,child21
0202,child22
Create a java object and hbm xml to map the table.The Item.parent is a Item whose code is equal to the first two characters of its code :
class Item{
String code;
String name;
Item parent;
List<Item> children;
.... setter/getter....
}
<hibernate-mapping>
<class name="Item" table="Item">
<id name="code" length="4" type="string">
<generator class="assigned" />
</id>
<property name="name" column="name" length="50" not-null="true" />
<many-to-one name="parent" class="Item" not-found="ignore">
<formula>
<![CDATA[
(select i.code,r.name from Item i where (case length(code) when 4 then i.code=SUBSTRING(code,1,2) else false end))
]]>
</formula>
</many-to-one>
<bag name="children"></bag>
</class>
</hibernate-mapping>
I try to use formula to define the many-to-one relationship,but it doesn't work!Is there something wrong?Or is there other method?
Thanks!
ps,I use mysql database.
add 2010/05/23
Pascal's answer is right,but the "false" value must be replaced with other expression,like "1=2".Because the "false" value would be considered to be a column of the table.
select i.code
from Item i
where (
case length(code)
when 4 then i.code=SUBSTRING(code,1,2)
else 1=2
end)
And I have another question about the children "bag" mapping.There isn't formula configuration option for "bag",but we can use "loader" to load a sql-query.I configure the "bag" as following.But it get a list whose size is 0.What's wrong with it?
<class>
... ...
<bag name="children">
<key />
<one-to-many class="Item"></one-to-many>
<loader query-ref="getChildren"></loader>
</bag>
</class>
<sql-query name="getChildren">
<load-collection alias="r" role="Item.children" />
<![CDATA[(select {r.*} from Item r join Item o where
o.code=:code and
(
case length(o.code)
when 2 then (length(r.code)=4 and SUBSTRING(r.code,1,2)=o.code)
else 1=2
end ))]]>
</sql-query>
According to the documentation, you're supposed to return the value of the computed foreign key, nothing more:
formula (optional): an SQL expression that defines the value for a computed foreign key.
So I would expect a query like this:
select i.code
from Item i
where (
case length(code)
when 4 then i.code=SUBSTRING(code,1,2)
else false
end)
Disclaimer: not tested.