I've got an app which contains a number of sports clubs, each having a number of contacts.
Each contact can be one of a number of types, and there's a specified display order for these.
I want to specify this sort order for the contacts in my entity relationship, but can't quite see how to do it.
To clarify things a little, here are my three (simplified) entity CFCs:
club.cfc
component persistent="true" table="clubs"
{
// identifier
property name="clubid" fieldtype="id" setter="false" generator="identity";
// properties
property name="clubname";
// relationships
property name="contacts" cfc="contact" singularname="contact" fieldtype="one-to-many" fkcolumn="clubid";
}
contact.cfc
component persistent="true" table="contacts"
{
// identifier
property name="contactid" fieldtype="id" setter="false" generator="identity";
// properties
property name="clubid";
property name="name";
//relationships
property name="contacttype" cfc="contacttype" fieldtype="many-to-one" fkcolumn="type";
}
contacttype.cfc
component persistent="true" table="contacttypes"
{
// identifier
property name="type" fieldtype="id" insert="false" update="false";
// properties
property name="typename";
property name="displayorder";
}
So, in summary, what I want to do is get the club with its contacts sorted according to the displayorder value in contacttype.
Suggestions?
Sounds to me like you need to override getContacts in club.cfc to do a custom lookup using HQL. Unfortunately I'm no HQL wizard, and I don't have your db to test this against. But here's my somewhat wild guess at the HQL:
public array getContacts()
{
return ormExecuteQuery(
"from club cl, contact co, contacttype ct
where co.clubid = cl.clubid and cl = :thisClub
order by ct.displayorder"
, { thisClub = this }
);
}
I'm sure that's not right, but hopefully it'll get you started.
Related
I have parent table "Parent" with column "SpokenlanguageId", "WrittenlanguageId". These two columns are referenced by column "languageid" in child table (both Parent table columns are referred by same child table column).
In parent table hbm file, I have included many to one relationship on parent table columns (SpokenlanguageId, WrittenlanguageId) with cascade = "None"
my scenario would be to insert and update data only in parent table not on child table as it a lookup table. I get the following error with above settings being set on my hbm file
NHibernate.TransientObjectException
object references an unsaved transient instance - save the transient instance before flushing or set cascade
action for the property to something that would make it autosave.
I got above error while flushing the nhibernate session.
Based on more information in the comments, there are more details
parent class:
Language SpokLanguages { get; set; }
Language WrittenLanguages { get; set; }
parent.hbm.xml file:
<many-to-one name="SpokLanguages" class="Language"
column="SpokenLanguageID" unique="true" cascade="none" />
<many-to-one name="WrittenLanguages" class="Language"
column="WrittenLanguageID" unique="true" cascade="none" />
And this was the way how to assign property:
SpokLanguages = new Language() { SpokenLanguageID = 5, };
WrittenLanguages = new Language() { WrittenLanguageID = 6, };
Which was almost correct, but what we need is to assign the ID of Language
parent.SpokLanguages = new Language() { ID = 5, };
parent.WrittenLanguages = new Language() { ID = 6, };
because this ID property is what is mapped here
the column, in the Paren table will contain the ID of the Language. The essence of the mapping <many-to-one> is in fact, that NHibernate is trying to put together the information in the Parent table... with the ID of the Language. The element mapped in Langauge as id
<class name="Language" ...
<id name="ID"
is what we need. If this is missing, entity seems to be transient. And that's why NHibernate blames.
EXTEND:
The more appropriate solution would be to load the instances of selected Languages from DB and assign them. It could be done even with some trick, the Load method. Please, read:
NHibernate – The difference between Get, Load and querying by id
it could look like this
parent.SpokLanguages = session.Load<Language>(5)
parent.WrittenLanguages = session.Load<Language>(6)
i have to model classes User and Order. what i want is to have a relationship between them, so that a user has many orders, which in this case would be a one-to-many rel. which i defined in the User.cfc as follows:
property name="orders"
fieldtype="one-to-many"
cfc="Order"
fkcolumn="userID"
type="array";
each of this cfcs can be loaded through EntityLoad( Entity Name ) without any problems; i see all the data in the dump output.
however, as soon as i put the orders relationship in the User.cfc, it all breaks apart and i get an error message:
Association references unmapped class: Order
here's the code for the cfcs
User.cfc
component persistent="true" datasource="otherDatasource"
{
property name="id" fieldtype="id";
property name="userName";
property name="password";
property name="firstName";
property name="lastName";
property name="title";
property name="orders"
fieldtype="one-to-many"
cfc="Order"
fkcolumn="userID"
type="array";
function init()
{
return this;
}
}
Order.cfc
component persistent="true"
{
property name="id" fieldtype="id" generator="guid";
property name="quantity";
property name="period";
property name="region";
property name="createdAt" ormtype="date";
function init()
{
return this;
}
}
Any ideas what i'm doing wrong here?
it seems as i have my answer. coldfusion 9.0.1 orm isn't capable of building a relationship between tables in different databases.
When I use CF9's ORM feature and generate an explict setter for my ORM CFC, is there anyway to call the default funcitionailty of the ORM CFC after i have done the work needed in the method. For example i am looking for something like this. Obviosuly the code will not run , and super is the wrong concept since the ORM CFC isnt inherting anything, but thats the type of functionality I am looking for.
public void setMovie(String movie){
if(movie == "inception"){
ORMCFC.super().setMovie("Greatest movie ever made")
}else{
ORMCFC.super().setMovie(movie)
}
In your model CFC for the ORM you can specify additional "decorator" functions.
component persistent="true" table="Movie" schema="dbo" output="false"
{
/* properties */
property name="MovieNo" column="MovieNo" type="numeric" ormtype="double" fieldtype="id" ;
property name="Name" column="Name" type="string" ormtype="string" ;
/* decorator */
public void function setMovie(name)
{
if(name == "inception"){
setName("Greatest movie ever made")
}else{
setName(name)
}
}
}
Otherwise if you need to (using your example) setMovie() you will need to do an EntityLoad or create a new entity to set a value to.
Just a quickie ... I have the following ID generator strategy for one of my mapped classes:
<id name="UID" type="System.Guid">
<column name ="UID" sql-type ="uniqueidentifier" />
<generator class="guid.comb" />
</id>
The entity in question is involved in synchronisation / merging behaviours from which it is necessary to have a globally unique identifier.
When an entity is created on a client application for the first time, it's UID property gets assigned so that it is the same value of the equivilent entity on the server.
However the above ID generator strategy overwrites any value provided for new/transient entities.
Whats the fix? Will I have to remove the generator strategy and assign my own GUIDs? Or is the generator strategy configurable to only generate a guid.comb when required?
I think you can accomplish this by making UID a private field and controlling access through the property.
public class MyClass
{
private Guid _uid;
protected MyClass() { // parameterless ctor for NH }
public MyClass(Guid uid) { _uid = uid; // assign on creation }
public Guid Uid
{
get { return _uid; }
private set { // do nothing or compare _uid to Guid.Empty and set }
}
}
For mapping component in nhibernate , is there a way in the hmb file we can indicate a overlaoded constructor to be used instead of default one.
In below mapping nHibernate will use the default constructor of MyClass when reading data from database - I am wondering if we can instruct nhibernate to use a overloaded constructor instead ?
<component name="MyProperty" class="MyClass" >
<property name="Member1" column="member_1" />
<property name="Member2" column="member_2" />
<property name="Member3" column="member_3" />
</component >
Edit #1
Alternatively , does nHibernate allow to map a static value to a property instead of a column ?
Something like below:
<component name="MyProperty" class="MyClass" >
<property name="Member1" column="member_1" />
<property name="Member2" column="member_2" />
<property name="Member3" **value="555"** />
</component >
NHibernate will always use the default constructor to instantiate an object of the type, (unless you want to create some kind of DTO and retrieve it via HQL) and then it will use the properties (or backing fields if specified) to populate the object.
If you have a type for which you do not want to expose a default (no-args) constructor, but you want to make sure that you can only instantiate the type via a specific constructor, then I always do it like this:
public class MyClass
{
private MyClass()
{
// Default constructor has been made private, so it is not usable
// by user code, but NHibernate requires a default constructor
// (it may be private)
}
public MyClass( int member1, int member2, string member3 )
{
}
}
The only standard way you can use constructor with parameters in NHibernate using select new HQL construct:
select new Family(mother, mate, offspr)
from Eg.DomesticCat as mother
join mother.Mate as mate
left join mother.Kittens as offspr
Otherwise it uses parameterless constructors for all purposes. I'm not sure whether this can be altered by hacking NHibernate internals (IClassPersister, IInterceptor, etc.)