#UniqueConstraints failing on update - ejb-3.0

I have an Entity class in which I put uniqueconstraint annotation
#Table(uniqueConstraints={#UniqueConstraint(columnNames={"staffRecord_id", "defaultLabel_id","company_id","keyCode"})})
public class AllowanceDeduction implements Serializable{
---
What I have noticed that is when I try to save on the table
using
if (allowanceDeduction.getId() == null) {
this.entityManager.persist(allowanceDeduction);
} else {
this.entityManager.merge(allowanceDeduction);
}
when the save or update fails due to a unique constraint. Isn't it only supposed to fail when trying to save a new record that is identical to a record that already exist.
Why would it fail when trying to merge or update?
Please help needed

I can't say for sure but it looks like you're trying to persist a null id
if (allowanceDeduction.getId() == null) {
this.entityManager.persist(allowanceDeduction);
/* don't you need the id set to a non-null value in order to persist it? */
} else {

Related

Spring + Hibernate: Select from repository while it's updating an entry

I've just run into a problem, where I'm trying to select (repo.findById(id)) an object from the database using it's id, while it's being updated (Hibernate's onPreUpdate and onPostUpdate methods in PreUpdateEventListener and PostUpdateEventListener interfaces), but it's throwing a NullPointerException for me.
Perhaps it's easier if I explain it this way:
I have an object with status "PENDING", if it's being changed to "CONFIRMED", I want to check what the previous status was in the onPreUpdate method by doing this:
#Override
public boolean onPreUpdate(PreUpdateEvent preUpdateEvent)
{
final Object entity = preUpdateEvent.getEntity();
if (entity instanceof Status)
{
Status status = (Status) entity;
//Method below throws NullPointerException
Status statusFromRepo = statusRepo.findByStatusId(status.getStatusId());
}
return false;
}
But since statusRepo is already updating this object in the database, am I not able to do anything to get the object BEFORE it's updated? PreUpdateEvent contains the already "updated" version which is going to be saved in the database.

Could this be a bug?

I have the following test case
[TestMethod()]
[DeploymentItem("Courses.sdf")]
public void RemoveCourseConfirmedTest()
{
CoursesController_Accessor target = new CoursesController_Accessor();
int id = 50;
ActionResult actual;
CoursesDBContext db = target.db;
Course courseToDelete = db.Courses.Find(id);
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
actual = target.RemoveCourseConfirmed(courseToDelete);
foreach (var meet in meets)
{
Assert.IsNull(db.Meets.find(meet));
}
Assert.IsNull(db.Courses.Find(courseToDelete.courseID));
}
Which tests the following method from my controller.
[HttpPost, ActionName("RemoveCourse")]
public ActionResult RemoveCourseConfirmed(Course course)
{
try
{
db.Entry(course).State = EntityState.Deleted;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException)
{
return RedirectToAction("RemoveMeet", new System.Web.Routing.RouteValueDictionary { { "concurrencyError", true } });
}
catch (DataException)
{
ModelState.AddModelError(string.Empty, "Unable to save changes. Try again.");
return View(course);
}
}
I know i should be using a Mock db .... but for this project I have decided to go with this approach.
So this what happens. When I run the actual web site this function works perfectly fine and removes the course and all the meets that belong to it.
But when I run the test i get the following exception
System.InvalidOperationException: The operation failed: The relationship could not be
changed because one or more of the foreign-key properties is non-nullable. When a change is
made to a relationship, the related foreign-key property is set to a null value. If the
foreign-key does not support null values, a new relationship must be defined, the foreign-
key property must be assigned another non-null value, or the unrelated object must be
deleted.
Here is the even more interesting part if I comment out the following line from the test
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
and replace the loop with the following:
foreach (var meet in db.Meets.ToList())
{
Assert.IsFalse(meet.courseID == courseToDelete.courseID);
}
I dont get any exceptions and the test case passess.
Am I missing something about Entity Framework or is this a bug?
Well this has been open for a while now. I still haven't been able to find a definite answer but working more with MVC and EF i think what is happening is that once i execute the line
List<CourseMeet> meets = courseToDelete.meets.ToList<CourseMeet>();
the meets get loaded into the object manager and hence when the parent object is deleted the no longer have a reference to the parent course.

Entity Framework Code-First: "The ObjectStateManager cannot track multiple objects with the same key."

I'm running into an issue with Entity Framework code-first in MVC3. I'm hitting this exception:
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
This is addressed many times on SO, but I'm having trouble utilizing any of the suggested solutions in my situation.
Here is a code sample:
FestORM.SaleMethod method = new FestORM.SaleMethod
{
Id = 2,
Name = "Test Sale Method"
};
FestContext context = new FestContext();
//everything works without this line:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
context.Entry(method).State = System.Data.EntityState.Modified;
context.SaveChanges();
EDITED to clarify: I am attempting to update an object that already exists in the database.
Everything works fine without the query noted in the code. In my application, my controller is instantiating the context, and that same context is passed to several repositories that are used by the controller--so I am not able to simply use a different context for the initial query operation. I've tried to remove the entity from being tracked in the ObjectStateManager, but I can't seem to get anywhere with that either. I'm trying to figure out a solution that will work for both conditions: sometimes I will be updating an object that is tracked by the ObjectStateManager, and sometimes it will happen to have not been tracked yet.
FWIW, my real repository functions look like this, just like the code above:
public void Update(T entity)
{
//works ONLY when entity is not tracked by ObjectStateManager
_context.Entry(entity).State = System.Data.EntityState.Modified;
}
public void SaveChanges()
{
_context.SaveChanges();
}
Any ideas? I've been fighting this for too long...
The problem is that this query
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).Single().Name;
brings one instance of the SaleMethod entity into the context and then this code
context.Entry(method).State = System.Data.EntityState.Modified;
attaches a different instance to the context. Both instances have the same primary key, so EF thinks that you are trying to attach two different entities with the same key to the context. It doesn't know that they are both supposed to be the same entity.
If for some reason you just need to query for the name, but don't want to actually bring the full entity into the context, then you can do this:
string thisQueryWillMessThingsUp =
context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name;
If what you are tying to do is update an existing entity and you have values for all mapped properties of that entity, then the simplest thing to do is to not run the query and just use:
context.Entry(method).State = System.Data.EntityState.Modified;
If you don't want to update all properties, possibly because you don't have values for all properties, then querying for the entity and setting properties on it before calling SaveChanges is an acceptable approach. There are several ways to do this depending on your exact requirements. One way is to use the Property method, something like so:
var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();
These blog posts contain some additional information that might be helpful:
http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx
The obvious answer would be that your not actually saving the method object to the database before you call:
//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;
However, I think perhaps this is just a bit a code you left out.
What if you make your entities inherit from an abstract class ie.
public abstract class BaseClass
{
public int Id { get; set; }
}
Then update your Repository to
public class Repository<T> where T : BaseClass
{
.....
public void Update(T entity)
{
_context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified;
}
}
Also you might want to not set the ID of your SaleMethod and let it be generated by the database. Problem could also be because SaleMethod Object in the database has Id of 2 and then you try to add another SaleMethod object with Id 2.
The error you see stems from trying to add another SaleMethod object with ID of 2 to the ObjectStateManager.

How can I create temporary records of Linq-To-Sql types without causing duplicate key problems?

I have code that generates records based on my DataGridView. These records are temporary because some of them already exist in the database.
Crop_Variety v = new Crop_Variety();
v.Type_ID = currentCropType.Type_ID;
v.Variety_ID = r.Cells[0].Value.ToString();
v.Description = r.Cells[1].Value.ToString();
v.Crop = currentCrop;
v.Crop_ID = currentCrop.Crop_ID;
Unfortunately in this little bit of code, because I say that v.Crop = currentCrop,
now currentCrop.Crop_Varieties includes this temporary record. And when I go to insert the records of this grid that are new, they have a reference to the same Crop record, and therefore these temporary records that do already exist in the database show up twice causing duplicate key errors when I submit.
I have a whole system for detecting what records need to be added and what need to be deleted based on what the user has done, but its getting gummed up by this relentless tracking of references.
Is there a way I can stop Linq-To-Sql from automatically adding these temporary records to its table collections?
I would suggest revisiting the code that populates DataGridView (grid) with records.
And then revisit the code that operates on items from a GridView, keeping in mind that you can grab bound item from a grid row using the following code:
public object GridSelectedItem
{
get
{
try
{
if (_grid == null || _grid.SelectedCells.Count < 1) return null;
DataGridViewCell cell = _grid.SelectedCells[0];
DataGridViewRow row = _grid.Rows[cell.RowIndex];
if (row.DataBoundItem == null) return null;
return row.DataBoundItem;
}
catch { }
return null;
}
}
It is also hard to understand the nature of Crop_Variety code that you have posted. As the Crop_Variety seems to be a subclass of Crop. This leads to problems when the Crop is not yet bound to database and potentially lead to problems when you're adding Crop_Variety to the context.
For this type of Form application I normally have List _dataList inside form class, then the main grid is bound to that list, through ObjectBindingList or another way. That way _dataList holds all data that needs to be persisted when needed (user clicked save).
When you assign an entity object reference you are creating a link between the two objects. Here you are doing that:
v.Crop = currentCrop;
There is only one way to avoid this: Modify the generated code or generate/write your own. I would never do this.
I think you will be better off by writing a custom DTO class instead of reusing the generated entities. I have done both approaches and I like the latter one far better.
Edit: Here is some sample generated code:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="RssFeed_RssFeedItem", Storage="_RssFeed", ThisKey="RssFeedID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public RssFeed RssFeed
{
get
{
return this._RssFeed.Entity;
}
set
{
RssFeed previousValue = this._RssFeed.Entity;
if (((previousValue != value)
|| (this._RssFeed.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._RssFeed.Entity = null;
previousValue.RssFeedItems.Remove(this);
}
this._RssFeed.Entity = value;
if ((value != null))
{
value.RssFeedItems.Add(this);
this._RssFeedID = value.ID;
}
else
{
this._RssFeedID = default(int);
}
this.SendPropertyChanged("RssFeed");
}
}
}
As you can see the generated code is establishing the link by saying "value.RssFeedItems.Add(this);".
In case you have many entities for wich you would need many DTOs you could code-generate the DTO classes by using reflection.

Guid values in Oracle with fluentnhibernate

I've only been using fluent nhibernate a few days and its been going fine until trying to deal with guid values and Oracle. I have read a good few posts on the subject but none that help me solve the problem I am seeing.
I am using Oracle 10g express edition.
I have a simple test table in oracle
CREATE TABLE test (Field RAW(16));
I have a simple class and interface for mapping to the table
public class Test : ITest
{
public virtual Guid Field { get; set; }
}
public interface ITest
{
Guid Field { get; set; }
}
Class map is simple
public class TestMap : ClassMap<Test>
{
public TestMap()
{
Id(x => x.Field);
}
}
I start trying to insert a simple easily recognised guid value
00112233445566778899AABBCCDDEEFF
Heres the code
var test = new Test {Field = new Guid("00112233445566778899AABBCCDDEEFF")};
// test.Field == 00112233445566778899AABBCCDDEEFF here.
session.Save(test);
// after save guid is changed, test.Field == 09a3f4eefebc4cdb8c239f5300edfd82
// this value is different for each run so I pressume nhibernate is assigning
// a value internally.
transaction.Commit();
IQuery query = session.CreateQuery("from Test");
// or
// IQuery query = session.CreateSQLQuery("select * from Test").AddEntity(typeof(Test));
var t in query.List<Test>().Single();
// t.Field == 8ef8a3b10e704e4dae5d9f5300e77098
// this value never changes between runs.
The value actually stored in the database differs each time also, for the run above it was
EEF4A309BCFEDB4C8C239F5300EDFD82
Truly confused....
Any help much appreciated.
EDIT: I always delete data from the table before each test run. Also using ADO directly works no problem.
EDIT: OK, my first problem was that even though I thought I was dropping the data from the table via SQL command line for oracle when I viewed the table via oracle UI it still had data and the first guid was as I should have expected 8ef8a3b10e704e4dae5d9f5300e77098.
Fnhibernate still appears to be altering the guid value on save. it alters it to the value it stores in the database but I'm still not sure why it is doing this or how\if I can control it.
If you intend on assigning the id yourself you will need to use a different id generator than the default which is Guid.comb. You should be using assigned instead. So your mapping would look something like this:
Id(x => x.Field).GeneratedBy.Assigned();
You can read more about id generators in the nhibernate documentation here:
http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-id-generator

Resources