Hibernate 4.3.5.Final (JPA 2.1) を使用して、ストアド プロシージャから複数の結果セットを使用しようとしていますが、動作させることができませんでした。SQL Server 2008 を使用しています。
ストアド プロシージャの結果セットにはいくつかの共通点を持つ異なる列がありますが、それらを 1 つの結果セットに結合するには十分ではありません。共通性は、継承階層を持つ Java で表現されます。ストアド プロシージャの結果セットに明示的なテーブルが実際には存在しないにもかかわらず、TABLE_PER_CLASS の InheritanceType 戦略を使用してきました。それでも、Hibernate が 1 つの結果セットに対してクラス X1 のオブジェクトをハイドレートし、もう 1 つの結果セットに対して 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 でした
hibernate から生成された DEBUG ステートメントを見ると、@DiscriminatorValue() アノテーションが適切に処理されていないようです。X1 に @DiscriminatorValue(value="1") を指定したのに、Hibernate が頑固に X1 に対して 2 で SQL を生成している (2 as clazz_ from X1) これが問題の原因かもしれませんし、そうではないかもしれません。まだ。
複数の結果セットを返すストアド プロシージャで Hibernate/JPA を使用する方法はありますか?
私は何を間違っていますか?
前もって感謝します!
(誰かが私のテストコードから追加情報を必要とする場合は、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 にマッピングします。これは私が期待したものとはまったく異なります.resultSetは1つずつマップされると思っていました.つまり、最初のresultSetはX1にマップされ、2番目は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 番目の結果セットを処理しようとすると'2' as dc
、ClassCastException が発生します。
java.lang.ClassCastException: com.xyz.search.jpa.X1 は com.xyz.search.jpa.X2 にキャストできません
2 番目の getResultList() から返されたオブジェクトを X2 にケース化しようとしていますが、どうやら休止状態の JPA は、dc='2' を持つ行に対しても X1 をハイドレートしているようです。
ストアド プロシージャの結果セット 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 は単一のストアド プロシージャからの複数の結果セットを適切に処理するように設計されていないことが明らかになります。一般に、特定のストアド プロシージャからの 2 つの結果セットに共通点があるという保証はなく、同じ主キーでさえない可能性があります。ストアド プロシージャから複数の結果セットを生成するという決定は、効率によって決まる場合があります。たとえば、複数の異なる結果セットを生成するために、SQL 側で同じ前処理手順 (一時テーブルの生成など) が必要になる場合があります。
ただし、SQL 行ごとに異なるクラスをインスタンス化するための JPA の唯一の機能は、ディスクリミネーター フィールドであり、ディスクリミネーターは少なくともいくつかの共通性を前提とする継承でのみ機能します。共通の識別子である主キーがない場合、Java クラス階層は機能しません。
また、共通の @Id フィールドを識別できる場合でも、Id フィールドの値が同じである異なる結果セットからの行は、行の残りの部分が完全に異なっていても、既存のオブジェクトにハイドレートされます。その ID を持つオブジェクトがキャッシュに既に存在する場合、Hibernate は明らかに識別子フィールドを無視します。
MappedSuperclass アプローチでさえ、共通の Id フィールドが必要です。また、結果セットは参照可能な名前付きテーブルではないため、サブクラスに Table(name="???") を指定する方法はありません。