I have a mapping like this
<class name="UserFileSummary" table="UserFile" lazy="false">
<id name="FileId" column="UserFileId" type="int">
<generator class="identity" />
</id>...
and a property in the c# object like this
public long FileId { get; set; }
What I don't understand is why when I get an instance using
var myFile = session.Get<UserFileSummary>(id)
change a field value and then save it like this
myFile.myProperty = newValue
session.Save(myFile)
I get an error saying the the Id have been altered from 1 to 1. There are some posts around about this, but this is a simple int column (identity 1, 1). I must have made some basic error, please can anyone help out. Thanks
I've added this bit as an edit as the question turns out to be a non question
The FileId property is type long, and the mapping is type int, that is why altered from 1 to 1 is a problem.
Please give me some feedback if you want me to delete this question, thanks everyone :)
The only thing I can point out from your code is the use of Save for updating an existing entity.
You could avoid calling explicitly the Save method for an entity that's alredy present on the session. Your changes will be automatically persisted when you flush the session.
If you insist to explicitly call the update method I'd say you should use SaveOrUpdate.
First, make the setter on the property private; the project will fail to compile if you have any code that mistakenly sets it. This is a good practice anyway. But it's not foolproof because there could be code inside the class that sets it, so you'll have to check for that.
public int FileId { get; private set; }
Second, closely examine the mapping file to make sure that you haven't mapped the field twice.
The error in my case was due to the data type being a long (int64) in the database and being mapped to an int (int32) in the mapping. This meant that the real value changed whenever the object was saved, thus breaking the identifier. Thanks to everyone who helped out.
Related
Can someone tell me why in NHibernate mapping we can set access="field.camelcase", since we have access="field" and access="property"?
EDIT: my question is "why we can do this", not "what does it mean". I think this can be source of error for developper.
I guess you wonder what use field.camelcase have when we can do the same with just field? That's true, but that would give (NH) properties unintuive names when eg writing queries or reference the property from other mappings.
Let's say you have something you want to map using the field, eg
private string _name;
public string Name { get { return _name; } }
You sure can map the field using "field" but then you would have to write "_name" when eg writing HQL queries.
select a from Foo a where a._name = ...
If you instead using field.camelcase the data, the same query would look like
select a from Foo a where a.Name...
EDIT
I now saw you wrote "field.camelcase" but my answer is about "field.camelcase-underscore". The principles are the same and I guess you get the point ;)
the portion after the '.' is the so called naming strategy, that you should specify when the name you write in the hbm differ from the backing field. In the case of field.camelcase you are allowed to write CustomerName in the hbm, and NHibernate would look for a field with name customerName in the class. The reason for that is NHibernate not forcing you to choose a name convention to be compliant, NH will works with almost any naming convention.
There are cases where the properties are not suitable for NH to set values.
They may
have no setter at all
call validation on the data that is set, which is not used when loading from the database
do some other stuff that is only used when the value is changed by the business logic (eg. set other properties)
convert the value in some way, which would cause NH performing unnecessary updates.
Then you don't want NH to call the property setter. Instead of mapping the field, you still map the property, but tell NH to use the field when reading / writing the value. Roger has a good explanation why mapping the property is a good thing.
I'd like NHibernate to generate guids for entities only if they are not set manually by the user or application. Basically, when saving objects with new Guid() (all zeros), NHibernate should generate one. When saving an object that has a non-zero Guid, it should use that instead.
Is my only option to write my own generator?
edit Folks, I'm aware of 'assigned'. I should have specified I was aware of it. Since it doesn't do what I want it to do, it's not the option I'm looking for. Writing my own generator is an option that works, but I'd like something else. I'm suspecting there is nothing else.
The problem here is that NH needs to know if the object is new or if it already exists. It does this usually by setting the ID.
If you wrote your own generator, it doesn't solve the problem, because it is only called if the object is new.
You could use the assigned generator.
You could use a version column to indicate if the object is new. I never tried it this way, but it should work. NOT having any indication for NH if the object is new causes quite some troubles. Believe me.
You could also have a integer as primary key and the GUID as regular unique field.
I whould generate the id in the class' constructor
class Entity
{
Guid id;
Entity(Guid id = Guid.Empty)
{
if (id == Guid.Empty) this.id = Guid.NewGuid();
else this.id = id;
}
}
Have you tried setting the unsaved-value attribute?
<id name="Id" column="Id" type="Guid" unsaved-value="00000000-0000-0000-0000-000000000000">
<generator class="guid.comb" />
</id>
edit now I understand your question fully another option instead of rolling out your own generator is to use
<generator class="assigned" />
However you cannot use SaveOrUpdate(). Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession. Also you will always need to set the GUID manually on all your NEW entities. Its an option.
If I call Save on a new object, then populate its properties, NHibernate generates and insert statement that contains only the default values for the properties. For example (session is an open ISession):
var homer = new Person();
session.Save(homer);
homer.Name = "Homer J. Simpson";
session.Flush();
I thought that calling Save would make homer persistent and that NH would track any changes and include them in the insert. Instead, it issues an insert with the name property parameter set to null. If I put the Save call after the assignment then it works. This object has a GUID id assigned by NH so it's not doing a premature insert to get an identity.
ETA I'm using session-per-request in an ASP.NET app and the pattern I want to follow is:
MyObject myObject;
if (id == null)
{
myObject = new MyObject();
repository.Add(myObject);
}
else
{
myObject = repository.GetMyObject(id);
}
// populate myObject's properties
// NH magic happens here when the HTTP request ends
I think your assumption in this case is simply incorrect.
Reading the code sample you provided, you could just as well expect NHibernate to insert the object, and then subsequently change the Name and then issue an Update. That, however, would assume that Flush implicitly saves the changed state.
I also wonder why this happens. NH should really wait to insert the object to the database.
Reasons why could do this:
the id, you already said that you are using guids, so this shouldn't be the reason.
there is a query. To ensure that it is performed on actual data, the session is flushed.
there are calculated columns, which need to be read back from the database
there might be other reasons I don't remember.
Is this really the code you are running to reproduce the test?
How does the mapping file look like?
You just mentioned it in the answer to my (perhaps rather naive) comment. You have set session FlushMode to Auto. Change that to Manual and you're more likely to see the behavior you are seeking.
It's still a rather wild guess, simply because so many other properties of your configuration can be at play.
hello i got an application that uses nhibernate as orm, i need to store data that represents time,
whats the best way to do it?
nhibenate dont know to convert time field from db to a timespan, only string.
NHibernate supports DateTime, Ticks, TimeSpan and Timestamp. Make sure you are specifying the type explicitly on your mapping element as the different time types have different semantics so what NHibernate is guessing may not be correct.
If you are and are still having problems, modify your post to include the relevant portions of your entity, mapping file, and the actual problem you are encountering.
Edit:
For example, with the following class for a TimeSpan:
public class MyClass
{
// Other properties
// ...
// ...
public virtual TimeSpan MyTimeProperty { get; set; }
}
And the mapping file:
<!-- other properties -->
<property name="MyTimeProperty" type="TimeSpan" /> <!-- Note: NH expects the DB type to be DbType.Int64 -->
You indicate that you're trying to map a TimeSpan ("nhibenate dont know to convert time field from db to a timespan, only string"). If this is the correct type matching between .NET (typeof TimeSpan) and the database (DbType.Int64), NH should do this automatically (i.e. you shouldn't need to specify type="TimeSpan"). So if it's not working, I suspect there is a problem with the way you have things setup. It may be helpful if you post the property/field declaration with full signature, the <property> line for this property from your mapping file, and the column definition from the database.
Also, make sure you use nullables for your DateTimes that can be null in database.
DateTime? instead of just DateTime.
If not, NHibernate will attempt to initialize your date to a default value which probably isn't what you want.
How do you go about changing the subtype of a row in NHibernate? For example if I have a Customer entity and a subclass of TierOneCustomer, I have a case where I need to change a Customer to a TierOneCustomer but the TierOneCustomer should have the same Id (PK) as the original Customer entity.
The mapping looks something like this:
<class name="Customer" table="SiteCustomer" discriminator-value="C">
<id name="Id" column="Id" type="Int64">
<generator class="identity" />
</id>
<discriminator column="CustomerType" />
... properties snipped ...
<subclass name="TierOneCustomer" discriminator-value="P">
... more properties ...
</subclass>
</class>
I'm using the one-table per class hierarchy model, so using plain-sql, it'd be just a matter of a sql update of the discriminator (CustomerType) and set the appropriate columns relevant for the type. I can't find the solution in NHibernate, so would appreciate any pointers.
I'm also thinking whether the model is correct considering this use-case, but before I go down that route, I want to make sure doing as described above is actually possible in the first place. If not, I'll almost certainly think about changing the model.
Short answer is yes, you can change the discriminator value for the particular row(s) using native SQL.
However, I don't think NHibernate is intended to work this way, since the discriminator is generally "invisible" to the Java layer, where its value is supposed to be set initially according to the class of the persisted object and never changed.
I recommend looking into a cleaner approach. From the standpoint of the object model, you're trying to convert a superclass object into one of its subclass types while not changing the identity of its persisted instance, and that's where the conflict is (the converted object isn't really supposed to be the same thing). Two alternative approaches are:
Create a new instance of TierOneCustomer based on the information in the original Customer object, then delete the original object. If you were relying on the Customer's Primary Key for retrieval, you'll need to take note of the new PK.
or
Change your approach so the object type (discriminator) doesn't need to change. Instead of relying on a subclass to distinguish TierOneCustomer from Customer, you can use a property that you can modify freely at any time, i.e. Customer.Tier = 1.
Here are some related discussions on the Hibernate Forums that may be of interest:
Can we update the discriminator column in Hibernate
Table-per-Class Problem: Discriminator and Property
Converting a persisted instance into a subclass
You're doing something wrong.
What you are trying to do is to change the type of an object. You can't do that in .NET or in Java. That simply doesn't make sense. An object is of exactly one concrete type, and its concrete type cannot be changed from the time the object is created until the time the object is destroyed (black magic notwithstanding). In order to accomplish what you are trying to do, but with the class hierarchy you laid out, you would have to destroy the customer object which you want to turn into a tier-one customer object, create a new tier-one customer object, and copy all the relevant properties from the customer object to the tier-one customer object. That is how you do it with objects, in object-oriented languages, with your class hierarchy.
Obviously, the class hierarchy you have isn't working for you. You don't destroy customers in real life when they become tier-one customers! So don't do it with objects either. Instead, come up with a class hierarchy that makes sense, given the scenarios you need to implement. Your use scenarios include:
A customer who previously is not tier-one status now becomes tier-one status.
That means you need a class hierarchy which can accurately capture this scenario. As a hint, you should favor composition over inheritance. That means, it may be a better idea to have a property named IsTierOne, or a property named DiscountStrategy, etc., depending on what works best.
The entire purpose of NHibernate (and Hibernate for Java) is to make the database invisible. To allow you to work with objects natively, with the database magically there behind the scenes to make your objects persistent. NHibernate will let you work with the database natively, but that's not the type of scenario which NHibernate is built for.
This is REALLY late, but may be of use to the next person looking to do something similar:
While the other answers are correct that you shouldn't change the discriminator in most cases, you can do it purely within the scope of NH (no native SQL), with some clever use of mapped properties. Here's the gist of it using FluentNH:
public enum CustomerType //not sure it's needed
{
Customer,
TierOneCustomer
}
public class Customer
{
//You should be able to use the Type name instead,
//but I know this enum-based approach works
public virtual CustomerType Type
{
get {return CustomerType.Customer;}
set {} //small code smell; setter exists, no error, but it doesn't do anything.
}
...
}
public class TierOneCustomer:Customer
{
public override CustomerType Type {get {return CustomerType.TierOneCustomer;} set{}}
...
}
public class CustomerMap:ClassMap<Customer>
{
public CustomerMap()
{
...
DiscriminateSubClassesOnColumn<string>("CustomerType");
DiscriminatorValue(CustomerType.Customer.ToString());
//here's the magic; make the discriminator updatable
//"Not.Insert()" is required to prevent the discriminator column
//showing up twice in an insert statement
Map(x => x.Type).Column("CustomerType").Update().Not.Insert();
}
}
public class TierOneCustomerMap:SubclassMap<TierOneCustomer>
{
public CustomerMap()
{
//same idea, different discriminator value
...
DiscriminatorValue(CustomerType.TierOneCustomer.ToString());
...
}
}
The end result is that the discriminator value is specified for inserts, and used to determine the instantiated type on retrieval, but then if a record of a different subtype with the same Id is saved (as if the record was cloned or un-bound from the UI to a new type), the discriminator value is updated on the existing record with that ID as an object property, so that future retrievals of that type are as the new object. The setter is required on the properties because AFAIK NHibernate can't be told that a property is read-only (and thus "write-only" to the DB); in NHibernate's world, if you write something to the DB, why wouldn't you want it back?
I used this pattern recently to allow users to change the basic type of a "tour", which is in reality a set of rules governing the scheduling of the actual "tour" (a single digital "visit" to a client's on-site equipment to ensure it all works properly). While they're all "tour schedules" and need to be collectable in lists/queues etc as such, the different types of schedules require very different data and very different processing, calling for a similar data structure as the OP has. I therefore completely understand the OP's desire to treat a TierOneCustomer in a substantially different way while minimizing the effect at the data layer, so, here ya go.
If you're doing it offline (e.g. in a DB upgrade script), just use SQL and ensure consistency yourself.
If this is something you plan will happen in while the app is running, I think your requirements are wrong, just like keeping the same pointer address for a different object is wrong.
If you save the ID and use it to access the customer again (e.g. in a URL) consider making a new field that contains a token for this that will be the business key. Since it's not the ID, it's easy to create a new entity instance and copy over the token (you'll probably need to remove the token from the old one).