我需要使用JPA读取和写入记录到DB2数据库中的表,其中主键是存储在定义为“char(16)bit for data”的列中的UUID。
由于数据以位格式存储在数据库中,我无法将该列视为JPA实体类中的字符串。经过一段谷歌搜索后,我在JPA and UUID primary keys找到了这个网页,并尝试在我的代码中使用带有@Id注释的字节数组数据类型,但是当我执行代码时,它失败并显示错误消息
输入“class model.AbstractEntity”将字段“id”声明为主要字符 键,但不支持“[B”类型的键。
看看JPA标准,似乎不支持字节数组作为主键。
我正在编写的代码是在容器提供的OpenJPA的WebSphere 8.5上运行的EJB的一部分。不幸的是,我无法更改数据库架构。
所以我的问题是在JPA实体类中,当包含数据库中主键的列被定义为“char(16)bit for data”时,我应该使用哪种Java数据类型?
我已经看过这些问题,但是我们没有帮助解决这个问题。
更新 基于@Rick的建议,我创建了一个标识类,其中包含将用于主键的字节数组。但是,每当我尝试将记录持久化到表中时,DB2都会返回错误:
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]
标识类的代码如下:
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());
}
}
是否需要将特定的内容添加到类中以将ByteArrayInputStream转换为DB2可以理解的内容,还是应该由OpenJPA内部处理?我查看了OpenJPA用户指南,但是关于字节数组使用的信息很少。
答案 0 :(得分:3)
OpenJPA支持实体作为标识字段,参考指南在第4.2节“作为标识字段的实体”中讨论。对于具有二进制主键列的旧模式,OpenJPA还支持使用byte []类型的标识字段。使用byte []标识字段时,必须创建标识类。身份类别包含在下面。
http://openjpa.apache.org/builds/latest/docs/docbook/manual.html#jpa_overview_pc_identity
答案 1 :(得分:0)
这个问题在邮件列表的内心深处得到了解答: mailing list archive
您不仅需要创建ID类,还需要正确注释ID列:
表类
@IdClass(ByteArrayId.class)
@Entity
@Table(name = "xxx", schema = "yyy")
public class MediaShare {
@Id
@Column(name = "ID", columnDefinition = "CHAR(16) FOR BIT DATA NOT NULL")
private byte[] id;
}
<强> ID级:强>
import java.util.Arrays;
public class ByteArrayId {
private byte[] id;
/**
* Equality must be implemented in terms of identity field equality, and must use instanceof rather than comparing classes
* directly (some JPA implementations may subclass the identity class).
*/
public boolean equals(Object other) {
if (other == this)
return true;
if (!(other instanceof ByteArrayId))
return false;
ByteArrayId mi = (ByteArrayId) other;
byte[] comp = mi.id;
if (id.length != comp.length)
return false;
for (int i = 0; i < comp.length; i++)
if (comp[i] != id[i])
return false;
return true;
}
/**
* Hashcode must also depend on identity values.
*/
public int hashCode() {
return Arrays.hashCode( id );
}
/**
* Must create Hex-String-Representation of the byte array
*/
public String toString() {
StringBuilder ret = new StringBuilder();
for (Byte b : id) {
String hexString = Integer.toHexString( b & 0xFF );
// pad values < 16 with leading '0' so that we have 2 characters
if (hexString.length() == 1)
ret.append( "0" );
ret.append( hexString );
}
return ret.toString();
}
public byte[] getId() {
return id;
}
public void setId(byte[] id) {
this.id = id;
}
}
要搜索MediaShare,请使用以下JPQL:
String sqlString = "SELECT m FROM MediaShare m WHERE m.id=:id";
Query q = entityManager.createQuery(sqlString);
q.setParameter ("id", byteArray);