How to save a UUID as binary(16) in java

Maximus Decimus picture Maximus Decimus · Oct 28, 2016 · Viewed 10.9k times · Source

I have a table TestTable with columns ID as binary(16) and name as varchar(50)

I've been trying to store an ordered UUID as PK like in this article Store UUID in an optimized way

I see the UUID is saved in database as HEX (blob)

So I want to save this ID from java but I am getting this error

Data truncation: Data too long for column 'ID' at row 1

I am currently using the library sql2o to interact with mysql

So basically this is my code

String suuid = UUID.randomUUID().toString();
String partial_id = suuid.substring(14,18) + suuid.substring(9, 13) + suuid.substring(0, 8) + suuid.substring(19, 23) + suuid.substring(24)
String final_id = String.format("%040x", new BigInteger(1, partial_id.getBytes()));
con.createQuery("INSERT INTO TestTable(ID, Name) VALUES(:id, :name)")
        .addParameter("id", final_id)
        .addParameter("name", "test1").executeUpdate();

The partial id should be something like this 11d8eebc58e0a7d796690800200c9a66

I tried this statement in mysql without issue

insert into testtable(id, name) values(UNHEX(CONCAT(SUBSTR(uuid(), 15, 4),SUBSTR(uuid(), 10, 4),SUBSTR(uuid(), 1, 8),SUBSTR(uuid(), 20, 4),SUBSTR(uuid(), 25))), 'Test2');

But I got the same error when I remove the unhex function. So how can I send the correct ID from Java to mysql?

UPDATE

I solved my problem inspired on the answer of David Ehrmann. But in my case I used the HexUtils from tomcat to transform my sorted UUID string into bytes[]:

byte[] final_id = HexUtils.fromHexString(partial_id);

Answer

David Ehrmann picture David Ehrmann · Oct 28, 2016

Try storing it as bytes:

UUID uuid = UUID.randomUUID();
byte[] uuidBytes = new byte[16];
ByteBuffer.wrap(uuidBytes)
        .order(ByteOrder.BIG_ENDIAN)
        .putLong(uuid.getMostSignificantBits())
        .putLong(uuid.getLeastSignificantBits());

con.createQuery("INSERT INTO TestTable(ID, Name) VALUES(:id, :name)")
    .addParameter("id", uuidBytes)
    .addParameter("name", "test1").executeUpdate();

A bit of an explanation: your table is using BINARY(16), so serializing UUID as its raw bytes is a really straightforward approach. UUIDs are essentially 128-bit ints with a few reserved bits, so this code writes it out as a big-endian 128-bit int. The ByteBuffer is just an easy way to turn two longs into a byte array.

Now in practice, all the conversion effort and headaches won't be worth the 20 bytes you save per row.