Hibernate JPA,继承和存储过程返回多个结果集

时间:2014-07-07 17:26:39

标签: hibernate jpa stored-procedures multiple-resultsets

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

1 个答案:

答案 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_ProductTypeIDUUIDgeo_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提供者完成。

希望它对你有所帮助。