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.
Related
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.
How do you set up dynamic property defaults on CF9 ORM objects?
For instance, I know I can set a property default like this:
property name="isActive" default="1";
But what if you want to have a dynamically generated default, such as a date or a UUID?
property name="uuid" default="#createUUID()#";
...throws an error - so what's the workaround for this?
When an Entity object is created the objects constructor is called. This is a great place for running "setup" code.
User.cfc
component persistent="true"
{
property name="id" fieldtype="id" generator="native";
property name="secretKey";
public User function init() {
if (isNull(variables.secretKey))
setSecretKey(createdUUID());
return this;
}
}
Have you tried overloading the getter?
public string function getUUID() {if(variables.UUID EQ ""){ return createUUID(); } else { return variables.firstName; }; }
I can't test that from where I'm at, but I would try.
Following the technique described here, I was able to populate a domain object that uses custom collections for its children. The relevant property mapping looks like this:
<component name="Contacts" class="My.CustomList`1[[Domain.Object, DomainAssembly]], MyAssembly">
<set name="InnerList">
<key column="PARENT_ID" />
<one-to-many class="Contact" />
</set>
</component>
My custom collection class exposes the InnerList property as an ICollection like so:
protected System.Collections.ICollection InnerList
{
get
{
return this;
}
set
{
Clear();
foreach (DomainObject o in value)
{
Add(o);
}
}
}
This worked like a charm to load data from the database and not have to abandon my rather useful custom collection class.
Then I moved on to try implement saving, and following the advice given in this thread, decided to wrap every call to NHibernate in a transaction.
Now when I commit following my load, NHibernate throws an InvalidCastException: "Unable to cast object of type 'My.CustomList`1[Domain.Object, DomainAssembly]' to type 'Iesi.Collections.ISet'."
Is there a way to make this work the way I expect it to?
EDIT:
Following the lead provided by Raphael, I tried switching to ICollection<T> which gives me a different InvalidCastException when I commit the transaction: Unable to cast object of type 'My.CustomList`1[Domain.Object]' to type 'NHibernate.Collection.IPersistentCollection'.
Change the property to be of type
IList<T>
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.
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>