Jointable update between transient and persistent objects? - eclipselink

I have a Many-to-Many relationship between a persistent object (retrieved by a query) and a newly created transient object (created with new, not yet persisted).
They are connected via a #JoinTable association, such as:
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "adccollectiontype_cn_node_type", joinColumns = {
#JoinColumn(name = "idadccollectiontype")
}, inverseJoinColumns = {
#JoinColumn(name = "idcn_node_type")
})
and on the other end:
#ManyToMany(mappedBy="cnNodeTypes", cascade=CascadeType.ALL)
Note CascadeType.ALL.
If I simply create an association between these two objects by adding each to the other's collection, and persisting later (another object, and rely on cascading) then the join table is not updated.
However, if I manually persist the transient object before doing the association it seems to work.
Now I've tried to find some info about this case, why and how it works, but I couldn't find any explanation. Maybe someone can enlighten me, whether what I assume is right or not, and the reason behind it.

I'm not sure there is enough information to really explain it. If you are modifying a managed entity, the changes should be picked up when the transaction commits or flush is called. So, is the existing entity managed in the current EntityManager context? Does the following work?
ManagedEntity oe = em.find(ManagedEntity.class, id);
NodeType nt = new NodeType(id);
oe.getCnNodeTypes().add(nt);
nt.getOtherEntityCollection().add(oe);
em.flush();
You could also try calling merge on your managed entity to have it cascade to the new entity instance.

Related

NHibernate thread-safe way of checking if entity exists

I've been banging my head off what should be a simple issue. I'm trying to do the following basic operation
1) Check if entity exists by a field other than ID
2) If not, create entity
Problem is this is in a console app that is multi-threaded, so I need to somehow get an entity by a field other than the ID and set the LockMode to Upgrade (or at least I think thats what needs to be done). From what I see there is no way to do that with ISession.
Any ideas?
in a single process use a global lockobject
lock(existsLocker)
{
var entity = session.Query<Entity>().Where(...).FirstOrDefault();
if (entity == null)
{
entity = new Entity();
session.Save(entity);
session.Flush();
}
}

nHibernate Proxy Load using Natural Key

How would I use nHibernate,configured by fluent nhibernate if it makes any difference, to load an entity using natural/alternate key in some cases, rather than the primary key when using the Load method on an ISession.
I still need the functionality to allow me to do both, and in the majority of cases, the entity will be loaded via the PKey, but in some cases (where an external system is involved), I need to select the record using the natural key.
I'd like to keep the performance benefit Load allows, rather than do a query etc.
// Current
int countryID = 1; // from normal input source
Address a = new Address();
a.Country = session.Load<Country>(countryID);
session.SaveOrUpdate(a);
// Required
string countryCode = "usa"; // from external input source
Address a2 = new Address();
a2.Country = session.LoadViaNatualKeySomehow<Country>(c=> c.Code, countryCode); // :)
session.SaveOrUpdate(a2);
AFAIK, it is not possible. As you can see in Ayendes post, there is a query syntax for criteria, the only natural ID in the whole NHibernate API as far as I know. This query translates into a "normal" query, except of the second level cache handling as described in this post.
It would be nice if it wouldn't at least flush the session.
one simple performance enhancement you can do is turning off auto flush before querying by the (immutable!) natural ID:
session.FlushMode = FlushMode.Never;
session.CreateQuery(...by natural id ...);
session.FlushMode = FlushMode.Auto;
This can make a big difference, but does of course not compete to Load.
The reason why it doesn't exist is most probably the fact the entities in the session are all identified by the id.
If you had it:
var entity1 = session.Load<Entit>(id);
// does not exist
var entity2 = session.LoadByNaturalKey(natural id);
How could NH determine that the id and the natural id are identifying the same object, without loading them from the database? The whole session cache gets into trouble.

Trying to update entities from a disconnected entity

Ok, each and every time I get into this situation, I struggle back and forth until I find a way to solve it (and that is usually not the way I would have liked to solve it).
What I'm talking about is disconnected entities in EF that should update existing entities in the database.
I'll give an example of my problem here (this example is the last time I got into this problem that caused me to write this question).
I have a WCF service that uses Entity Framework as well. The other program that have added a service reference to my service have gotten proxy versions of the Entities as normal.
The case is that the consumer of the service now construct a object of this proxy class, and call the method UpdateEntity on the WCF service. This entity has a foreign key to another type of entities, and the primary key of the entity I want to link this new entity to is also sent as a parameter to this method. In this case, I want the entity with the same primary key in the database to be updated. It seems simple enough right?
My method looks something like this now:
public bool ChangeEntity(MyEntity entity, int otherTableForignKey)
{
//first I verify that the entity to update exist in the system
var entitytochange = entityContext.MyEntities.FirstOrDefault(e => e.Name == entity.Name);
if (systemtochange == null) return false;
try
{
entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey);
//code for updating the entity should go here, but I'm nor sure what
entityContext.SaveChanges();
return true;
}
catch (Exception exc)
{
return false;
}
}
I tried many different combinations of ApplyCurrentValues, Attach, setting ObjectState to Modified and so on, but I get either the error message that I can't add a new entity with the same key as an existing entity, that the object state of the new object can't be Added and so on.
So my question is: What is the best way to do this without writing code that looks like a big hack.
The only way I got this working now was to just set the properties of entitytochange manually with the properties of entity, but it is a bad solution since any added properties to MyEntity will break the code if I don't remember to add code in this method as well, and it seems there really should be another way that is better.
EDIT
When I put entityContext.MyEntities.ApplyCurrentValues(entity); where my comment is put above, I get the following exception on this line:
The existing object in the ObjectContext is in the Added state. Changes can only be applied when the existing object is in an unchanged or modified state.
However, if I remove this line above entity.ForignEntity = entityContext.ForeignEntities.FirstOrDefault(f => f.Key == otherTableForignKey); then the ApplyCurrentValues works without any problems.
Why would me setting the ForeignEntity of the object set it to Added state? So it seems that setting a Property on the Detached entity, attaches it to the context with a state of added?

cached collection not being invalidated by Nhibernate

I have two objects - ContentPage, which has a collection of ChildLinks.
ContentPage
-----------
ID
Title
ChildLink
----------
ID
ParentPageID [ContentPage]
ChildPageID [ContentPage]
Priority
The ContentPage.ChildLinks property utilises the 2nd level cache. I am using Fluent NH to configure Nhibernate, and using Nhibernate 3.1. Cache is set as 'Read-Write' both for the collection, and the 'ChildLink' class.
I've noticed that whenever I delete a ChildLink, the collection cache is not being invalidated. Thus, when I call the ContentPage.ChildLinks, I get an error:
no row with the given identifier exists
I've turned off the cache, and it works well. Shouldn't the cache be automatically invalidated? I am using SysCache as the cache provider, and MySQL as the database.
Thanks in advance!
I had the same problem and I can across the following article which solved my problem:
Inverse Mapped Collections and NHibernate's Second-Level Cache
Basically if you have mapped your collections as inverse, when you delete the child item you also have to make sure to explicitly remove it from the parent collection or the cache state will be invalid after you delete the child. The first thing to check is if the relationship really needs to be inverse.
Assuming inverse is necessary or desired, and using your example:
Instead of only something like:
Session.Delete(ChildLink);
You have to do:
ContentPage.ChildLinks.Remove(ChildLink);
ChildLink.ParentPage = null;
Session.Delete(ChildLink);
You also might need to explicitly save your ContentPage object at this point as well, it depends on your Session flush settings.
I use methods on my entities for managing such inverse relationships, for example:
public ChildLink
{
public ContentPage ParentPage {get;set;}
public void AddToPage(ContentPage addTo)
{
addTo.ChildLinks.Add(this);
this.ParentPage = addTo;
}
public void RemoveFromPage()
{
ParentPage.ChildLinks.Remove(this);
this.ParentPage = null;
}
}
And then when deleting a child object:
ChildLink.RemoveFromPage();
Session.Delete(ChildLink);

Populating an existing entity using NHibernate

Using NHibernate, is it possible to fill an existing object using the results of a query, rather than returning a new entity? For example:
var foo = new Foo();
session.GetById(foo, id);
Well... kind of... If you object is transient you can Session.Get<Foo>(id) another object into NH identity map and then manually copy its fields into your object. If your object is persistent (attached to a session), you can Session.Refresh(foo) to re-retrieve it from DB.
I guess you can try doing Session.Lock on your transient instance to reattach it to the session and then Session.Refresh to refresh it... Should work... at least in theory...