I'm facing a little issue that I cannot understand here.
Using this chunk of code:
IEntity myEntity = controller.entityFactory.createEntityInstance(MyEntity.class)
myEntity.straightSetProperty(IEntity.ID, "anId")
myEntity.setReferenceProperty(someReference)
I get an "UOW bad usage" error
BAD SESSION USAGE You are modifying an entity ()[MyEntity] that has not been previously merged in the session.
You should 1st merge your entities in the session by using the backendController.merge(...) method.
The property being modified is [referenceProperty].
But when switching the lines it's okay
IEntity myEntity = controller.entityFactory.createEntityInstance(MyEntity.class)
myEntity.setReferenceProperty(someReference)
myEntity.straightSetProperty(IEntity.ID, "anId")
Any idea why i'm facing this issue ?
Jspresso computes the hashcode of an entity based on its id. this hashcode is indirectly used by Jspresso internally to perform some controls and other operations by using it in Hash[Map|Set].
That's why it's mandatory that :
The id is assigned as soon as the entity instance is created and before any setter or operation is performed on the entity.
The id does not change during the lifetime of the entity.
When you call :
IEntity myEntity = entityFactory.createEntityInstance(MyEntity.class)
a generated id is assigned to the entity.
In scenario 1, you first change the id (which breaks the hashcode), and call a setter. Jspresso thinks this entity has not been correctly registered because it cannot retrieve its id from the internal, hashcode based, storage.
In scenario 2, same violation but you call the setter before changing the id. But I suppose that if you call another setter afterwards, it will fail the same way.
The solution is to use the other signature of the entityFactory create method that allows to pass an id as parameter, e.g.
IEntity myEntity = entityFactory.createEntityInstance(MyEntity.class, "anId")
myEntity.setReferenceProperty(someReference)
which will immediately assign your id to the entity and perform all necessary operations afterwards.
Related
NHibernate's documentations specifically says
5.1.4.7. Assigned Identifiers
If you want the application to assign identifiers (as opposed to having NHibernate generate them), you may use the assigned generator. This special generator will use the identifier value already assigned to the object's identifier property. Be very careful when using this feature to assign keys with business meaning (almost always a terrible design decision).
Due to its inherent nature, entities that use this generator cannot be saved via the ISession's SaveOrUpdate() method. Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession.
But I am using assigned identifiers and session.SaveOrUpdate() and I'm not getting an error/warning of any sort.
What am I missing? Did they change how SaveOrUpdate behaves and now it can be used with assigned identifiers?
I am also using Fluent NHibernate's auto mapping.
Here's the code:
public class MyIDConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.GeneratedBy.Assigned();
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure().Database(SQLiteConfiguration.Standard.UsingFile("testDB.db"))
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Product>(cfg).Conventions.Add<MyIDConvention>()
.BuildSessionFactory();
}
I'm expecting an error when I do:
Product myProduct = new Product(presetID);
session.SaveOrUpdate(myProduct);
transaction.Commit();
but nothing happens.
If I had left out the myIDConvention, then I get the error:
NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect).
Can anyone explain to me what is going on?
When using assigned ids NHibernate can determine if the entity is persistent by comparing the id to the unsaved value setting. My understanding is that the unsaved value setting allows SaveOrUpdate to be used and the documentation you referenced is outdated/misleading.
For example, if your id is int the default unsaved-value is 0.
5.1.4
unsaved-value (optional - defaults to a "sensible" value): An
identifier property value that indicates that an instance is newly
instantiated (unsaved), distinguishing it from transient instances
that were saved or loaded in a previous session.
In lithium, document and record classes, or their superclasses/interfaces, have no save() method. Yet lithium's method for saving the record is as follows:
$record = Model::create()
$record->save()
I am not certain how this works, since record does not have save method (only Model has).
Document and Record extend from a base Entity class. The Entity class has a __call() magic method. See the api doc for Entity::__call over here: http://li3.me/docs/lithium/data/Entity::__call() That method obtains an instance of the Model class associated with the Entity and then calls the method passing the entity object in as the first argument. The Adding Functions To Models section of the Lithium manual also contains additional info related to this.
I am using Model-Glue/Coldspring for a new application and I thought I would throw CF9 ORM into the mix.
The only issue I am having right now is with populating an entity with an object. More or less the code below verifies that only one username can exist. There is some other logic that is not displayed.
My first thought was to using something like this:
var entity = entityload('UserAccount' ,{UserName=arguments.UserAccount.getUserName()},"true")
entity = arguments.UserAccount;
How ever this does not work the way that I expected. Is it even possible to populate an entity with an object or do I need to use the setters?
Not sure if this is what you're looking for. If you have...
component persistent="true" entityName="Foo"
{
property a;
property b;
}
You can pass a struct in the 2nd param to init the entity (added in CF9.0.1 I believe)
EntityNew("Foo", {a="1",b="2"});
To populate Foo with another object, you can use the Memento pattern, and implement a GetMemento() function to your object that returns a struct of all its properties.
EntityNew("Foo", bar.getMemento());
However, CF does NOT call your custom setters! If you want to set them using setters, you may add calls to the setters in your init() constructor, or use your MVC framework of choice to populate the bean. In Model-Glue, it is makeEventBean().
Update: Or... Here's hack...
EntityNew("Foo", DeserializeJSON(SerializeJSON(valueObject)));
Use this at your own risk. JSON might do weird things to your numbers and the 'yes','no','true','false' strings. :)
Is it even possible to populate an entity with an object or do I need to use the setters?
If you mean "Is it possible to create load an ORM Entity from an instance of that persistent CFC that already exists and has properties set?", then yes you can using EntityLoadByExample( object,[unique] )
entity = EntityLoadByExample( arguments.userAccount,true );
This assumes the userAccount CFC has been defined as persistent, and its username value has been set before being passed in (which seems to be the case in your situation).
Bear in mind that if any other properties have been set in the object you are passing, including empty strings, they will be used as filters to load the entity, so if they do not exactly match a record in your database, nothing will be loaded.
I've been using the IPreUpdateEventListener for auditing entities, in particular using the FindDirty method to find the changed properties:
public bool OnPreUpdate(PreUpdateEvent updateEvent)
{
int[] dirtyFieldIndices = updateEvent.Persister.FindDirty(updateEvent.State, updateEvent.OldState, updateEvent.Entity, updateEvent.Session);
// Get changed property names and audit...
}
This works fine for simple properties. However, my entity has a collection property of other entities. One of these entities has changed, and the change gets persisted, but FindDirty does not give me the index of that collection property. Is there any way of getting hold of the changed property in order to audit this change?
I have decided to have a method on my domain objects that receives the OldState collection, and applies its own processing on it, checking each object to see if it has changed.
I have a call that needs to determine if a field has changed. But calling get using that entities id returns the same entity not the prior version.
Entity e = Dao.Get(id);
//At this point e.Field is X
e.Field = y;
Dao.Save(e);
Entity Dao.Get(Guid id)
{
return Session.Get(id);
}
Entity Dao.Save(Entity e)
{
Entity olde = Session.Get(e.Id);
if (e.Field != olde.Field) <--- e.Field == olde.Field so it does not run.
DoBigMethod(e);
return e;
}
How do I handle this situation without adding an onChange method to the Entity class.
You only know one "version" of the entity: the current one. There is actually only one version of the entity. You have it in memory and you already changed it and forgot the previous state.
Call get to see the previous database state is dangerous. If changes are already flushed (NHibernate flushes before queries for instance), you get your changes. If you open another session, you see changes from other transactions.
Are you only interested in one single field? Then you can cache the old value somewhere.
If this wouldn't work, you need to tell me more about the reason why you need to know the previous value of this field.
EDIT:
Some more ideas:
cache the previous state of the field when you get the object, in DAO.Get
implement this property that it sets a flag if it changed.
consider to make this change an explicit operation called by the client, instead of an implicit operation that is called when the flag changes. For instance, if this flag is called "Activated", implement a "Activate" and "Deactivate" method. This methods change that flag and perform the "large set of code". The flag is read-only for the rest of the world.