-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Please Help - <composite-id> problem
PostPosted: Thu Feb 02, 2006 2:55 pm 
Newbie

Joined: Wed Feb 01, 2006 6:17 am
Posts: 11
Hibernate version: 1.0.2
Name and version of the database you are using:SQL Server 2000

Mapping documents:

Order:
Code:
<class name="TestNameSpace.Order, impl" table="tOrder">

  <id name="ID" type="Int32" column="ID">
    <generator class="native" />
  </id>

  <property name="Price" column="Price" type="Double" />
   
  <set name="Users" table="rOrderToUser" cascade="all" inverse="false">
    <key column="OrderID" />
    <many-to-many column="UserID" class="TestNameSpace.User, impl" />
  </set>
   
  <set name="OrderItems" lazy="true" cascade="all" inverse="false">
    <key column="OrderID" />
    <one-to-many class="TestNameSpace.OrderItem, impl" />
  </set>
 
</class>


Warehouse:
Code:
<class name="TestNameSpace.Warehouse, impl" table="tWarehouse">
  <id name="ID" type="Int32" column="ID">
    <generator class="native" />
  </id>
  <property name="Name" column="Name" type="String" />
</class>


OrderItem:
Code:
<class name="TestNameSpace.OrderItem, impl" table="tOrderItem">
<composite-id>
    <key-many-to-one name="Order" class="TestNameSpace.Order, impl" column="OrderID" />
    <key-many-to-one name="Warehouse" class="TestNameSpace.Warehouse, impl" column="WarehouseID" />
  </composite-id>
  <property name="Price" column="Price" type="Double" />
</class>


Classes:
Code:
public class Order
{
   private Int32 id;
   private Double price;
   private ISet users = new HashedSet();
   private ISet orderItems = new HashedSet();

   public Int32 ID
   {
      get { return id; }
      set { id = value; }
   }

   public double Price
   {
      get { return price; }
      set { price = value; }
   }

   public ISet Users
   {
      get { return users; }
      set { users = value; }
   }
   
   public ISet OrderItems
   {
      get { return orderItems; }
      set { orderItems = value; }
   }
}



Code:
public class OrderItem
{
   private Order order;
   private Warehouse warehouse;
   private Double price;
   private int id;

   public int ID
   {
      get { return id; }
      set { id = value; }
   }

   public Warehouse Warehouse
   {
      get { return warehouse; }
      set { warehouse = value; }
   }

   public Order Order
   {
      get { return order; }
      set { order = value; }
   }

   public double Price
   {
      get { return price; }
      set { price = value; }
   }

   public override bool Equals(object obj)
   {
      return base.Equals(obj);
   }

   public override int GetHashCode()
   {
      return base.GetHashCode();
   }
}



Code:
public class Warehouse
{
   private Int32 id;
   private String name;

   public Int32 ID
   {
      get { return id; }
      set { id = value; }
   }

   public String Name
   {
      get { return name; }
      set { name = value; }
   }
}

Code between sessionFactory.openSession() and session.close():
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
order.OrderItems.Add(orderItem);

ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();


Full stack trace of any exception that occurs:
Quote:
SQL insert, update or delete failed (expected affected row count: 1, actual affected row count: 0). Possible causes: the row was modified or deleted by another user, or a trigger is reporting misleading row count.
at NHibernate.Impl.NonBatchingBatcher.AddToBatch(Int32 expectedRowCount)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Boolean[] includeProperty, Object oldVersion, Object obj, SqlString sqlUpdateString, ISessionImplementor session)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Object[] oldFields, Object oldVersion, Object obj, ISessionImplementor session)
at NHibernate.Impl.ScheduledUpdate.Execute()
at NHibernate.Impl.SessionImpl.Execute(IExecutable executable)
at NHibernate.Impl.SessionImpl.ExecuteAll(IList list)
at NHibernate.Impl.SessionImpl.Execute()
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()



No exception occurs when changing the execution order to the following:
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;
session.Save(order);

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
session.Save(orderItem);

session.Close();



When creating the DB using SchemaExport, all tables are created as they should:
tOrder (PK: ID)
tWarehouse (PK: ID)
tOrderItem (PK: OrderID + WarehouseID)


1. Please help in solving out this exception. I want to be able to save the order + orderItems in one batch.
2. How should I implement Equals and GetHashCode in OrderItem (because of composite-id usage):
public override bool Equals(object obj)
public override int GetHashCode()

Thanks
Ruby


Top
 Profile  
 
 Post subject: Try this
PostPosted: Fri Feb 03, 2006 1:34 am 
Regular
Regular

Joined: Tue Jan 03, 2006 7:21 am
Posts: 85
One problem I see is that you are only setting
order.OrderItems.Add(orderItem);

Set the Order property in the OrderItem as well before calling
session.Save(order);

If this does not help can you please post the SQL's being generated by NHib


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 6:16 am 
Newbie

Joined: Wed Feb 01, 2006 6:17 am
Posts: 11
Code section # 1 (save warehouse, save order, save OrderItem):
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;
session.Save(order);

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
session.Save(orderItem);

session.Close();


NHib Generated SQLs for code section # 1:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'


Code section # 2 (save warehouse, batch save order + OrderItem with setting orderItem.Order = order):
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
order.OrderItems.Add(orderItem);

ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();


NHib Generated SQLs for code section # 2:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'
NHibernate: UPDATE dbo.tOrderItem SET Price = @p0 WHERE OrderID = @p1 AND WarehouseID = @p2
@p0 = '0'
@p1 = '1'
@p2 = '1'

Exception code section # 2:
Quote:
SQL insert, update or delete failed (expected affected row count: 1, actual affected row count: 0). Possible causes: the row was modified or deleted by another user, or a trigger is reporting misleading row count.
at NHibernate.Impl.NonBatchingBatcher.AddToBatch(Int32 expectedRowCount)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Boolean[] includeProperty, Object oldVersion, Object obj, SqlString sqlUpdateString, ISessionImplementor session)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Object[] oldFields, Object oldVersion, Object obj, ISessionImplementor session)
at NHibernate.Impl.ScheduledUpdate.Execute()
at NHibernate.Impl.SessionImpl.Execute(IExecutable executable)
at NHibernate.Impl.SessionImpl.ExecuteAll(IList list)
at NHibernate.Impl.SessionImpl.Execute()
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()


Code section # 3 (save warehouse, batch save order + OrderItem without setting orderItem.Order = order):
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
//orderItem.Order = order;
order.OrderItems.Add(orderItem);

ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();


NHib Generated SQLs for code section # 3:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'
NHibernate: UPDATE dbo.tOrderItem SET Price = @p0 WHERE OrderID = @p1 AND WarehouseID = @p2
@p0 = '0'
@p1 = ''
@p2 = '1'

Exception code section # 3:
Quote:
SQL insert, update or delete failed (expected affected row count: 1, actual affected row count: 0). Possible causes: the row was modified or deleted by another user, or a trigger is reporting misleading row count.
at NHibernate.Impl.NonBatchingBatcher.AddToBatch(Int32 expectedRowCount)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Boolean[] includeProperty, Object oldVersion, Object obj, SqlString sqlUpdateString, ISessionImplementor session)
at NHibernate.Persister.EntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Object[] oldFields, Object oldVersion, Object obj, ISessionImplementor session)
at NHibernate.Impl.ScheduledUpdate.Execute()
at NHibernate.Impl.SessionImpl.Execute(IExecutable executable)
at NHibernate.Impl.SessionImpl.ExecuteAll(IList list)
at NHibernate.Impl.SessionImpl.Execute()
at NHibernate.Impl.SessionImpl.Flush()
at NHibernate.Transaction.AdoTransaction.Commit()



in code section 1 - no exception but the relation record (orderItem) is not saved
in code section 2 setting orderItem.Order = order cause changes in NHib's SQL (@p1 = '1'). SQLs look fine IMHO (but the exception occurs).
in code section 3 no setting orderItem.Order = order. Relevant NHib's SQL (@p1 = '').
in both code section 2 + 3 - exception occurs.

Is it because of the default implementation (in class OrderItem) of
public override bool Equals(object obj)
public override int GetHashCode()?

If yes, How should I implement it?


Thanks
Ruby


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 6:51 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
ruby wrote:
NHib Generated SQLs for code section # 1:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'


Seems like it never inserts orderitem?

ruby wrote:
NHib Generated SQLs for code section # 2:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'
NHibernate: UPDATE dbo.tOrderItem SET Price = @p0 WHERE OrderID = @p1 AND WarehouseID = @p2
@p0 = '0'
@p1 = '1'
@p2 = '1'


It tries to update OrderItem but the orderItem is never inserted??

I would try to flush the session before starting transaction:

i.e.
session.Flush();
ITransaction transaction = session.BeginTransaction();

You might found that the NHibernate tries to insert order/orderitem before the transactions is started, because they are related to warehouse (which is saved already), and NHibernate tries to cascade updates/additions...

Gert


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 6:57 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
ruby wrote:
Code section # 2 (save warehouse, batch save order + OrderItem with setting orderItem.Order = order):
Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
order.OrderItems.Add(orderItem);

ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();



AND (and probably only): You MUST explicity call
session.Save(orderItem): NHibernate has no way to distinquish between persistent and new instances of OrderItem, as Your composite-Id does not have unsaved meaning

Gert


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 9:55 am 
Newbie

Joined: Wed Feb 01, 2006 6:17 am
Posts: 11
Gert,
I've tried it, and the only code sequence that makes it work was:

Code:
ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;
session.Save(order);

ITransaction transaction = session.BeginTransaction();

try
{
   OrderItem orderItem = new OrderItem();
   orderItem.Warehouse = warehouse;
   orderItem.Order = order;
   session.Save(orderItem);

   order.OrderItems.Add(orderItem);
   session.Save(order);

   transaction.Commit();
}
catch(Exception)
{
   transaction.Rollback();
}

session.Close();



However, this code produced the following DB calls:

Code:
NHibernate: INSERT INTO dbo.tWarehouse (Name) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = 'TestWarehouse'
NHibernate: INSERT INTO dbo.tOrder (Price) VALUES (@p0); select SCOPE_IDENTITY()
@p0 = '100'
NHibernate: INSERT INTO dbo.tOrderItem (Price, OrderID, WarehouseID) VALUES (@p0, @p1, @p2)
@p0 = '0'
@p1 = '1'
@p2 = '1'
NHibernate: UPDATE dbo.tOrderItem SET OrderID = @p0 WHERE OrderID = @p1 AND WarehouseID = @p2
@p0 = '1'
@p1 = '1'
@p2 = '1'


The code section:
Code:
OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
session.Save(orderItem);

cause the NHibernate: INSERT INTO dbo.tOrderItem....statement

The code section:
Code:
order.OrderItems.Add(orderItem);
session.Save(order);

cause the NHibernate: UPDATE dbo.tOrderItem..... statement
If skipping this code, although the INSERT INTO dbo.tOrderItem....statement, no relation record created in the DB.

Very strange behaviour, I don't get it.
Ruby


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 11:18 am 
Expert
Expert

Joined: Thu Jan 19, 2006 4:29 pm
Posts: 348
Very starnge ineed... Did You try session.Flush() before StartTransaction?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 03, 2006 12:44 pm 
Newbie

Joined: Wed Feb 01, 2006 6:17 am
Posts: 11
OK,
I've changed Code Section 1 to 3 above to include the session.Flush() as Gert offered:

Section 1 with Flush()
[code]ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;
session.Save(order);

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
session.Save(orderItem);
session.Flush();

session.Close();

Section 2 with Flush()
[code]ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
orderItem.Order = order;
order.OrderItems.Add(orderItem);

session.Flush();
ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();[/code]

Section 3 with Flush()
[code]ISession session = sessionFactory.OpenSession();

Warehouse warehouse = new Warehouse();
warehouse.Name = "TestWarehouse";
session.Save(warehouse);

Order order = new Order();
order.Price = 100.0;

OrderItem orderItem = new OrderItem();
orderItem.Warehouse = warehouse;
//orderItem.Order = order;
order.OrderItems.Add(orderItem);

session.Flush();
ITransaction transaction = session.BeginTransaction();
session.Save(order);
transaction.Commit();
session.Close();[/code]


Section 1 is working fine now. 2 and 3 not (exception still occurs) .
The conclusion:

To insert the orderItem (relation record) to the DB one must prepare it with proper refrences (orderItem.Warehouse = warehouse and orderItem.Order = order) save it, and flush the session (or do it inside of a transaction).

Any try to add it to the orderItems collection of the order object and than save the order + orderItem as a batch, will fail.

This is probably because of the <composite-id> ID

Thanks you Gert and samujob.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 05, 2006 7:45 pm 
Newbie

Joined: Thu Mar 02, 2006 2:50 pm
Posts: 3
Quote:
AND (and probably only): You MUST explicity call
session.Save(orderItem): NHibernate has no way to distinquish between persistent and new instances of OrderItem, as Your composite-Id does not have unsaved meaning


but it doesn't make to much sense, the trick is that order and orderItems should be saved with one batch: order.OrderItems.Add(orderItem); ...explicitly calling session.Save(orderItem) just partly destroys the idea of one-to-many mapping, more to assign explicitly PK with FK and we won't need one-to-many property at all (

I also came across this problem, implementing similar example:
Code:
SQL insert, update or delete failed (expected affected row count: 1, actual affected row count: 0). Possible causes: the row was modified or deleted by another user, or a trigger is reporting misleading row count.



Please help in solving out this exception using order.OrderItems.Add(orderItem) only.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.