I have a requirement to read and write records using JPA to a table in a DB2 database where the primary key is a UUID stored in a column defined as "char(16) bit for data".
As the data is stored in the database in bit format, I'm unable to treat the column as a string within the JPA entity class. After a bit of Googling I found this webpage on JPA and UUID primary keys and tried using a byte array data type with the @Id annotation in my code, however when I executed the code it failed with an error message saying
Type "class model.AbstractEntity" declares field "id" as a primary key, but keys of type "[B" are not supported.
Looking at the JPA standard it appears that byte arrays are not supported as primary keys.
The code I'm writing is part of an EJB running on WebSphere 8.5 with OpenJPA provided by the container. Unfortunately I'm not able to change the database schema.
So my question is in a JPA entity class which Java data type should I use when the column containing the primary key in the in the database is defined as "char(16) bit for data"?
I've already taken a look at these SO questions but they don't help with the issue I've got.
Update Based on @Rick's suggestion, I have created an identity class which contains the byte array that will be used for the primary key. However whenever I try to persist the record to the table DB2 returns the error:
Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: The value of input variable, expression or parameter number "1" cannot be used because of its data type.. SQLCODE=-301, SQLSTATE=07006, DRIVER=3.63.123 {prepstmnt -937290353 INSERT INTO FRAMEWORK.SITE3 (ID, SITE_ADDRESS, SITE_NAME, ROW_VERSION) VALUES (?, ?, ?, ?) [params=(InputStream) java.io.ByteArrayInputStream@94c7f78e, (String) test, (String) test, (int) 1]} [code=-301, state=07006]
The code for the identity class is as follows:
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
import javax.persistence.Embeddable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
@Embeddable
@Access(AccessType.FIELD)
public class UniqueId implements Serializable {
private static final long serialVersionUID = 4458438725376203754L;
@Column(name="ID")
private byte[] id;
public UniqueId() {}
public UniqueId(byte[] id) {
this.id = id;
}
public UniqueId(String id) {
this(toByteArray(UUID.fromString(id)));
}
@Override
public String toString() {
return toUUID(id).toString();
}
public static UniqueId fromString(String s) {
return fromUUID(UUID.fromString(s));
}
public static UniqueId fromUUID(UUID uuid) {
return new UniqueId(toByteArray(uuid));
}
private static byte[] toByteArray(UUID uuid) {
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return bb.array();
}
private static UUID toUUID(byte[] byteArray) {
long msb = 0;
long lsb = 0;
for (int i = 0; i <8; i++)
msb = (msb << 8) | (byteArray[i] & 0xff);
for (int i = 8; i < 16; i++)
lsb = (lsb << 8) | (byteArray[i] & 0xff);
UUID result = new UUID(msb, lsb);
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(id);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UniqueId other = (UniqueId) obj;
if (!Arrays.equals(id, other.id))
return false;
return true;
}
public static UniqueId generate() {
return fromUUID(UUID.randomUUID());
}
}
Is there something specific that needs to be added to the class to convert the ByteArrayInputStream into something that can be understood by DB2 or should this be handled internally by OpenJPA? I've looked through the OpenJPA user guide but there is very little information on the use of byte arrays.