1
votes

I have a simple class, that has a Name member, used to uniquely identify an instance. This class is referenced in a ManyToMany-mapping which will use the Name(since it is the primary key) to create the mapping-table. The type of strings involved makes this very slow (length > 128 chars, often only differing in the last few). To speed it up I'd like to create an auto_increment column (I'm using MySQL) for the join, while still being able to have nhibernate sort out the uniqueness of my objects using the Name-column.

Here is an example of what it looks like. In reality the objects are a lot larger.

class MyObject{
  public String Name;
  public String Value;
}
//Mapping
Id(x => x.Name)
  .Length(255)
  .Unique();
Map(x => x.Value);

This works as intended. If I add a new MyObject to the table nhibernate will do a select first to see if a record with the same Name is already in the table.

I have an ObjectHolder that has a Set of MyObject

class ObjectHolder{
  public UInt64 Id;
  public ICollection<MyObject> MyObjects;
}
//Mapping
Id(x => x.Id)
  .GeneratedBy.Native();
HasManyToMany(x => x.MyObjects)
  .AsSet()
  .Cascade.SaveUpdate();

This will create a mapping using ObjectHolder.Id and MyObject.Name, which is slow. I want the MyObject to have an Id-column that is autogenerated and only used for the mapping-table, but not for the select-to-see-if-it-exists-checking that nhibernate does with the primary key.

I have altered the mapping to

class MyObject{
  public UInt64 Id;
  public String Name;
  public String Value;
}
//Mapping
Id(x => x.Id)
  .GeneratedBy.Native();
Map(x => x.Name)
  .Length(255)
  .Unique();
Map(x => x.Value);

This will create an auto_increment column and use it for the mapping-table, however it will now no longer check correctly if a record with the same Name already exists, which leads to duplicate entry exceptions.

Can I tell nhibernate to check the Name column instead the id, or annotate the Id-column so it is only used as a foreign key, but nothing else? Do I have to modify the manyToMany mapping?

Edit: It seems even worse if I have to use composite ids. All id properties are saved twice, once in the object itself and once in the mapping, which adds a huge time and storage space overhead.

1

1 Answers

1
votes

You're looking at this problem the wrong way, NH isn't supposed to be checking to see if it already exists for you. You should know if it already exists or not.

Checking if a name is unique should be part of your validation and business logic. As such you should check to see if the name is taken before trying to save.

If you were accessing the database yourself you could have handled the constraint violation. However, NHibernate does not support this as it would complicate batching. I.e. the batch would be saved when the transaction is flushed, and that would be the point the error is raised rather than where you persisted the entity. So the only sensible thing NH can do is rollback the whole transaction and throw an error.

Essentially the database constraint is just final failsafe to prevent invalid data, rather than something you rely on to check your business rules.