如何在数据库中以二进制形式存储UUID并进行查询(JPA/Hibernate/MySQL)

9
我有一个基于Java/JPA/Hibernate/MySQL的应用程序。我想使用UUID作为对象标识,但我希望确保数据库性能不会受到影响。
我发现了这篇很棒的博客文章JPA and UUID Primary Keys,它让我迈出了一部分的步伐。请注意,通过以二进制形式存储UUID来优化存储(而不是字符串表示)。
这解决了部分问题,因为现在我们可以有效地将对象插入到数据库中。
然而,当我想使用EntityManager.createQuery从数据库查询时,我遇到了问题。是否可以/应该针对二进制数据进行查询?或者,应该将String UUID与二进制版本一起存储以便于查询?

2
你有测量过这个技巧所带来的性能提升吗?我真的不建议这样做,因为应用程序将更难调试,而且从Java和外部查询数据库也会更加困难。如果你想要高效的键,为什么不只使用数字呢? - JB Nizet
3个回答

8

使用 Hibernate 4.1.2 和 MySQL-Connector-J 5.1.18 进行测试后,您可以定义一个 UUID 字段:

@Entity
class EntityType {
    @Column( columnDefinition = "BINARY(16)", length = 16 )
    private UUID id;
}

...并使用UUID实例进行查询:

UUID id = ....;
EntityType result = em.createQuery( 
   “SELECT x FROM EntityType x WHERE x.id = ?1″, EntityType.class )
   .setParameter( 1, id ).getSingleResult();

0
只要您已经拥有二进制格式的ID,查询它就很简单:
byte[] id = ....;
em.createQuery(“SELECT x FROM TableName x WHERE x.id = ?1″, TableName.class).setParameter(1, id).getSingleResult();

实际上,如果您只是按主键查找,您可以使用

em.find(TableName.class, id);

以二进制格式获取ID可能有点麻烦,特别是如果您需要在URL等中传递它。我建议对其进行Base64编码/解码; Apache Commons Codec具有从byte[]转换为URL安全字符串然后再转换回byte[]的辅助方法。


感谢Robert(再次)!我们将ID作为UUID字符串分配。EntityManager().find(TestEntity.class, convert(id))public static byte[] convert(String uuidAsString) { UUID u = UUID.fromString(uuidAsString); ByteBuffer bb = ByteBuffer.allocate(16); bb.putLong(u.getMostSignificantBits()).putLong(u.getLeastSignificantBits()); return bb.array(); } - Jason Chambers

-1

在10亿条记录中,16字节的开销大约为15Gb。如果您确实拥有这么多数据,那么您将面临更严重的可扩展性问题,并且以每个Gb 10美分或更低的价格购买这15Gb并不是什么大问题。许多对多关系可能会更快地增长到这个大小,但这仍然不足以引起太多担忧。

总之,只需使用字符串表示即可。这将为您在处理数据库时节省大量精力,而代价相当小。

P.S. 我个人偏好使用数字ID,但这是另一个讨论话题。


3
亚历克斯 - 存储不是我的关注点,交易性能才是。我正在探索使用基于16字节UUID的主键是否比基于36字节字符串的UUID提供明显的性能优势。 - Jason Chambers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接