Java 覆盖Hibernate注释
我正在开发一个使用Hibernate并连接到Oracle实例的Java应用程序。另一个客户端希望使用相同的应用程序,但要求它在MS SQL Server上运行。我希望避免对现有注释进行更改,而是创建一个xml文件包,根据环境的不同,我们可以访问这些文件包 一种方法是使用JPA XML配置覆盖现有的类注释。然而,JPA不支持通用生成器,这是由于我们遗留数据库的结构所要求的。我正在研究的另一种方法是使用HibernateXMLConfigs重新映射整个类,并访问Java 覆盖Hibernate注释,java,hibernate,jpa,Java,Hibernate,Jpa,我正在开发一个使用Hibernate并连接到Oracle实例的Java应用程序。另一个客户端希望使用相同的应用程序,但要求它在MS SQL Server上运行。我希望避免对现有注释进行更改,而是创建一个xml文件包,根据环境的不同,我们可以访问这些文件包 一种方法是使用JPA XML配置覆盖现有的类注释。然而,JPA不支持通用生成器,这是由于我们遗留数据库的结构所要求的。我正在研究的另一种方法是使用HibernateXMLConfigs重新映射整个类,并访问generatorXML标记。不过,此
generator
XML标记。不过,此解决方案存在一些问题:
- Hibernate不允许您有选择地覆盖实体成员
- Hibernate不允许您重新映射同一类(例如,
)org.Hibernate.AnnotationException:两次使用同一实体名称
@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.SEQUENCE)
@SequenceGenerator(name="EXAMPLE_ID_GEN", sequenceName="SEQ_EXAMPLE_ID")
@Column(name = "EXAMPLE_ID")
public String getExampleId() {
return this.exampleId;
}
但是,MS SQL Server没有序列的概念(意识形态差异)。因此,可以使用表生成器来模拟序列
@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN", strategy=GenerationType.TABLE)
@TableGenerator(name="EXAMPLE_ID_GEN", tableName="SEQUENCE", valueColumnName="VALUE", pkColumnName="SEQUENCE", pkColumnValue="EXAMPLE_ID")
public String getExampleId() {
return this.exampleId;
}
针对两种不同类型的数据库的两种不同配置。请记住,这是一个遗留数据库,我们不会重写应用程序以支持SQL Server标识,即SQL Server的本机id生成器(这也需要不同的注释)
为了缓解这种情况,我研究了使用Hibernate的@GenericGenerator
,并将其指向我自己创建的一个类,该类对org.Hibernate.id.SequenceGenerator(或类似的东西)建模,还通过扩展org.Hibernate.id.TableStructure
来定制表的结构
回到我最初的问题——使用XML覆盖是否有可能做到这一点
我是如何解决这个问题的
所以,最后,我发现JPA和Hibernate并没有提供我想要的现成功能。相反,我创建了一个自定义生成器,用于检查数据库方言并适当地设置表结构。在探索所有选项时,我最终使用了Hibernate的@GenericGenerator
注释。这是Id生成注释的一个示例:
@Id
@GeneratedValue(generator="EXAMPLE_ID_GEN")
@GenericGenerator(name = "EXAMPLE_ID_GEN", strategy="com.my.package.CustomIdGenerator", parameters = {
@Parameter(name = "parameter_name", value="parameter_value")
})
public String getExampleId() {
return this.exampleId;
}
此解决方案需要使用新的Id生成器修改每个Hibernate实体。我认为,如果您的注释是特定于数据库的,那么您就错了。我认为,如果您在配置
会话工厂时不使用注释配置
,注释将被忽略
因此,使用配置
解决您的生成器问题(解决方案通常是“使用本机生成器”,但由于使用遗留数据库而不适用于您),您可能会扩展SQLServer方言并覆盖getNativeIdentifierGeneratorClass以返回一个(可能是自定义的)生成器,它可以满足您的遗留数据库的需要。我以前在Grails(GORM)应用程序中遇到过将遗留数据库与新模式/数据库混合匹配的需要,该应用程序当然在下面运行Hibernate 3
我不会说“你做错了”——但我会将JPA@注释保留在@Entity和@Column之类的基础上,并将其留给Hibernate方言,这也是在XML配置文件中指定的
您可以尝试将Oracle10gDialogue子类化为一个为所有表分配序列生成器的子类,而Sybase子类则没有
关于如何实现这一点,请参见本文
更新:
james和我建议(几乎在同一分钟内)在persistence.xml文件中设置多个持久性单元部分
这允许使用@Entity和@Id,而无需在类中提供详细信息。详细信息在hibernate.dialogue
属性中。我建议将Oracle10gDialogue(以及SQLServerDialogue的james)子类化——这些子类可以选择表命名、id生成器策略等
请参阅-->如果在HBM XML文件中重写注释,则可以维护两组这样的XML,并通过Hibernate的映射指令选择要使用的XML。我在Hibernate Core中做过,但在J2EE/JPA环境中没有,所以我不知道在这方面是否存在任何缺陷
最大的缺点是删除所有注释并用XML重建它们可能需要大量工作。在我的例子中:
机架和插槽是具有自定义ID生成器的实体。我使用单向一对一映射。维度表将保存具有自动生成的自定义ID的数据,作为多个表的外键(例如此处的机架和插槽)。
我的想法是这样的:在现实世界中,Rack------->Dimension使用“遗留”和“现有应用程序”这两个术语,说“你做错了”是没有帮助的。Oracle和SQL Server在数据库设计上存在理念上的差异,为了弥合两者之间的差距,需要在配置(即注释)上有所不同@rynmrtn:我想他想指出的是,JPA应该已经独立于数据库了。。。数据存储已经可以通过persistence.xml进行配置。他的回答特别引用了特定于数据库的注释,而不是persistence.xml。这仍然是一个没有答案的答案。我支持ORM的这个答案。也就是说,我将尝试在另一个答案中给出一些有针对性的建议。如果您指出需要处理注释中的哪些差异,可能会有所帮助
Rack rack = new Rack(params);
Dimension dim = new Dimension(params);
rack.setDimension(dim);
session.save(rack);
Slot Slot = new Slot(params);
Dimension dim = new Dimension(params);
slot.setDimension(dim);
session.save(slot);
attempted to assign id from null one-to-one property: rack
@GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
@Parameter(name = "property", value = "slot"),
@Parameter(name = "property", value = "rack")})
@Entity
@Table(name="tablename")
@GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
@Id
@GeneratedValue(generator = "customseq")
@Column(name = "uni_id")
private String id;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private Dimension dimension;
// Getters and Setters
}
@Entity
@Table(name="tablename")
@GenericGenerator(name = "customseq", strategy = "CustomIdGenerator")
public class Rack {
@Id
@GeneratedValue(generator = "customseq")
@Column(name = "uni_id")
private String id;
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
private Dimension dimension;
// Getters and Setters
}
public class Dimension implements Serializable{
@Id
@Column(name = "systemid")
@GeneratedValue(generator = "foreign")
@GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
@Parameter(name = "property", value = "slot"),
@Parameter(name = "property", value = "rack")})
private String systemid;
@OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private Rack rack;
@OneToOne(mappedBy = "dimension", fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private Slot slot;
// Getters and Setters
}