2010-02-22 33 views
0

我有一个父对象与复合ID(遗传数据库 - 无法修改此)。我有一个双向一对多(父母与子女)关系的子对象。映射是正确的,因为我可以加载任一实体的实例并正确导航关系。当我存储父级并且级联到孩子时,我的问题就出现了。 Postgres的方言发放形式的查询:复合ID在休眠+ postgres休息由于返回的列顺序

“插入到表名(列1,列2,栏3,column4) 值(值1,值2,值3,值4)返回*”

这是一个很好postgres快捷方式返回刚刚插入的行的所有值。但是,列以数据库设置的任意顺序返回,尽管它是包含所有列元数据的标准结果集。但是,postgres似乎假设列回来的是一些任意的顺序。

问题表中有一个btime和mtime字段,它们通过插入时的触发器进行更新。两者都是时间戳列。那些是回来的前两列。我花了一段时间试图调试休眠,但这是一个缓慢的过程。我相信现在发生的情况是,第一个时间戳列被假定为生成的id列,并且它在尝试将时间戳字符串转换为Long时失败。实际上,生成的id显示为第4列。

Caused by: org.postgresql.util.PSQLException: Bad value for type long : 2010-02-21 18:11:19.774362 
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.toLong(AbstractJdbc2ResultSet.java:2796) 
at org.postgresql.jdbc2.AbstractJdbc2ResultSet.getLong(AbstractJdbc2ResultSet.java:2019) 
at org.hibernate.id.IdentifierGeneratorFactory.get(IdentifierGeneratorFactory.java:104) 
at org.hibernate.id.IdentifierGeneratorFactory.getGeneratedIdentity(IdentifierGeneratorFactory.java:92) 
at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:98) 
at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:57) 

我认为,这在某种程度上与使用复合键,因为我已经使用在另一个应用程序BTIME和修改时间列的几乎相同的设置,它具有使用无处不在长期产生一个IDS对Hibernate架构。因为btime和mtime来自db中所有其他表继承的父表,所以无法更改列的顺序。最后的结果是父插入和级联子插入都成功了,但是当hibernate未能为子实体加载生成的字段后,它会抛出异常。这感觉非常像冬眠中的一个错误,而且它阻止了我的冷静。我希望有人知道解决方法或错误修复。

我使用的Hibernate 3.3.1-GA通过SpringSource的的包装中最新的Spring依赖3.0.1版本

CREATE TABLE parent_stat (
     btime timestamp DEFAULT NOW() NOT NULL, -- Birth Time or Creation Time 
     mtime timestamp DEFAULT NOW() NOT NULL, -- Modified Time 
     enabled boolean DEFAULT true NOT NULL 
); 

CREATE TABLE portal.parent_persistent (
    version int DEFAULT 1  NOT NULL -- Version Number 
); 


CREATE TABLE portal.customers 
(
    customer_id int NOT NULL, 
    zone_id int NOT NULL, 
    <other properties go here> 
    CONSTRAINT pk_customers PRIMARY KEY (zone_id, customer_id) 
) INHERITS (portal.parent_stat, portal.parent_persistent); 

CREATE TABLE portal.users 
(
    user_id bigserial NOT NULL, 
    customer_zone_id int NOT NULL, 
    customer_id int NOT NULL, 
    <more properties here> 
    CONSTRAINT pk_users_user_id PRIMARY KEY (user_id), 
    CONSTRAINT fk_users_customers FOREIGN KEY (customer_zone_id, customer_id) REFERENCES customers(zone_id, customer_id) ON UPDATE RESTRICT ON DELETE RESTRICT 
) INHERITS (portal.parent_stat, portal.parent_persistent); 

<hibernate-mapping> 
    <class name="CustomerImpl" proxy="Customer" schema="portal" table="customers"> 
     <composite-id name="key" class="CustomerKeyImpl"> 
      <key-property name="zoneId" type="int" column="zone_id"/> 
      <key-property name="customerId" type="int" column="customer_id"/> 
     </composite-id> 
     &version; 
     &auditable; 
     <set name="users" lazy="true" inverse="true" order-by="lower(email) asc" cascade="save-update,delete"> 
      <key> 
       <column name="customer_zone_id"/> 
       <column name="customer_id"/> 
      </key> 
      <one-to-many class="UserImpl"/> 
     </set> 
    </class> 
</hibernate-mapping> 

<hibernate-mapping default-lazy="true"> 
    <class name="UserImpl" proxy="User" schema="portal" table="users"> 
     <id name="id" type="java.lang.Long" column="user_id"> 
      <generator class="identity"/> 
     </id> 
     &version; 
     &auditable; 
     <many-to-one name="customer" class="CustomerImpl" not-null="true" cascade="save-update"> 
      <column name="customer_zone_id"/> 
      <column name="customer_id"/> 
     </many-to-one> 
    </class> 
</hibernate-mapping> 

版本实体和审计实体分布的定义如下:

<version name="version" column="version" unsaved-value="null" type="java.lang.Long"/> 

<property name="created" type="java.util.Calendar" column="btime" generated="insert" insert="false" update="false"/> 
<property name="modified" type="java.util.Calendar" column="mtime" generated="always" insert="false" update="false"/> 

最后,下面的存储PR ocedure被设置为在插入到两个表之前执行,这是mtime如何更新的。

CREATE OR REPLACE FUNCTION touchrow() RETURNS TRIGGER AS $$ 
DECLARE 
mtime timestamp NOT NULL DEFAULT NOW(); 
BEGIN 

NEW.mtime := mtime; 

RAISE DEBUG 'mtime=%', NEW.mtime; 

RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 

所有这些功能,包括父表和用于更新mtime的存储过程都已用于其他应用程序。唯一的区别是父对象的组合键。

注意:我可以毫无困难地存储没有引用孩子的父对象。如果我看看我的sql日志,我可以看到hibernate在执行插入操作之后发出单独的选择 - 我认为这是级联保存与否之间的区别,或者复合主键与复合外键之间的区别。方言只发出“插入...返回*”语法的子对象上,它也不管我先救母,然后保存孩子之前添加的孩子,或者,如果我只是让家长级联到子(反之亦然)。

+0

我有一点了解更多信息。我还不能完全确定如何“回*”比特被附加到查询。通过商店()方法执行的时候,已经创建了准备好的声明,而且我还没有弄清楚准备好的报表在哪里建成。但是,我已发现,问题的根源在于,IdentityGenerator假定结果集将只包含该标识符,或者它至少可以在第一列。在随后的列 – ideasculptor 2010-02-22 04:17:56

+0

更多的代码在executeAndExtract(PreparedStatement的插入)以下被称为: IdentifierGeneratorFactory.getGeneratedIdentity(RS,persister.getIdentifierType()); 它试图迫使结果集的类型的第一列(一龙,在我的情况)。它确实需要从persister中取出名称和类型,然后按名称而不是位置来获取标识列。编写代码足够简单,但我不知道如何在代码库中将其注入到类路径操作之外。 – ideasculptor 2010-02-22 04:20:24

+1

请修改你的问题,不要使用评论。 – 2010-02-22 10:04:18

回答

1

我从来没有能够找出一个修复这个问题。唯一合理的解决方法是停止使用身份密钥生成和去序列号生成器来代替。这导致休眠不会尝试使用插入的“返回”条款/更新语句。