我正在尝试使用 Hibernate 4.3.5.Final (JPA 2.1)从存储过程中使用多个结果集——但我无法让它工作。我正在使用 Sql Server 2008。
存储的 proc 结果集有不同的列,有一些共同点,但不足以将它们组合成一个结果集。通用性用 Java 表示,具有继承层次结构。我一直在使用 TABLE_PER_CLASS 的 InheritanceType 策略,即使存储过程结果集中确实没有显式表。尽管如此,我还是需要做一些事情来让 Hibernate 为一个结果集的 X1 类对象和另一个结果集水合 X2 类的对象。
我简化的 Java 层次结构如下:
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(
name="clazz_",
discriminatorType=DiscriminatorType.INTEGER
)
@DiscriminatorValue(value="0")
public class XBase {
@Column(name = "ProductTypeID")
protected Integer productTypeId;
}
和
@Entity
@DiscriminatorValue(value="1")
public class X1 extends XBase {
@Column(name = "UUID")
protected String uuid;
}
和
@Entity
@DiscriminatorValue(value="2")
public class X2 extends XBase {
@Column(name = "geo_id")
private Integer geoId;
}
使用@NamedStoredProcedureQuery,
@NamedStoredProcedureQuery (
name = "XInfoSProc",
resultClasses = {
com.xyz.search.jpa.XBase.class,
com.xyz.search.jpa.X1.class,
com.xyz.search.jpa.X2.class
},
procedureName = "spXInfo",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "XMatchID", type = String.class)
}
)
我构造 StoredProcedureQuery 并执行它,
// Create an EntityManagerFactory for this Persistence Unit
EntityManagerFactory factory = Persistence.createEntityManagerFactory("XPU");
EntityManager em = factory.createEntityManager();
StoredProcedureQuery spq = em.createNamedStoredProcedureQuery("XInfoSProc");
spq.setParameter("XMatchID", "10002916403");
try {
spq.execute();
} catch(Exception ex) {
System.err.println("Exception: " + ex.getMessage());
}
Hibernate 抛出 WrongClassException 异常,
异常:org.hibernate.WrongClassException:对象 [id=512565] 不属于指定的子类 [com.xyz.search.jpa.X2]:加载的对象属于错误的类 com.xyz.search.jpa.X1
查看从休眠生成的 DEBUG 语句,似乎我的 @DiscriminatorValue() 注释没有得到正确处理。即使我为 X1 指定了@DiscriminatorValue(value="1"),hibernate 仍然顽固地使用 2 为 X1 生成 SQL(2 作为 X1 的 clazz_)这可能是问题的原因,也可能不是,我不确定然而。
有没有办法使用 Hibernate / JPA 和存储的 procs 返回多个结果集?
我究竟做错了什么?
提前致谢!
(如果有人需要我的测试代码中的其他信息,请 plmk。:)
附录(已编辑):
根据 zxcf 的建议,我将 @NamedStoredProcedureQuery 修改为:
@NamedStoredProcedureQuery (
name = "XInfoSProc",
resultSetMappings = {
"XInfoSProcMapping1",
"XInfoSProcMapping2",
"XInfoSProcMapping3",
"XInfoSProcMapping7"
},
procedureName = "spXInfo",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "SearchID", type = String.class)
}
)
并添加了一个 SqlResultSetMapping 如下:
@SqlResultSetMappings(
value = {
@SqlResultSetMapping (
name="XInfoSProcMapping1",
entities= {
@EntityResult(entityClass=X1.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID"),
@FieldResult(name="natsId", column="NatsId")
}
)
}
),
@SqlResultSetMapping (
name="XInfoSProcMapping2",
entities= {
@EntityResult(entityClass=X2.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID"),
@FieldResult(name="phoneNumber", column="PhoneNumber")
}
)
}
),
@SqlResultSetMapping (
name="XInfoSProcMapping3",
entities= {
@EntityResult(entityClass=X3.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID")
}
)
}
),
@SqlResultSetMapping (
name="XInfoSProcMapping7",
entities= {
@EntityResult(entityClass=X7.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID"),
@FieldResult(name="geoId", column="geo_id")
}
)
}
)
}
)
通过这种修改,我得到了一些非常奇怪的行为。使用 List x = spq.getResultList() 依次处理每个结果集表明 x 实际上是一个 Object[] ,其中结果集中的每一行都已映射到每个类 - 即结果集 1 的第 1 行有一个映射到 X1、X2、X3 和 X7。这根本不是我所期望的——我认为resultSets 将被一个一个映射,即第一个resultSet 映射到X1,第二个映射到X2,等等,但这不是正在发生的事情。
2014 年 7 月 10 日更新——
在 XBase.java 中,
@SqlResultSetMapping (
name="XInfoSProcMapping",
entities= {
@EntityResult(entityClass=XBase.class,
discriminatorColumn="dc",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID"),
@FieldResult(name="natsId", column="NatsId"),
@FieldResult(name="xUUID", column="XUUID"),
@FieldResult(name="phoneNumber", column="PhoneNumber"),
@FieldResult(name="xAddress1", column="XAddress1"),
@FieldResult(name="couponURL", column="CouponURL"),
@FieldResult(name="geoId", column="geo_id"),
}
)
}
)
@NamedStoredProcedureQuery (
name = "XInfoSProc",
resultSetMappings = {
"XInfoSProcMapping"
},
procedureName = "spXInfo",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "XMatchID", type = String.class)
}
)
@Entity
@Inheritance(strategy=javax.persistence.InheritanceType.SINGLE_TABLE)
public abstract class XBase {
@Id protected Long id;
}
在 X1.java 中,
@Entity
@DiscriminatorValue(value="1")
public class X1 extends XBase {
/* ... */
}
在 X2.java 中,
@Entity
@DiscriminatorValue(value="2")
public class X2 extends XBase {
/* ... */
}
第一个结果集('1' as dc
适用于所有行)已正确处理,但是当尝试处理'2' as dc
适用于所有行的第二个结果集时,我得到了 ClassCastException。
java.lang.ClassCastException:com.xyz.search.jpa.X1 无法转换为 com.xyz.search.jpa.X2
我正在尝试将从第二个 getResultList() 返回的对象设置为 X2,但显然休眠 JPA 正在为 X1 补水,即使对于具有 dc='2' 的行也是如此 - 显然没有注意鉴别器列来确定要实例化的内容。
存储过程结果集1:
XID XTypeID XProductTypeID XUUID NatsID XPriority dc
512565 2 2001 AD6AB5A8-3A75-449D-8742-76C2425BA164 1809025090 10 1
存储过程结果集2:
XID XTypeID Name PhoneNumber dc
512565 2 ABC DEF 8152597378 2
上面的 sp 结果具有代表性 - 为了清楚起见,我删除了许多其他列。还有 5 个额外的结果集,每个都有不同的列集和不同的 dc 值:1,2,3,4,5,6,7
一些(可能是最终的)想法:
我越深入,就越清楚 Hibernate 4.3.5 Final 的设计目的是不能充分处理来自单个存储过程的多个结果集。一般来说,不能保证来自给定存储过程的两个结果集会有任何共同点,甚至可能没有相同的主键。从存储过程生成多个结果集的决定可能是由效率驱动的——例如,在 SQL 端可能需要相同的预处理步骤(例如,临时表生成)来生成几个不同的结果集。
然而,JPA 中为每个 SQL 行实例化不同类的唯一工具是鉴别器字段,鉴别器仅适用于继承,这至少预设了一些共性。如果没有公共标识符、主键,那么 Java 类层次结构就无法工作。
而且,即使可以识别一个公共的@Id 字段,来自不同结果集中的具有相同 Id 字段值的行也将被合并到现有对象中,即使该行的其余部分完全不同。如果缓存中已经存在具有该 ID 的对象,Hibernate 显然会忽略鉴别器字段。
即使是 MappedSuperclass 方法也需要一个公共的 Id 字段。而且,此外,无法为子类指定 Table(name="???"),因为结果集不是可以引用的命名表。