|« Stripping line feeds from dos files||How can I do lazy loading with a one-to-one relationship in hibernate? »|
java.util.UUID primary keys in hibernateNovember 1st, 2008
Surprisingly Hibernate 3 does not support UUID’s. I think the main reason for this is that UUID is not yet an ANSI database type. Each database manufacturer has implemented it in a different way, stored it as a slightly different type and has a driver (or set of drivers) that handle it differently!
For example, MySQL has a UUID() function that returns a 36 character string in the form aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee and expects you to store it in a VARCHAR(36) where as the .NET driver for MySQL will spot that a database column is a BINARY(16) and automatically convert it for you.
There are several supporting classes that you need to write, and a bit of configuration:
UUIDUserType class below was mostly lifted from the Springframework forum site. Thanks bessette for the starting point. I have added the corrections he discovered.
This class takes care of converting a java.util.UUID into something that the database column can coup with and converting the contents of a database column into a normal java.util.UUID. More information can be found in the JavaDoc for UserType.
We are probably going to be using this UUID type for a lot of things, for example if it is a key field, there will be a corresponding column in another table.
I like one hibernate definition per file (which has so many advantages I could get another blog out of it!), so I need a new file to hold
UUIDUserType’s indirection name so that we can reuse it in our other hibernate mapping files.
It would be sensible to call it uuid or guid but this will clash with existing (built-in) indirection names so we must pick another.
The main reason for using UUID’s is that they are unique! So now we need a generator that generates them and returns them in the form we want. Hibernate has 2 uid generators; none of which fit the bill.
The first, uuid, returns a uid in the form “aaaaaaaabbbbccccddddeeeeeeeeeeee” which is generated by hibernate and the second, guid, returns it in the form “aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee” which is generated from a call to the database.
The problem with both of these is that they return the object as a String, which means that your POJO must have the id as a String with the method signature of
Hibernate infers that because the setter for id is a String then id must be a String. We want our id to be a UUID. We don’t want to duplicate getters and setters depending on what situation we are in.
UuidGenerator below generates a UUID which implements the IdentifierGenerator hibernate interface and allows us to return a real UUID which travels through hibernate as a Serializable object, until it passes through
UUIDUserType (above) and ends up in our POJO’s setter as a real UUID.
Below is an example instance of a POJO class with a UUID for an id:
Finally we need to bring it all together,
La solution que tu donnes correspond parfaitement à mon problème. Je l’ai donc suivie scrupuleusement. Mais j’ai une erreur qui est générée suite à l’utilisation de l’UUID:
[MrN: The solution you give fits to my problem. So I followed scrupulously. But I get an error that is generated as a result of using the UUID]
org.springframework.dao.InvalidDataAccessResourceUsageException: Could not execute JDBC batch update; nested exception is org.hibernate.exception.DataException: Could not execute JDBC batch update
Quelqu’un peut-il m’aider ?
[MrN: Can anyone help me?]
You must make sure that the foreign key you are linking to is also set to be a UUID.
You are probably getting the exception because hibernate is trying to insert a record with no id, then trying to update it afterwards with a newly generated key.
Without seeing your code it’s difficult to tell.
I remember getting this exception and used the Eclipse debugger to figure it out, then changed my configuration accordingly.
Thanks for the tutorial. This is great information.
I followed your instructions exactly and am getting the following error:
Initial SessionFactory creation failed.org.hibernate.MappingException: Could not determine type for: bsuuid, for columns: [org.hibernate.mapping.Column(id)]
Any ideas? Thanks.
Thanks very much for your work on the UUID generator and user type. I was looking over to code to incorporate it into my project, and had a few questions about the user type implementation.
In deepCopy, since the UUID is immutable, I don’t believe you need to do any work - just return the value passed in.
For assemble and disassemble and replace, since UUIDs are serializable, is there any need to convert to String?
Under equals(), I don’t believe you need to cast before calling equals. You should just be able to check == and then call .equals().
Hashcode can probably just return hashcode(); I think the casting check is somewhat superfluous.
If your understanding is different from mine, please let me know. I have some UserType experience, but not a ton.
deepCopy - I agree.
serializable - UUID has a readObject() but no writeObject(). If you don’t explicitly assemble them then you get an exception when talking to the database because the (automatic) serialisation done by Java becomes the actual bytes in memory of the UUID which are invalid unicode characters to put into the varchar column in the database. Possibly you could change your schema to be binary but then it makes debugging and reading the database more difficult.
equals - I agree.
hashcode - I agree.
There is a UserType.isMutable() call on the interface so I suspect that a lot of the things you queried might be handled higher up in the hibernate code without calling into this UserType implementation.
My implementation is as much an example as anything else so from it you can work out all the things you need to do (and the way you need to do them) as if it was really a user defined class.
Have the same problem as Gian Paolo with my column type : Char(1)
Anyone have a solution ?
Hi, thanks for the tutorial! In UuidUserType, method deepCopy, performance can be improved by changing:
UUID.fromString (other.toString ()) ;