From 93855478dd22a93c993e446259e4ee194f6797df Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 17 May 2014 21:52:18 +1200 Subject: [PATCH] Fix for #128 storing UUID as binary --- .../server/type/ScalarTypeUUIDBinary.java | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeUUIDBinary.java diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeUUIDBinary.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeUUIDBinary.java new file mode 100644 index 000000000..699cf56cb --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypeUUIDBinary.java @@ -0,0 +1,141 @@ +package com.avaje.ebeaninternal.server.type; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +public class ScalarTypeUUIDBinary extends ScalarTypeBase { + + protected ScalarTypeUUIDBinary() { + super(UUID.class, false, Types.BINARY); + } + + @Override + public int getLength() { + return 16; + } + + public Object toJdbcType(Object value) { + return convertToBytes(value); + } + + @Override + public UUID toBeanType(Object value) { + return convertFromBytes((byte[]) value); + } + + @Override + public String formatValue(UUID v) { + return v.toString(); + } + + @Override + public UUID parse(String value) { + return UUID.fromString(value); + } + + @Override + public UUID parseDateTime(long dateTime) { + throw new IllegalStateException("Never called"); + } + + public boolean isDateTimeCapable() { + return false; + } + + /** + * Convert from byte[] to UUID. + */ + public static UUID convertFromBytes(byte[] bytes) { + + int usableBytes = Math.min(bytes.length, 16); + + // Need exactly 16 bytes - pad the input if not enough bytes are provided + // Use provided bytes in the least significant position; if more than 16 bytes are given, + // then use the first 16 bytes from the array; + byte[] barr = new byte[16]; + for (int i = 15, j = usableBytes - 1; j >= 0; i--, j--) { + barr[i] = bytes[j]; + } + + ByteArrayInputStream bais = new ByteArrayInputStream(barr); + DataInputStream inputStream = new DataInputStream(bais); + + try { + long msb = inputStream.readLong(); + long lsb = inputStream.readLong(); + return new UUID(msb, lsb); + + } catch (IOException e) { + throw new RuntimeException("Not Expecting this", e); + } + } + + /** + * Convert from UUID to byte[]. + */ + public static byte[] convertToBytes(Object value) { + + UUID uuid = (UUID) value; + ByteArrayOutputStream baos = new ByteArrayOutputStream(16); + DataOutputStream outputStream = new DataOutputStream(baos); + + try { + outputStream.writeLong(uuid.getMostSignificantBits()); + outputStream.writeLong(uuid.getLeastSignificantBits()); + } catch (IOException e) { + throw new RuntimeException("Not Expecting this", e); + } + + return baos.toByteArray(); + } + + @Override + public void bind(DataBind b, UUID value) throws SQLException { + if (value == null) { + b.setNull(Types.BINARY); + + } else { + b.setBytes(convertToBytes(value)); + } + } + + @Override + public UUID read(DataReader dataReader) throws SQLException { + byte[] bytes = dataReader.getBytes(); + if (bytes == null) { + return null; + } else { + return convertFromBytes(bytes); + } + } + + @Override + public Object readData(DataInput dataInput) throws IOException { + if (!dataInput.readBoolean()) { + return null; + } else { + return dataInput.readUTF(); + } + } + + @Override + public void writeData(DataOutput dataOutput, Object v) throws IOException { + + String value = (String) v; + if (value == null) { + dataOutput.writeBoolean(false); + } else { + dataOutput.writeBoolean(true); + dataOutput.writeUTF(format(v)); + } + } + +}