Hibernate Books

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 posts ] 
Author Message
 Post subject: Possible bug. Unnecessary updates when using byte arrays
PostPosted: Tue Apr 08, 2008 10:24 pm 
Newbie

Joined: Tue Apr 08, 2008 10:06 pm
Posts: 2
I've got a class with a byte[] array property that maps to a SQL Server table with an image column. When I save a new instance of this class it results in both an insert and update being sent to the database. If I read this record out and immediately save it without modifying anything, an update is sent to the database. If you look at the output below, you'll see that saving Cat only results in a single insert, while Dog (with the byte[] array) results in an insert and 2 updates. I believe that the problem can be traced to the EqualsHelper.cs class. Comparing the currentstate and previousstate byte arrays results in comparing the variable reference rather than the values. Is there something that I'm doing wrong here?

This is using NHibernate 2.0 Alpha 1.

Here is my mapping file.

Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    namespace="ClassLibrary1" assembly="ClassLibrary1">

  <class name="Dog" table="Dog">
    <id name="Id">
      <column name="Id" sql-type="uniqueidentifier" not-null="true"/>
      <generator class="guid.comb" />
    </id>

    <property name="Data" column="Data" type="BinaryBlob" />
  </class>
  <class name="Cat" table="Cat">
    <id name="Id">
      <column name="Id" sql-type="uniqueidentifier" not-null="true"/>
      <generator class="guid.comb" />
    </id>
  </class>

</hibernate-mapping>




Here are my mapped classes
Code:
namespace ClassLibrary1
{
    public class Dog
    {
        private Guid id;
        private byte[] data;

        public virtual Guid Id
        {
            get{ return id; }
            set{ id = value; }
        }

        public virtual byte[] Data
        {
            get { return data; }
            set { data = value; }
        }
    }

    public class Cat
    {
        private Guid id;

        public virtual Guid Id
        {
            get { return id; }
            set { id = value; }
        }
    }
}

Here is the code that I'm executing.

Code:
ISessionFactory sf = new Configuration().Configure().BuildSessionFactory();
ISession s = sf.OpenSession();

ITransaction tx = s.BeginTransaction();
Cat cat1 = new Cat();
Console.WriteLine("Should insert Cat");
s.Save(cat1);
tx.Commit();

Cat cat2 = s.Get<Cat>(cat1.Id);

tx = s.BeginTransaction();
Console.WriteLine("Should not update Cat");
s.Save(cat2);
tx.Commit();

Dog dog1 = new Dog();
dog1.Data = new byte[] {1, 2, 3};
tx = s.BeginTransaction();
Console.WriteLine("Should insert Dog");
s.Save(dog1);
tx.Commit();

Dog dog2 = s.Get<Dog>(dog1.Id);

tx = s.BeginTransaction();
Console.WriteLine("Should not update Dog");
s.Save(dog2);
tx.Commit();

s.Close();


Here is my configuration

Code:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
      <session-factory>
        <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
        <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
        <property name="connection.connection_string">Server=(local);initial catalog=NHibernateTest;Integrated Security=SSPI</property>
        <property name="show_sql">true</property>
        <mapping assembly="ClassLibrary1" />
      </session-factory>
    </hibernate-configuration>


Here is the console output displaying the sql executed.

Should insert Cat
NHibernate: INSERT INTO Cat (Id) VALUES (@p0); @p0 = '3000ffd5-4d07-4397-8d2c-9a78016f0133'
Should not update Cat
Should insert Dog
NHibernate: INSERT INTO Dog (Data, Id) VALUES (@p0, @p1); @p0 = 'System.Byte[]', @p1 = '0044e33c-dd6a-4403-ae38-9a78016f0170'
NHibernate: UPDATE Dog SET Data = @p0 WHERE Id = @p1; @p0 = 'System.Byte[]', @p1 = '0044e33c-dd6a-4403-ae38-9a78016f0170'
Should not update Dog
NHibernate: UPDATE Dog SET Data = @p0 WHERE Id = @p1; @p0 = 'System.Byte[]', @p1 = '0044e33c-dd6a-4403-ae38-9a78016f0170'


Top
 Profile  
 
 Post subject: Possible bug. Unnecessary updates when using byte arrays
PostPosted: Wed Apr 09, 2008 2:46 am 
Senior
Senior

Joined: Thu Jun 21, 2007 8:03 am
Posts: 127
Location: UK
Hi,

A patch has been uploaded for this:
http://jira.nhibernate.org:8080/jira/browse/NH-1246

Hopefully the fix will be applied soon.

Regards,
Richard


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 09, 2008 8:06 am 
Newbie

Joined: Tue Apr 08, 2008 10:06 pm
Posts: 2
Thanks.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 17, 2008 11:36 am 
Newbie

Joined: Wed Sep 10, 2008 5:59 pm
Posts: 8
The same happened to us. Whenever we have a byte[] collection in session even if the collection is not modified, any session flush compares the collection with the cached.

CollectionHelper class on NHibernate.Util namespace, using the method CollectionEquals.cs, the comparison here is a reference comparison so it always finds the collections to be different, this incurss in quite an overhead for our application.


I am getting an error while loading the URL http://jira.nhibernate.org:8080/jira/browse/NH-1246

Could you please verify this link? We need this fix very badly.


Thanks in advance.


Top
 Profile  
 
 Post subject: re: Possible bug. Unnecessary updates when using byte arrays
PostPosted: Wed Dec 17, 2008 11:54 am 
Senior
Senior

Joined: Thu Jun 21, 2007 8:03 am
Posts: 127
Location: UK
Hi,

The correct url is
http://jira.nhibernate.org/browse/NH-1246

The fix was applied before the final release of 2.0, so the latest download should sort the problem.

Regards,
Richard


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 17, 2008 11:55 am 
Newbie

Joined: Wed Sep 10, 2008 5:59 pm
Posts: 8
Never mind my previous post. I wasn't logged into http://jira.nhibernate.org/


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 22, 2008 1:11 pm 
Newbie

Joined: Wed Sep 10, 2008 5:59 pm
Posts: 8
Hi FlukeFan,
Thanks for your reply. I did get the final release and tested the applicarion with it, however the AbstractBinaryType.IsEqual method is still doing a comparison of the byte arrays byte per byte :(
This represents a major performance impact on my application. Please see the details below:

On the method:

public override bool IsEqual(object x, object y)
{
if (x == y)
{
return true;
}
if ((x == null) || (y == null))
{
return false;
}
return CollectionHelper.CollectionEquals<byte>(this.ToInternalFormat(x), this.ToInternalFormat(y));
}



if (x == y) is still comparing references.

I have a the following one to one mapping:

<class name="Namespace.Image, DomainModel" table="Image" lazy="false" select-before-update="true">

<id name="Id" column="ImageId" access="nosetter.camelcase-underscore">
<generator class="identity"/>
</id>

<property name="Title" type="String" column="Title"/>
...
<one-to-one name="ImageDetail" class="ImageDetail, DomainModel" cascade="persist"/>
...
</class>

and

<class name="ImageDetail, DomainModel" table="ImageDetail" lazy="false">
<id name="Id" column="ImageId">
<generator class="foreign">
<param name="property">parentImage</param>
</generator>
</id>
<one-to-one name="parentImage" class="Image, DomainModel" constrained="true"/>
<property name="ImageData" type="BinaryBlob" column="ImageData" />
...
</class>

The ImageDetail and the Image are never updated, they are either deleted or inserted.

Whenever there is a Session Flush and the ImageDetail entities are flushed the byte[] types end up being compared byte by byte.

CollectionHelper.CollectionEquals<byte>(this.ToInternalFormat(x), this.ToInternalFormat(y)


This incurs in a major performance impact for the application. As the images are not edited, these collections remain the same unless they are deleted.

Is there any way to change this and make the AbstractBinaryType.IsEqual different? Am I missing a mapping feature that can bypass this IsEqual call and looping through every byte in the collection?

This implementation of the BinaryBlob mapping is not giving me more choice. Please advise.

Thanks a lot in advance,

Lizet


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 22, 2008 2:52 pm 
Newbie

Joined: Wed Sep 10, 2008 5:59 pm
Posts: 8
Never mind my previous post, I needed a mapping 101

<property name="ImageData" type="BinaryBlob" column="ImageData" update="false" insert="true"/>

did the trick with

session.Save(objectName)
instead of
session.SaveOrUpdate(objectName)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 8 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.