How does hibernate populate ids of auto generated fields? - sql

Say i have an entity with an auto generated primary key. Now if i try to save the entity with values of all other fields which may not be unique.
The entity gets auto populated with the id of the row got inserted. How did it get hold of that primary key value?
EDIT:
If the primary key column is say identity column whose value is totally decided by the database. So it does an insert statement without that column value and the db decides the value to use does it communicate back its decision (I dont think so)

Hibernate use three method for extracting the DB auto generated field depending on what is support by the jdbc driver or the dialect you are using.
Hibernate extract generated field value to put it back in the pojo :
Using the method Statement.getGeneratedKeys (Statement javadocs)
or
Inserting and selecting the generated field value directly from the insert statement. (Dialect Javadocs)
or
Executing a select statement after the insert to retrieve the generated IDENTITY value
All this is done internally by hibernate.
Hope it`s the explication you are looking for.

This section of the Hibernate documentation describes the auto generation of ids. Usually the AUTO generation strategy is used for maximum portability and assuming that you use Annotations to provide your domain metadata you can configure it as follows:
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
Anyway the supplied link should provide all the detail you need on generated ids.

When you create an object with the, say, sequence-derived surrogate primary key, you pass it to the Hibernate session with that field set to the value that Hibernate interprets as "not assigned", by default 0. This field is not populated with the assigned value until the corresponding record is not inserted into the database table. You can trigger insertion by either explicitly calling flush() on the hibernate session or performing a database read in the same session. After that you can check the value of that field and it will be assigned rather than 0.

Related

Creating my Custom Unique Key

I have a table in my SQL Server. Currently I am using the identity column to uniquely identify each record but my changing needs required a unique key generated in a certain format (as specified by my client). I have tried to generate the unique key from my application by appending a unique integer (that is incremented on every insert) to the format specified my client is not satisfied with my current solution.
It would be great if I can be directed to a better technique to solve my problem rather then my current solution.
The format is like:
PRN-YEAR-MyAppGeneratedInt
Basically, keep the current identity column. That is the best way for you to identify and manage rows in the table.
If the client needs another unique key, then add it. Presumably, it will be a string (given that it has a "format"). You can possibly create the key as a generated column. Alternatively, you may need to use a trigger to calculate it.
In general, integers are better for identity columns, even if end users never see them. Here are some advantages:
They encode the ordering of row insertion in the database. You can, for instance, get the last inserted row.
They are more efficient for foreign key references (because numbers are fixed-length and generally shorter than strings).
They make it possible to directly address a row, when data needs to be fixed.
You can create a SEQUENCE to serve your purpose which were introduced in SQL Server 2012. A real detailed explanation about SEQUENCE can be found here.
Hope this helps :)
As per you specified in the comments the format let me also give you an example that how you can solve your problem using a sequence:
First create a sequence like:
CREATE SEQUENCE SeqName
AS int
START WITH 1
INCREMENT BY 1
CYCLE
CACHE
Next you can use this sequence to generate your desired unique key in you app program.
Get the next value for sequence "SELECT NEXT VALUE FOR SeqName;"
Create a string using the value like :String key= "PRN"+year+SeqValue;
Finally store this string as your unique key in your Insert statement.
You can write the application code as per you need :)
You could create a Computed Column and just append the identity
('Custom_'+CONVERT(varchar(10),iden))

EF performing a select on every insert with Identity column

I have noticed that when I insert with EF, it performs a select to find the next PK.
I have a PK field with Identity set and auto-increment enabled.
Here is the Query
SELECT [ackId]
FROM [dbo].[Acks]
WHERE ##ROWCOUNT > 0 AND [ackId] = scope_identity()
I happened to notice it as it was at the top of the Recent Expensive Queries List in SQL Manger Studio. It doesn't quite make sense that the query to find the PK is more expensive than the actual insert?
Is this normal behaviour? Or is this behaviour causef by entity framework?
Another issue I can think of. If EF is doing a select to get the value, what happens if there are several connections writing to the db? Can there not be a case when the select returns the same value?
Yes it's a normal behavior, when inserting a new entity with identity key.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
which is a default convention for numeric and guid
Code First infers that a property is a primary key if a property on a
class is named “ID” (not case sensitive), or the class name followed
by "ID". If the type of the primary key property is numeric or GUID it
will be configured as an identity column. - MSDN
EF will update the temporary key with the inserted key by selecting the last identity value.
The Entity Framework replaces the value of the property in a temporary
key with the identity value that is generated by the data source after
SaveChanges is called. - MSDN
And selecting an scope_identity will return the last identity value of the inserted entity which will be a new increment value.
If you don't want to select the identity value every time you insert a new entity, you can disable the identity option or using fluent api.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
And If you insert a lot of records and don't want EF to reselect the identity key you can write a normal ADO.NET sql query or you can also try using Bulk Insert.
This is a common pattern found in every ORM that supports database-generated identity keys. Identity is a key concept of entities. For example, two clients with the same name are still two distinct clients. A surrogate key like ClientId is the only way to tell them apart.
An ORM needs to know this surrogate key value in the database and the only way to get it unambiguously when inserting data is by querying scope_identity() directly.
This never causes race conditions, because an identity column is always incremented when an insert happens (it never rolls back) and scope_identity() always returns the identity value that's generated within the scope of the INSERT statement.
The only way to get rid of this expensive pattern is to generate key values in code and set the primary key property to DatabaseGeneratedOption.None. But generating and inserting unique primary key values without concurrency problems is not trivial.
I guess it's something you have to live with. ORMs were never meant to do bulk inserts, there are other ways to do these.

Programmatically get a unique integer using HNibernate HiLo generator

I have a need to generate a unique integer which I use to identify an order to an external system. I'm using the NHibernate HiLo generator in the application, so can I access the generator programmatically to get it to return me a unique integer? I don't need to store this in any database key. I just want to use the NH functionality of generating unique integers.
I'd add a column to the HiLo table that I'd only use for this unique number sequence and not for any real database key. For example, say I added a column "Foo" to the hibernate_unique_key table.
Could I then do something to the effect:
int n = GetNextId("Foo");
Where I just pass the name of the column?
You should be able to just instantiate TableHiLoGenerator or SequenceHiLoGenerator, configure it and call Generate when you need a value.
When calling Generate(), you can pass session.GetSessionImplementation() for the first parameter and null for the second one.
Now, keep in mind that, if you aren't really storing these numbers, it might not be the best idea to use the DB to generate them. GUIDs are effectively unique, 128-bit numbers that can be generated more easily.

Batch insert return identity and object reference/sequence

With SQL Server 2K8 from C# I'm trying to do a batch insert/updates of records to a parent/child tables to optimize.
The inserts/updates will generate a key automatically which I'd like to extract via an OUTPUT, etc. and then reassign back in the domain model. For batch inserts I need to keep track of which newly generated ID belongs to which domain object in the batch list.
This example comes close to what I need, but was wondering if there's a way to not have an extra column added to the table (SequenceNumber) and still achieve the same results: http://illdata.com/blog/2010/01/13/sql-server-batch-inserts-of-parentchild-data-with-ibatis/
ie. could we rely on the order of the inserts generated from the OUTPUT into the temp table, or pass a ref GUID set on the data model and passed temporarily to the SQL just for reference purposes?
In SQL Server 2008 it is possible to use merge and output to get a mapping between the generated key and the key used in the staging table.
Have a look at this question. Using merge..output to get mapping between source.id and target.id
Unless I've misunderstood...
A surrogate key (IDENTITY or NEWID etc) isn't your actual object identifier. It's an implementation detail and has no intrinsic meaning.
You must have another identifier (name, ISBN, serial number, transaction code/date, etc) that is the real (natural) key.
Your OUTPUT clause can return the surrogate key and the natural key. You then use this to map back

Hibernate and IDs

Is it possible in hibernate to have an entity where some IDs are assigned and some are generated?
For instance:
Some objects have an ID between 1-10000 that are generated outside of the database; while some entities come in with no ID and need an ID generated by the database.
You could use 'assigned' as the Id generation strategy, but you would have to give the entity its id before you saved it to the database. Alternately you could build your own implementation of org.hibernate.id.IdentifierGenerator to provide the Id in the manner you've suggested.
I have to agree w/ Cade Roux though, and doing so seems like it be much more difficult than using built in increment, uuid, or other form of id generation.
I would avoid this and simply have an auxiliary column for the information about the source of the object and a column for the external identifier (assuming the external identifier was an important value you wanted to keep track of).
It's generally a bad idea to use columns for mixed purposes - in this case to infer from the nature of a surrogate key the source of an object.
Use any generator you like, make sure it can start at an offset (when you use a sequence, you can initialize it accordingly).
For all other entities, call setId() before you insert them. Hibernate will only generate an id if the id property is 0. Note that you should first insert objects with ids into the db and then work with them. There is a lot of code in Hibernate which expects the object to be in the DB when id != 0.
Another solution is to use negative ids for entities which come with an id. This will also make sure that there are no collisions when you insert an new object.