3

我正在尝试使用 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="???"),因为结果集不是可以引用的命名表。

4

1 回答 1

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和。UUIDgeo_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 提供者完成。

希望它可以帮助你。

于 2014-07-07T18:30:39.490 回答