原文:JPA implementation patterns: Using UUIDs as primary keys
作者:Albert Sikkema
出处:http://blog.xebia.com/2009/06/03/jpa-implementation-patterns-using-uuids-as-primary-keys/
作为Vincent Partington的关于JPA实施模式的博客序列的继续,我想补充以下内容。
JPA缺省的主键方式是使用带有strategy属性的@GenerateValue注解来把主键策略设置为AUTO、IDENTITY、SEQUENCE或者TABLE中的一个,你结合自己的具体情况来挑选最适合的策略,仅此而已。
不过也可以选择由你自己来生成主键。
使用UUID做主键是理想的,而且有一些很大的好处。在我们当前的项目中,我们通过创建一个抽象的基类来使用这一策略,其负责处理主键,我们的实体会继承这一基类。
@MappedSuperclass
public abstract class AbstractBaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String id;
public AbstractBaseEntity() {
this.id = UUID.randomUUID().toString();
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof AbstractBaseEntity)) {
return false;
}
AbstractBaseEntity other = (AbstractBaseEntity) obj;
return getId().equals(other.getId());
}
}
总的来说使用UUID和序列(sequence)的比较在网络上已经被广泛地讨论,因此在这里我不会说得过于详细,尽管如此,在这里还是给出一些优缺点:
优点
一旦编写了这样的一个基类之后,每个实体就都能够无偿地获得一个Id,如果像前面的例子那样实现了equals和hashCode方法的话,你还可以把这作为额外的收获添加进来。
UUID是全球唯一(Universal Unique,这是UUID这一名称的含义)的,这意味着如果你需要无需重新生成键而拷贝/合并位置a到位置b的记录的话,那么你拥有着极大的灵活性。
UUID是不可推测的,这意味着把它们暴露到允许告知URL的那些地方是可以变得更加安全的,不过这是否是一种好的做法则属另一回事。
缺点
性能可能会是个问题,参见http://johannburkard.de/blog/programming/java/Java-UUID-generators-compared.html,一些数据库在使用UUID(至少在它们被存储成串时)进行索引时表现欠佳。
排序、分类,这方面不言自明。
JPA特有的:不能通过检查Id域是否被设定来测试记录是否已被持久。可能有人会质疑是否总是会需要这样的检查。
结论
UUID易于使用,不过,如果开放JPA规范,把UUID作为一种策略包括进来不是很好吗?有些JPA的实现,比如Hibernate,就已经支持了这样的做法:
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid",
strategy = "uuid")