NHibernate 3 and castle proxy: myList.Contains is broken - nhibernate

I'm trying to upgrade a C# .NET 3.5 project from NH 1.2 to NH 3.1. I'm having a hard time with a: myListOfT.contains(someT).
myListOfT contains someT, and myListOfT[0] seems to be someT (I can see it with Visual Studio "spy"). HashCodes are the same. myListOfT[0] and someT are both Castle.Proxies.T...
What is wrong with my code and NH 3.1?
Let's say that I want to know I some User belongs to a Workplace or a child one's.
public bool UserBelongToWorkplace(Workplace wp, User u)
{
if (wp.Users.Contains(u)) return true;
foreach (Workplace subWp in wp.Workplaces)
{
return UserBelongToWorkplace(subWp, u);
}
}
In this context: wp.Users : ReadOnlyCollection<User> (hides IList<Users>)
Nhibernate config:
<property name="cache.use_second_level_cache">true</property>
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
</property>
At runtime, the function always returns false, even if the workplace really contains the user.
With debugger/traces I can check that wp.Users[0] == u (same Id, same HashCode). Both objects are proxyfied. It was working fine before the upgrade to Nhibernate 3.1.
I am pretty sure it has something to do with lazy-loading/proxy as we already had kinda issues, but it was involving type comparison (CProxy_User and User for instance).

Make sure both Equals() and GetHashCode() are correctly implemented for User

Related

Mapping Geometry from SQLServer2008 to .NET (NHibernate 4.0.0.4000)

I try to map a SqlServer2008 geometry with FluentNHibernate. I am using NHibernate version 4.0.0.4000. I installed NHibernate.Spatial, NetTopologySuite, GeoAPI and NHibernate with FluentNhibernate all with NUget.
My Fluent mapping looks like this:
public class ArealMap: ClassMap<Areal>
{
public Areal()
{
Table("Areal");
Id(x => x.Id).Column("Id").GeneratedBy.Identity();
Map(x => x.Geometry).Column("Geometry").CustomType(typeof(MsSql2008GeometryType));
}
}
public class Areal
{
....
public virtual Geometry Geometry{ get; set; }
}
With NHibernate 4.0.0.4000 Areal will always be mapped to null instead of the value in the DB (of type geometry). The non-geometry properties are mapped correctly.
This mapping was working perfectly in NHibernate 3.3.3.
I also added
.Dialect<MsSql2008GeometryDialect>())
to my fluent configuration....
I think all the dlls have the correct version installed, as I got them via NUget. I don´t think its an NH /Fluent NH issue as all my other mappings are working fine...
Is there a new syntax for mapping sql server geometry to NHibernate in the release for NH 4.0? I don´t know what I am missing..
EDIT:
When I change the mapping to:
Map(x => x.Geometry).Column("Geometry").CustomType(typeof(MsSqlLegacyGeometryType));
I don´t get null in Geometry anymore, instead my polygon in database is written
to a mapped as a Geometry of type point with different coordinates....
I think this problem might be caused by different versions of Microsoft.SqlServer.Types beeing used... SqlServer2008 uses version 10.0 and NHibernate.Spatial.MsSql uses version 11.0...
Or there may be breaking changes in GeoApi or NetTopologySuite...
EDIT:
Okay, I found the source of the problem....
I am using Sql Server 2008, so it uses Microsoft.SqlServer.Types.dll in
C:\Windows\assembly\GAC_MSIL\Microsoft.SqlServer.Types\10.0.0.0__89845dcd8080cc91\
However, the NHibernate.Spatial dlls which work with NHibernate 4.0 expect higher SQLServer and reference Microsoft.SqlServer.Types.dll version 11 in
C:\Windows\assembly\GAC_MSIL\Microsoft.SqlServer.Types\11.0.0.0_89845dcd8080cc91\
Anyone know how to fix this issue?
I think I have to checkout / clone one of the NHibernate.Spatial projects and manually reference Microsoft.SqlServer.Types.dll version 10 and then recompile the project...
I am lost however as where to start. What projects do I need to reoompile? NetTopologySuite which is referenced by NHibernate.Spatial or only NHibernate.Spatial?
While perhaps not ideal since it's forcing you to use an older version, I was able to solve a mismatch involving the Geometry DLLS after an NHibernate upgrade by using a binding redirect on the assembly declaration in the web.config
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

JPA with HIBERNATE insert very slow

I am trying to insert some data to SQL Server 2008 R2 by using JAP and HIBERNATE. Everything "works" except for that it's very slow. To insert 20000 rows, it takes about 45 seconds, while a C# script takes about less than 1 second.
Any veteran in this domain can offer some helps? I would appreciate it a lot.
Update: got some great advices from the answers below, but it still doesn't work as expected. Speed is the same.
Here is the updated persistence.xml:
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="ClusterPersist"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>cluster.data.persist.sqlserver.EventResult</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url"
value="jdbc:sqlserver://MYSERVER:1433;databaseName=MYTABLE" />
<property name="javax.persistence.jdbc.user" value="USER" />
<property name="javax.persistence.jdbc.password" value="PASSWORD" />
<property name="javax.persistence.jdbc.driver"
value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
<property name="hibernate.show_sql" value="flase" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.provider_class"
value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />
<property name="hibernate.c3p0.max_size" value="100" />
<property name="hibernate.c3p0.min_size" value="0" />
<property name="hibernate.c3p0.acquire_increment" value="1" />
<property name="hibernate.c3p0.idle_test_period" value="300" />
<property name="hibernate.c3p0.max_statements" value="0" />
<property name="hibernate.c3p0.timeout" value="100" />
<property name="hibernate.jdbc.batch_size" value="50" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
</properties>
</persistence-unit>
And here is the updated code part:
public static void writeToDB(String filePath) throws IOException {
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = (Session) entityManager.getDelegate();
Transaction tx = session.beginTransaction();
int i = 0;
URL filePathUrl = null;
try {
filePathUrl = new URL(filePath);
} catch (MalformedURLException e) {
filePathUrl = (new File(filePath)).toURI().toURL();
}
String line = null;
BufferedReader stream = null;
try {
InputStream in = filePathUrl.openStream();
stream = new BufferedReader(new InputStreamReader(in));
// Read each line in the file
MyRow myRow = new MyRow();
while ((line = stream.readLine()) != null) {
String[] splitted = line.split(",");
int num1 = Integer.valueOf(splitted[1]);
float num2= Float.valueOf(splitted[6]).intValue();
myRow.setNum1(num1);
myRow.setNum2(num2);
session.save(myRow);
if (i % 50 == 0) {
session.flush();
session.clear();
}
i++;
}
tx.commit();
} finally {
if (stream != null)
stream.close();
}
session.close();
}
Updated, here is the source for MyRow:
#Entity
#Table(name="MYTABLE")
public class MyRow {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "Num1")
private int Num1;
#Basic
#Column(name = "Num2")
private float Num2;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public float getNum1() {
return Num1;
}
public void setNum1(float num1) {
Num1 = num1;
}
public int getNum2() {
return Num2;
}
public void setNum2(int num2) {
Num2 = num2;
}
}
The problem
One of the major performance hits if you use Hibernate as your ORM is the way its "dirty check" is implemented (because without Byte Code Enhancement, which is standard in all JDO based ORMs and some others, dirty checking will always be an inefficient hack).
When flushing, a dirty check needs to be carried out on every object in the session to see if it is "dirty" i.e. one of its attributes has changed since it was loaded from the database. For all "dirty" (changed) objects Hibernate has to generate SQL updates to update the records that represent the dirty objects.
The Hibernate dirty check is notoriously slow on anything but a small number of objects because it needs to perform a "field by field" comparison between objects in memory with a snapshot taken when the object was first loaded from the database. The more objects, say, a HTTP request loads to display a page, then the more dirty checks will be required when commit is called.
Technical details of Hibernate's dirty checking mechanism
You can read more about Hibernate's dirty check mechanism implemented as a "field by field" comparison here:
How does Hibernate detect dirty state of an entity object?
How the problem is solved in other ORMs
A much more efficient mechanism used by some other ORMs is to use an automatically generated "dirty flag" attribute instead of the "field by field" comparison but this has traditionally only been available in ORMs (typically JDO based ORMs) that use and promote byte code enhancement or byte code 'weaving' as it is sometimes called eg., http://datanucleus.org and others
During byte code enhancement, by DataNucleus or any of the other ORMs supporting this feature, each entity class is enhanced to:
add an implicit dirty flag attribute
add the code to each of the setter methods in the class to automatically set the dirty flag when called
Then during a flush, only the dirty flag needs to be checked instead of performing a field by field comparison - which, as you can imagine, is orders of magnitude faster.
Other negative consequences of "field by field" dirty checking
The other innefficiency of the Hibernate dirty checking is the need to keep a snap shot of every loaded object in memory to avoid having to reload and check against the database during dirty checking.
Each object snap shot is a collection of all its fields.
In addition to the performance hit of the Hibernate dirty checking mechanism at flush time, this mechanism also burdens your app with the extra memory consumption and CPU usage associated with instantiating and initializing these snapshots of every single object that is loaded from the database - which can run into the thousands or millions depending on your application.
Hibernate has introduced byte code enhancement to address this but I have worked on many ORM persisted projects (both Hibernate and non Hibernate) and I am yet to see a Hibernate persisted project that uses that feature, possibly due to a number of reasons:
Hibernate has traditionally promoted its "no requirement for byte code enhancement" as a feature when people evaluate ORM technologies
Historical reliability issues with Hibernate's byte code enhancement implementation which is possibly not as mature as ORMs that have used and promoted byte code enhancement from the start
Some people are still scared of using byte code enhancement due to the promotion of an anti 'byte code enhancement' stance and the fear certain groups instilled in people regarding the use of byte code enhancement in the early days of ORMs
These days byte code enhancement is used for many different things - not just persistence. It has almost become mainstream.
To enable JDBC batching you should initialize the property hibernate.jdbc.batch_size to between 10 and 50 (int only)
hibernate.jdbc.batch_size=50
If it's still not as fast as expected, then I'd review the document above paying attention to NOTE(s) and section 4.1. Especially the NOTE that says, "Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator."
Old topic but came across this today looking for something else. I had to post on this common problem that is unfortunately not very well understood and documented. For too long, Hibernate's documentation had only that brief note as posted above.
Starting with version 5, there is a better but still thin explanation: https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#identifiers-generators-identity
The problem of slow insert of very large collection is simply poor choice of Id generation strategy:
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
When using Identity strategy, what need to be understood is that the database server creates the identity of the row, on the physical insert. Hibernate needs to know the assigned Id to have the object in persisted state, in session. The database generated Id is only known on the insert's response. Hibernate has NO choice but to perform 20000 individual inserts to be able to retrieve the generated Ids. It doesn't work with batch as far as I know, not with Sybase, not with MSSQL. That is why, regardless how hard you tried and with all the batching properties properly configured, Hibernate will do individual inserts.
The only solution that I know and have applied many time is to choose a client side Id generation strategy instead of the popular database side Identity strategy.
I often used:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#GenericGenerator(strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator")
There's a bit more configuration to get it to work but that the essence of it. When using a client side Id generation, Hibernate will set the Ids of all the 20000 objects before hitting the database. And with proper batching properties as seen in previous answers, Hibernate will do inserts in batch, as expected.
It is unfortunate that Identity generator so convenient and popular, it appears everywhere in all examples without clear explanation of the consequence of using this strategy. I read many so called "advance" Hibernate books and never seen one so far explaining the consequence of Identity on underlying insert performance on large data set.
Hibernate "default mode" IS slow.
Its advantages are Object Relational Mapping and some cache (but obviously it is not very useful for bulk insertion).
Use batch processing instead http://docs.jboss.org/hibernate/core/4.0/devguide/en-US/html/ch04.html

Pass a Guid to NHibernate's ISession.Get<T> method

I'm experiencing with NHibernate 3.1.0 (without Fluent yet) on an existing solution in VS2010. Our DB is MsSql 2008 (I'm using NHibernate.Dialect.MsSql2008Dialect in the cfg.xml file).
I have an ValidationActivity object which I'd like to get from the DB. It has an ActivityID column of type uniqueidentifier (in the DB) / Guid (in the business object).
I've been following Summer Of NHibernate's 1st session so my provider is:
public ValidationActivity GetActivityById(Guid activityGuid)
{
var sessionFactory = new configuration().Configure().BuildSessionFactory();
var session = sessionFactory.OpenSession();
return session.Get<ValidationActivity>(activityGuid);
}
My mapping uses the guid generator:
<id name="ActivityID" type="Guid">
<generator class ="guid"/>
</id>
To test that, I created a test that tries to get an activity with a known Guid (one that I've inserted manually) as follows:
NHibernateDataProvider provider = new NHibernateDataProvider();
Guid guid = new Guid("885f380d-c6e3-459e-8cff-10f96f26cc0a");
Activity testActivity = provider.GetActivityById(guid);
The exception thrown is 'Could not load entity' with the following SQL string:
SELECT validation0_.ActivityID as ActivityID0_0_, validation0_.ActivityName as Activity2_0_0_, validation0_.CreationDate as Creation3_0_0_, validation0_.Owner as Owner0_0_, validation0_.Project as Project0_0_, validation0_.Step as Step0_0_ FROM Activities validation0_ WHERE validation0_.ActivityID=?
It seems to me that the '?' at the end is wrong, but I don't know what am I doing wrong.
Please help.
Thanks.
Take a look at this post with a similar problem:
Mapping Error in NHibernate
Have you tried to run the query above replacing the ? with the actual guid in the database that this test is hitting? Are all the column/table names the same? Seems like it could be a mapping issue.

S#arp Architecture 1.9 + Fluently.Configure() NHiberbante

I have a need to fluently configure nhibernate in my S#arp application so that I can use a custom NHibernate.Search directory for each of my tenants in a multi-tenant app.
However I have googled for hours looking for a solution but can't seem to find anything current that works.
Thanks,
Paul
I haven't tried this myself, but AddConfiguration takes a dictionary of cfgProperties, which I guess you can pass the tenant specific hibernate.search.default.indexBase value to.
I had a look at this, adding the key as described above will cause a problem if you attempt to use CfgHelper.LoadConfiguration() since it will return null.
But you can configure NHSearch to use different directories for each factory using the factory key:
<nhs-configuration xmlns="urn:nhs-configuration-1.0">
<search-factory sessionFactoryName="YOUR_TENANT1_FACTORY_KEY">
<property name="hibernate.search.default.indexBase">~\IndexTenant1</property>
</search-factory>
<search-factory sessionFactoryName="YOUR_TENANT2_FACTORY_KEY">
<property name="hibernate.search.default.indexBase">~\Tenant2</property>
</search-factory>
</nhs-configuration>
If you are following instructions on
http://wiki.sharparchitecture.net/Default.aspx?Page=NHibSearch
You would need to change the method GetIndexDirectory to
private string GetIndexDirectory() {
INHSConfigCollection nhsConfigCollection = CfgHelper.LoadConfiguration();
string factoryKey = SessionFactoryAttribute.GetKeyFrom(this); // Change this with however you get the factory key for your tenants,
string property = nhsConfigCollection.GetConfiguration(factoryKey).Properties["hibernate.search.default.indexBase"];
var fi = new FileInfo(property);
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fi.Name);
}

S#arp Architecture - Rhino Security (unmapped class: Rhino.Security.IUser)

I'm using S#arp Architecture 1.6 and have implemented the Rhino Security integration as per
Rhino Security - S#arp Architecture
I'm using the latest build from Rhino.Commons
My Application_EndRequest method contains
ISession session = NHibernateSession.Current;
My ComponentRegister.cs contains
container.Kernel.Register(
Component.For<IAuthorizationService>()
.ImplementedBy<AuthorizationService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IAuthorizationRepository>()
.ImplementedBy<AuthorizationRepository>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsBuilderService>()
.ImplementedBy<PermissionsBuilderService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsService>()
.ImplementedBy<PermissionsService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IUnitOfWorkFactory>()
.ImplementedBy<NHibernateUnitOfWorkFactory>()
.LifeStyle.Is(LifestyleType.Singleton),
Component.For<Rhino.Commons.IRepository<User>>()
.ImplementedBy<NHRepository<User>>()
.LifeStyle.Is(LifestyleType.Transient)
);
container.AddFacility<FactorySupportFacility>()
.Register(Component.For<ISession>()
.UsingFactoryMethod(() => NHibernateSession.Current)
.LifeStyle.Is(LifestyleType.Transient));
I have also added RhinoSecurityPersistenceConfigurer() as per instructions.
The error I'm recieving on calling
UnitOfWork.Start()
is
An association from the table Permissions refers to an unmapped class: Rhino.Security.IUser
Does anyone know what the cause of this error may be?
Has anyone successfully integrated Rhino.Security with S#arp Architecture?
Any help would be great.
Thanks
Rich
-- Additional Details --
Thanks for all the replies so far.
I've still not been able to resolve this, so thought I'd add more details.
In my Global.asax.cs I have
private void InitializeNHibernateSession()
{
NHibernateSession.Init(
webSessionStorage,
new string[] { Server.MapPath("~/bin/SwitchSnapshot.Data.dll") },
new AutoPersistenceModelGenerator().Generate(),
Server.MapPath("~/NHibernate.config"),
null, null, new RhinoSecurityPersistenceConfigurer());
}
RhinoSecurityPersistenceConfigurer :
public Configuration ConfigureProperties(Configuration nhibernateConfig)
{
Security.Configure<User>(nhibernateConfig, SecurityTableStructure.Prefix);
return nhibernateConfig;
}
I have an AuthorizationAttribute which calls
using (UnitOfWork.Start())
The error is occuring in NHibernateUnitOfWorkFactory.cs as
sessionFactory = cfg.BuildSessionFactory();
You need an NHibernate mapping for your User class (i.e. the class that implements the IUser interface). You also need a table in the database with the correct fields for your User class.
You have to let RS do some configuration work before the SessionFactory is created. Look at the second issue here http://groups.google.com/group/sharp-architecture/browse_frm/thread/4093c52596f54d23/194f19cd08c8fdd7?q=#194f19cd08c8fdd7. It should get you in the right direction.
Thanks to all who helped out.
In the end it was my own fault.
All I needed to do was to follow the S#arp Architecture Instructions a little better.
From an old version of S#arp I had 2 config files hibernate.cfg.xml and NHibernate.config. I thought I still needed both, but all I needed was hibernate.cfg.xml for S#arp version 1.6 and mapped User.cs using Fluent NHibernate.
Other changes I did was in ComponentRegister.cs
container.Kernel.Register(
Component.For<IAuthorizationService>()
.ImplementedBy<AuthorizationService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IAuthorizationRepository>()
.ImplementedBy<AuthorizationRepository>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsBuilderService>()
.ImplementedBy<PermissionsBuilderService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IPermissionsService>()
.ImplementedBy<PermissionsService>()
.LifeStyle.Is(LifestyleType.Transient),
Component.For<IUnitOfWorkFactory>()
.ImplementedBy<NHibernateUnitOfWorkFactory>()
.LifeStyle.Is(LifestyleType.Singleton),
Component.For<Rhino.Commons.IRepository<User>>()
.ImplementedBy<NHRepository<User>>()
.LifeStyle.Is(LifestyleType.Transient)
);
container.Kernel.AddFacility<FactorySupportFacility>()
.Register(Component.For<ISession>()
.UsingFactoryMethod(() => NHibernateSession.Current)
.LifeStyle.Is(LifestyleType.Transient)
);
Then use the following in my code.
var authorizationService = IoC.Resolve<IAuthorizationService>();
using (UnitOfWork.Start())
{
}