Hibernate 本来是独立于数据库的 ORM 解决方案,但是在迁移到另一个数据库供应商时,一些关键问题出现了。其中之一是底层数据库的自动 ID 生成。 MySQL、Oracle 和 MS SQL Server 都使用不同的技术来生成主键的自动 ID。因此,当我们开始迁移时,我们会遇到很多问题,需要额外的工作。
在 Hibernate 3.2.3 之前的版本中,Hibernate 没有合适的解决方案,但是在 3.2.3 版本中,Hibernate 开发人员可以提供这种适用于任何数据库的便携式 ID 生成器。两者如下,
- org.hibernate.id.enhanced.SequenceStyleGenerator
“实现可移植性的方法是,您实际上并不关心您是否在物理上使用数据库中的 SEQUENCE;实际上,您只需要类似序列的值生成。在支持 SEQUENCES 的数据库上,SequenceStyleGenerator 实际上会使用 SEQUNCE 作为值生成器;对于那些不支持 SEQUENCES 的数据库,它将改为使用单行表作为值生成器,但具有与 SEQUENCE 值生成器完全相同的特性(即它始终在单独的事务中处理序列表) ”。
- org.hibernate.id.enhanced.TableGenerator
虽然不是专门针对可移植性,但 TableGenerator 当然可以在所有数据库中使用。它使用多行表,其中行由(可配置的)sequence_name 列作为键;一种方法是让每个实体在表中定义一个唯一的 sequence_name 值来分割其标识符值。它从旧的 org.hibernate.id.MultipleHiLoPerTableGenerator 发展而来,并使用基本相同的表结构。然而,虽然 MultipleHiLoPerTableGenerator 固有地将 hi-lo 算法应用于值生成,但添加了这个新的 TableGenerator 以便能够利用可插入优化器。
Example Entity which usage, Hibernate's Sequences across all databases。
@Entity
@Table(name = "author")
public class Author implements java.io.Serializable {
// Fields
private Integer id;
private String name;
private Date birthDate;
private Date deathDate;
private String bio;
private String wikiUrl;
private String imagePath;
private Boolean isFeatured;
private Long totalContent;
private Set<Content> contents = new HashSet<Content>(0);
// Constructors
/** default constructor */
public Author() {
}
// Property accessors
@Id
@GeneratedValue(generator = "Author_SequenceStyleGenerator")
@GenericGenerator(name = "Author_SequenceStyleGenerator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
@Parameter(name = "sequence_name", value = "Author_SEQ"),
@Parameter(name = "optimizer", value = "hilo"),
@Parameter(name = "initial_value", value = "1"),
@Parameter(name = "increment_size", value = "1") }
)
@Column(name = "id", unique = true, nullable = false, length = 11)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "name", length = 50)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Temporal(TemporalType.DATE)
@Column(name = "birth_date", length = 10)
public Date getBirthDate() {
return this.birthDate;
}
public void setBirthDate(Date birthDate) {
this.birthDate = birthDate;
}
@Temporal(TemporalType.DATE)
@Column(name = "death_date", length = 10)
public Date getDeathDate() {
return this.deathDate;
}
public void setDeathDate(Date deathDate) {
this.deathDate = deathDate;
}
@Column(name = "bio", length = 65535)
public String getBio() {
return this.bio;
}
public void setBio(String bio) {
this.bio = bio;
}
@Column(name = "wiki_url", length = 128)
public String getWikiUrl() {
return this.wikiUrl;
}
public void setWikiUrl(String wikiUrl) {
this.wikiUrl = wikiUrl;
}
@Column(name = "image_path", length = 50)
public String getImagePath() {
return this.imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
@Column(name = "is_featured")
public Boolean getIsFeatured() {
return this.isFeatured;
}
public void setIsFeatured(Boolean isFeatured) {
this.isFeatured = isFeatured;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "author")
public Set<Content> getContents() {
return this.contents;
}
public void setContents(Set<Content> contents) {
this.contents = contents;
}
@Transient
public Long getTotalContent() {
return totalContent;
}
public void setTotalContent(Long totalContent) {
this.totalContent = totalContent;
}
}
}