我有一个带有composite-id的父对象(遗留数据库 - 不能修改它)。我有一个子对象,它是双向的一对多(父对子)关系。映射是正确的,因为我可以加载任一实体的实例并正确地浏览关系。当我存储父母并将其级联到孩子时,我的问题出现了。 Postgres方言正在发出以下形式的查询:
“插入tablename(column1,column2,column3,column4) 值(value1,value2,value3,value4)返回*“
这是一个很好的postgres快捷方式,用于返回刚刚插入的行的所有值。但是,列以db设置的任意顺序返回,尽管它是包含所有列元数据的标准结果集。然而,postgres似乎假设返回的列处于某种任意顺序。
有问题的表有一个btime和mtime字段,可以通过insert上的触发器进行更新。两者都是时间戳列。这是前两个回来的专栏。我花了一些时间尝试调试hibernate,但这是一个缓慢的过程。我相信发生的事情是第一个时间戳列被假定为生成的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和mtime列设置,该应用程序具有特定于hibernate的模式,该模式在任何地方都使用生成的长ID。因为btime和mtime来自父表,而db中的所有其他表都继承,所以无法更改列的顺序。
最终结果是父插入和级联子插入都成功,但是在无法加载子实体的生成字段后,hibernate会抛出异常。这感觉非常像休眠中的一个bug,它让我感到很冷。我希望有人知道解决方法或修复错误。
我正在使用hibernate-3.3.1-GA作为springource在依赖包中使用最新的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"/>
最后,设置以下存储过程以在插入两个表之前执行,这就是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会发出一个单独的选择 - 我认为它是级联保存与否之间的区别,或者是复合主键和复合外键之间的区别。方言只在子对象上发出“insert ... returns *”语法,无论我先保存父对象,然后在保存子对象之前添加子对象,或者我只是让父级联系给子对象,它都会这样做(反之亦然)。
答案 0 :(得分:1)
我无法找到解决此问题的方法。唯一合理的解决方法是停止使用身份密钥生成并转而使用序列ID生成器。这导致hibernate不尝试使用insert / update语句的'returns'子句。