我正在尝试使用Hibernate 4.3.5.Final(JPA 2.1)从存储过程中使用多个结果集 - 我无法获得它工作。我正在使用Sql Server 2008。
存储的proc结果集具有不同的列,但具有一些共性但不足以将它们组合成单个结果集。通用性在Java中用继承层次结构表示。我一直在使用TABLE_PER_CLASS的InheritanceType策略,即使存储过程结果集中确实没有显式表。尽管如此,我还是需要做些什么来让Hibernate为一个结果集和一个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:Object [id = 512565]不是指定的子类[com.xyz.search.jpa.X2]:加载的对象是类com.xyz.search.jpa的错误。 X1
查看从hibernate生成的DEBUG语句,似乎我的@DiscriminatorValue()注释没有得到妥善处理。即使我为X1指定了@DiscriminatorValue(value =" 1"),hibernate也顽固地为X1生成了2的SQL(2作为来自X1的clazz_)这可能是问题的原因,或者可能不是,我还不确定。
有没有办法使用Hibernate / JPA和存储过程返回多个结果集?
我做错了什么?
提前致谢!
(如果有人需要我的测试代码中的其他信息,请输入:。)
附录(编辑):
根据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上,但显然hibernate JPA正在为X1s提供保湿,即使对于具有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字段。而且,此外,没有办法为子类指定表(name =" ???"),因为结果集不是可以引用的命名表。
答案 0 :(得分:2)
Result-classes
将起作用。
我会将您的@NamedStoredProcedureQuery
更改为包含resultSetMappings
@NamedStoredProcedureQuery (
name = "XInfoSProc",
resultSetMappings = {
"XInfoSProcMapping"
},
procedureName = "spXInfo",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "XMatchID", type = String.class)
}
)
并添加SqlResultSetMapping
定义
@SqlResultSetMapping(
name="XInfoSProcMapping",
entities=
@EntityResult(
entityClass=XBase.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="productTypeId", column="ProductTypeID"),
@FieldResult(name="uuid", column="UUID"),
@FieldResult(name="geoId", column="geo_id")
}
)
)
如您所见,我假设您的程序至少返回四列clazz_
,ProductTypeID
,UUID
和geo_id
。
<强>更新强>
我觉得你误解了我。我仍然不知道你的存储过程返回什么,但是在一行中返回多个实例并不常见。
如果您声明
resultClasses = {
com.xyz.search.jpa.XBase.class,
com.xyz.search.jpa.X1.class,
com.xyz.search.jpa.X2.class
}
然后你要告诉JPA每个行包含三个类实例,你让它自己映射。
如果您声明
resultSetMappings = {
"XInfoSProcMapping1",
"XInfoSProcMapping2",
"XInfoSProcMapping3",
"XInfoSProcMapping7"
}
然后你要告诉JPA每个行至少包含由这些映射映射的四个“东西”。
在我看来,你应该声明单个resultSetMapping,让它命名为XInfoSProcMapping
。因此,NamedStoredProcedureQuery
应如下所示:
@NamedStoredProcedureQuery (
name = "XInfoSProc",
resultSetMappings = {
"XInfoSProcMapping"
},
procedureName = "spXInfo",
parameters = {
@StoredProcedureParameter(mode = ParameterMode.IN, name = "SearchID", type = String.class)
}
)
SqlResultSetMapping
应如下所示:
@SqlResultSetMapping (
name="XInfoSProcMapping1",
entities= {
@EntityResult(entityClass=XBase.class,
discriminatorColumn="clazz_",
fields={
@FieldResult(name="id", column="XID"),
@FieldResult(name="typeId", column="XTypeID"),
@FieldResult(name="productTypeId", column="XProductTypeID"),
@FieldResult(name="natsId", column="NatsId"),
@FieldResult(name="phoneNumber", column="PhoneNumber"),
@FieldResult(name="geoId", column="geo_id")
}
)
}
)
重要的是EntityResult->fields
列表应该适合存储过程查询返回的所有列。继承和具体对象的即时化将由JPA提供者完成。
希望它对你有所帮助。