Я пытаюсь использовать несколько наборов результатов из хранимой процедуры с помощью Hibernate 4.3.5.Final (JPA 2.1) - и мне не удалось заставить его работать . Я использую Sql Server 2008.
Сохраненные наборы результатов процедуры имеют разные столбцы с некоторой общностью, но недостаточной для объединения их в один набор результатов. Общность выражается в Java с помощью иерархии наследования. Я использовал стратегию InheritanceType для TABLE_PER_CLASS, хотя на самом деле в наборах результатов хранимых процедур нет явных таблиц. Тем не менее, мне нужно что-то сделать, чтобы 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 () не обрабатываются должным образом. Несмотря на то, что я указал @DiscriminatorValue (value = "1") для X1, спящий режим упорно генерирует SQL с 2 для X1 (2 как clazz_ из 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. Это совсем не все, что я ожидал - я думал, что resultSets будет отображаться один за другим, т.е. первый resultSet будет отображаться в X1, 2nd в X2 и т. Д., Но это не то, что происходит.
Обновление 10.07.2014 -
В 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, будут гидратированы в существующий объект, даже если остальная часть строки полностью отличается. Hibernate, очевидно, игнорирует поле дискриминатора, если в кеше уже есть объект с этим идентификатором.
Даже подход MappedSuperclass требует общего поля Id. И, кроме того, нет способа указать таблицу (name = "???") для подклассов, потому что набор результатов не является именованной таблицей, на которую может быть сделана ссылка.
@DiscriminatorColumn
определение вXBase
классе? попробуйте изменить его на String, я только что сделал аналогичный проект здесь, и он работает как шарм. Или, может быть, одна из ваших процедур возвращает в результатах не только один класс, а вы получаете их как список других подклассов? Взгляните сюда: java2s.com/