Hibernate 和 UUID 标示符

简介html


在前一篇博客 (https://www.javacodegeeks.com/2014/06/database-primary-key-flavors.html)中,咱们讨论了UUID代理键(surrogate keys)以及比自增标示符更适用的使用场景。java


UUID的数据库生成方式git


有不少种方式可表达一个128位UUID。每当遇到疑问时,我喜欢用stack exchange来做为权威参考。github


因为表ID通常会建索引,通过压缩的数据类型会占用更少的空间。按照效率由高到低有以下几种选择:数据库


  1. 一部分数据库(PostgreSQL, SQL Server) 提供了专门的UUID 存储数据类型。数组


  2. 另外还可把若干bit存放到字节数组(好比Oracle 的RAW(16) 或标准类型的BINARY(16) )。session


  3. 或可用2个bigint列(64位),但这种复合列的ID的效率低于单一列。app


  4. 把16进制值存入一个char(36)列(其包含32个16进制数和4个”-“),但这会占用不少磁盘空间,因此只是一个低效备选方案。dom


Hibernate提供了多种id策略供选择,针对UUID有如下三种:ide


  • 由应用程序逻辑来分配的uuid生成。


  • 16进制的字符串uuid生成器。


  • uuid2生成器更灵活些。它能够借用 java.lang.UUID ,这是16个字节数据或16进制字符串。


(程序)自定义生成方式


自定义生成方式可让应用程序的逻辑来处理实体ID的生成过程。Hibernate经过简单的忽略ID生成器定义来处理自定义ID。因为数据库使用了HSQLDB,因此下面示例使用BINARY(16) 列类型。


@Entity(name = "assignedIdentifier")

public static class AssignedIdentifier {

    @Id

    @Column(columnDefinition = "BINARY(16)")

    private UUID uuid;

        public AssignedIdentifier() {

    }

        public AssignedIdentifier(UUID uuid) {

        this.uuid = uuid;

    }

}


持久化实体:


session.persist(new AssignedIdentifier(UUID.randomUUID()));

session.flush();


具体生成一个INSERT语句:


Query:{[insert into assignedIdentifier (uuid) values (?)][[B@76b0f8c3]}


咱们能够看看使用merge后会发生什么:


session.merge(new AssignedIdentifier(UUID.randomUUID()));

session.flush();


此次既有select查询又有insert:


Query:{[select assignedid0_.uuid as uuid1_0_0_ from assignedIdentifier assignedid0_ where assignedid0_.uuid=?][[B@23e9436c]} 

Query:{[insert into assignedIdentifier (uuid) values (?)][[B@2b37d486]}


持久化方法会把transient实体添加到当前Hibernate session中。不过,若是session已有其余实体或当前实体已被去除,就会抛出一个exception。若是须要,针对transient实体和已去除的实体,合并操做会复制当前对象的状态到已持久化的实体中。但transient实体的持久化比合并要高效不少。


对于已生成的ID,因为Hibernate并不能知道库中有重复ID,因此合并还须要一次select查询。对于其余的ID生成器,Hibernate会找一空ID来判断该实体有没有处于transient状态。所以,若是使用自定义ID,那么Spring Data包里面 SimpleJpaRepository#save(S entity) 方法并非最好选择:


@Transactional

public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {

        em.persist(entity);

        return entity;

    } else {

        return em.merge(entity);


这个方法对于自定义ID会调用em.merge()而不是em.persist()。因此每次插入新实体都须要先select而后才能insert,效率天然会低些。


UUID生成器


此次咱们再也不用程序分配ID,而是让Hibernate来生成。若是遇到空ID,Hibernate就认为是transient实体,会给出一个新ID值。另外,merge方法也再也不须要先select查询了。


UUIDHexGenerator


UUID hex generator 是最古老的UUID identifier 生成器,也是一种 “uuid”类型。可生成一个32位 16进制UUID字符串(也带有分隔符),模式遵循:8{sep}8{sep}4{sep}8{sep}4。


这个生成器不受IETF RFC 4122 约束,使用该模式的数字表达式例如8-4-4-4-12。


@Entity(name = "uuidIdentifier")

public static class UUIDIdentifier {

    @GeneratedValue(generator = "uuid")

    @GenericGenerator(name = "uuid", strategy = "uuid")

    @Column(columnDefinition = "CHAR(32)")

    @Id

    private String uuidHex;

}


持久化和合并一个transient 实体:


session.persist(new UUIDIdentifier());

session.flush();

session.merge(new UUIDIdentifier());

session.flush();


对每一个操做生成一个 INSERT 语句:


Query:{[insert into uuidIdentifier (uuidHex) values (?)][2c929c6646f02fda0146f02fdbfa0000]}

Query:{[insert into uuidIdentifier (uuidHex) values (?)][2c929c6646f02fda0146f02fdbfc0001]}


最好检查传给SQL INSERT和select的string参数值。


UUIDGenerator


最新的UUID generator受IETF RFC 4122约束 (variant 2),而且可提供插件式生成策略。注册“uuid2″ 类型,提供更广的类型供选择。


  • java.lang.UUID


  • 一个16字节的数组


  • 一个16进制字符串值


@Entity(name = "uuid2Identifier")

public static class UUID2Identifier {    

    @GeneratedValue(generator = "uuid2")

    @GenericGenerator(name = "uuid2", strategy = "uuid2")

    @Column(columnDefinition = "BINARY(16)")

    @Id

    private UUID uuid; 

}


持久化和合并一个transient 实体的方法使用方式:


session.persist(new UUID2Identifier());

session.flush();

session.merge(new UUID2Identifier());

session.flush();


对每一个操做生成一个 INSERT 语句:


Query:{[insert into uuid2Identifier (uuid) values (?)][[B@68240bb]}

Query:{[insert into uuid2Identifier (uuid) values (?)][[B@577c3bfa]}


因为咱们配置了@Id column定义,这个insert使用字节数组:


具体更多代码可查看 GitHub (https://github.com/vladmihalcea/hibernate-master-class).

相关文章
相关标签/搜索