Thread: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
I have no idea where to point the blame on this one.  I have a
workaround, but here's the scenario.

I have a timestamp column which I want to use in JBoss/Hibernate as a
"version" column so that Hibernate can do opportunistic locking for
updates.  The timestamp is actually generated by a trigger on the
table.  Here's an edited down copy of the DDL for the table and trigger:

  CREATE TABLE security (
    id                        SERIAL,
    primary_identifier        VARCHAR(10) NOT NULL,
    ...
    active_on                 TIMESTAMP(3),
  );

  DROP FUNCTION security_biur_trg() CASCADE;

  CREATE FUNCTION security_biur_trg() RETURNS TRIGGER AS $$
      BEGIN
          NEW.active_on := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
          RETURN NEW;
      END;
  $$ LANGUAGE plpgsql;

  CREATE TRIGGER security_biur BEFORE INSERT OR UPDATE
      ON security FOR EACH ROW
      EXECUTE PROCEDURE security_biur_trg();

I have a custom UserVersionType for Hibernate to allow it to treat the
column as a version object.  You can see the full post of my code at
http://www.hibernate.org/461.html.  The code does work with one caveat
that I will come to shortly.

The Hibernate mapping file for this table is
  <?xml version="1.0"?>
  <!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

  <hibernate-mapping>
    <class name="Security">
      <id name="securityId" column="id" type="long" unsaved-value="null">
        <generator class="sequence">
          <param name="sequence">security_id_seq</param>
        </generator>
      </id>
      <natural-id>
        <property name="primaryIdentifier"
                  column="primary_identifier"
                  type="string"/>
      </natural-id>
      <version name="activeOn"
               column="active_on"
               type="HibernateUTC$TimestampType"
               generated="always"
               insert="false"
               unsaved-value="null"/>
      ...
    </class>
  </hibernate-mapping>

What happens when I actually try to update a row is described in more
detail here, http://forum.hibernate.org/viewtopic.php?p=2409286#2409286,
but the short answer is that Hibernate thinks the row has been updated
by another transaction.  After trying all sorts of different mapping
definitions for the active_on column (including letting Hibernate
generate the values, which sort-of works), I finally hit upon a simple
column definition change that works around the problem:

  active_on timestamp(3)

Yes, restrict the timestamp to millisecond precision.  Somewhere the
sub-millisecond parts are getting lost.  I have no idea if it is in the
JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?

Oh, the SQL generated by Hibernate puts both the primary key and the
active_on column in the "where" clause.  This is what it is supposed to
do and how it detects a row as having been updated by another
transaction.  In this case, the precision mismatch fools it.

roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


You should not use a trigger to generate the version info for hibernate. You should use the application's time.

Dave


On Fri, Mar 20, 2009 at 12:36 PM, Roland Roberts <roland@astrofoto.org> wrote:
I have no idea where to point the blame on this one.  I have a workaround, but here's the scenario.

I have a timestamp column which I want to use in JBoss/Hibernate as a "version" column so that Hibernate can do opportunistic locking for updates.  The timestamp is actually generated by a trigger on the table.  Here's an edited down copy of the DDL for the table and trigger:

 CREATE TABLE security (
  id                        SERIAL,
  primary_identifier        VARCHAR(10) NOT NULL,
  ...
  active_on                 TIMESTAMP(3),
 );

 DROP FUNCTION security_biur_trg() CASCADE;

 CREATE FUNCTION security_biur_trg() RETURNS TRIGGER AS $$
    BEGIN
        NEW.active_on := CURRENT_TIMESTAMP AT TIME ZONE 'UTC';
        RETURN NEW;
    END;
 $$ LANGUAGE plpgsql;

 CREATE TRIGGER security_biur BEFORE INSERT OR UPDATE
    ON security FOR EACH ROW
    EXECUTE PROCEDURE security_biur_trg();

I have a custom UserVersionType for Hibernate to allow it to treat the column as a version object.  You can see the full post of my code at http://www.hibernate.org/461.html.  The code does work with one caveat that I will come to shortly.

The Hibernate mapping file for this table is
 <?xml version="1.0"?>
 <!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

 <hibernate-mapping>
  <class name="Security">
    <id name="securityId" column="id" type="long" unsaved-value="null">
      <generator class="sequence">
        <param name="sequence">security_id_seq</param>
      </generator>
    </id>
    <natural-id>
      <property name="primaryIdentifier"
                column="primary_identifier"
                type="string"/>
    </natural-id>
    <version name="activeOn"
             column="active_on"
             type="HibernateUTC$TimestampType"
             generated="always"
             insert="false"
             unsaved-value="null"/>
    ...
  </class>
 </hibernate-mapping>

What happens when I actually try to update a row is described in more detail here, http://forum.hibernate.org/viewtopic.php?p=2409286#2409286, but the short answer is that Hibernate thinks the row has been updated by another transaction.  After trying all sorts of different mapping definitions for the active_on column (including letting Hibernate generate the values, which sort-of works), I finally hit upon a simple column definition change that works around the problem:

 active_on timestamp(3)

Yes, restrict the timestamp to millisecond precision.  Somewhere the sub-millisecond parts are getting lost.  I have no idea if it is in the JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?

Oh, the SQL generated by Hibernate puts both the primary key and the active_on column in the "where" clause.  This is what it is supposed to do and how it detects a row as having been updated by another transaction.  In this case, the precision mismatch fools it.

roland

--
                      PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


--
Sent via pgsql-jdbc mailing list (pgsql-jdbc@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-jdbc

Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
Dave Cramer wrote:
> You should not use a trigger to generate the version info for
> hibernate. You should use the application's time.
Then why bother having the generated atribute to version?  That flies in
the face of the design of *Hibernate* in allowing this.  Hibernate
*does* do the right thing in querying the column to get the value back
after the update.  You're telling me to not do what Hibernate explicitly
say I can do.  Why?

roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Oliver Jowett
Date:
Roland Roberts wrote:

> Yes, restrict the timestamp to millisecond precision.  Somewhere the
> sub-millisecond parts are getting lost.  I have no idea if it is in the
> JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?

Is the server built with integer_datetimes? Maybe you are just hitting a
rounding problem.

-O

Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
Oliver Jowett wrote:
> Roland Roberts wrote:
>
>> Yes, restrict the timestamp to millisecond precision.  Somewhere the
>> sub-millisecond parts are getting lost.  I have no idea if it is in the
>> JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?
>>
> Is the server built with integer_datetimes? Maybe you are just hitting a
> rounding problem.
>
Which server; do you mean PostgreSQL or JBoss?  I think you mean JBoss,
but I'm not sure how to check on that.  If you know, please share.  I'll
see what I can find out.

roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Oliver Jowett
Date:
Roland Roberts wrote:
> Oliver Jowett wrote:
>> Roland Roberts wrote:
>>
>>> Yes, restrict the timestamp to millisecond precision.  Somewhere the
>>> sub-millisecond parts are getting lost.  I have no idea if it is in the
>>> JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?
>>>
>> Is the server built with integer_datetimes? Maybe you are just hitting a
>> rounding problem.
>>
> Which server; do you mean PostgreSQL or JBoss?  I think you mean JBoss,
> but I'm not sure how to check on that.  If you know, please share.  I'll
> see what I can find out.

No, I mean the PostgreSQL server.

See http://www.postgresql.org/docs/8.3/static/datatype-datetime.html,
specifically this bit:

    Note:  When timestamp values are stored as double precision
floating-point numbers (currently the default), the effective limit of
precision might be less than 6. timestamp values are stored as seconds
before or after midnight 2000-01-01. Microsecond precision is achieved
for dates within a few years of 2000-01-01, but the precision degrades
for dates further away. When timestamp values are stored as eight-byte
integers (a compile-time option), microsecond precision is available
over the full range of values. However eight-byte integer timestamps
have a more limited range of dates than shown above: from 4713 BC up to
294276 AD. The same compile-time option also determines whether time and
interval values are stored as floating-point or eight-byte integers. In
the floating-point case, large interval values degrade in precision as
the size of the interval increases.

Try "SHOW integer_datetimes;" via psql to see how your server is configured.

-O


Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
Oliver Jowett wrote:
> Roland Roberts wrote:
>
>> Oliver Jowett wrote:
>>
>>> Roland Roberts wrote:
>>>
>>>
>>>> Yes, restrict the timestamp to millisecond precision.  Somewhere the
>>>> sub-millisecond parts are getting lost.  I have no idea if it is in the
>>>> JDBC layer or somewhere in Hibernate.  Any clues on figuring this out?
>>>>
>>> Is the server built with integer_datetimes? Maybe you are just hitting a
>>> rounding problem.
>>>
>> Which server; do you mean PostgreSQL or JBoss?  I think you mean JBoss,
>> but I'm not sure how to check on that.  If you know, please share.  I'll
>> see what I can find out.
>>
> No, I mean the PostgreSQL server.
>
roland=# show integer_datetimes;
 integer_datetimes
-------------------
 off
(1 row)

When I use a trigger to set the timestamps, they show up with
microsecond precision.  The problem is that the extra precision is
definitely getting lost before Hibernate tries to put the value back.
The update looks more-or-less like

update mytable set col1=val1 where primary_key = pkval and
some_timestamp = timestamp_value

timestamp_value doesn't match what is in the database even though it was
retrieved by an earlier query.

I've got some more poking I can do.  Java doesn't seem to have a way of
printing microseconds for timestamps with, for example,
SimpleDateFormat, but I should be able to see if it can at least
retrieve them.

roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
Roland Roberts wrote:
> I've got some more poking I can do.  Java doesn't seem to have a way
> of printing microseconds for timestamps with, for example,
> SimpleDateFormat, but I should be able to see if it can at least
> retrieve them.
I modified HibernateUTC$Timestamp, my UserVersionType class, to have it
use log4j to tell me exactly what the timestamp is immediates after the
rs.getTimestamp() call.  The extra precision is there.

20:01:21,756 INFO  [STDOUT] Hibernate: select security0_.id as id1_,
security0_.active_on as active2_1_, security0_.primary_identifier as
primary3_1_, security0_.identifier_type_code as identifier4_1_,
security0_.security_type_code as security5_1_, security0_.issue_name as
issue6_1_, security0_.issuer_name as issuer7_1_,
security0_.industry_name as industry8_1_, security0_.exchange_code as
exchange9_1_, security0_.mic_code as mic10_1_,
security0_.def_sector_code as def11_1_, security0_.icb4_code as
icb12_1_, security0_.def_gdr_security_id as def13_1_, security0_.is_live
as is14_1_, security0_.shares_outstanding as shares15_1_,
security0_.last_price_modified_time as last16_1_,
security0_.last_price_time as last17_1_,
security0_.last_price_validated_time as last18_1_,
security0_.active_until as active19_1_ from drpg.Security security0_
where primary_identifier=? limit ?
20:01:21,767 INFO  [HibernateUTC] got timestamp 2009-03-20
20:00:59.526338 nanos=526338000
20:01:21,768 INFO  [HibernateUTC] got timestamp 2009-03-20 20:00:59.6
nanos=600000000

I'm not through poking, but at least I know where the problem isn't.

roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220


Re: Hibernate, JBoss, PostgreSQL, timestamp read doesn't match during update

From
Roland Roberts
Date:
Roland Roberts wrote:
> Roland Roberts wrote:
>> I've got some more poking I can do.  Java doesn't seem to have a way
>> of printing microseconds for timestamps with, for example,
>> SimpleDateFormat, but I should be able to see if it can at least
>> retrieve them.
> I modified HibernateUTC$Timestamp, my UserVersionType class, to have
> it use log4j to tell me exactly what the timestamp is immediates after
> the rs.getTimestamp() call.  The extra precision is there.
>
> 20:01:21,756 INFO  [STDOUT] Hibernate: select security0_.id as id1_,
> security0_.active_on as active2_1_, security0_.primary_identifier as
> primary3_1_, security0_.identifier_type_code as identifier4_1_,
> security0_.security_type_code as security5_1_, security0_.issue_name
> as issue6_1_, security0_.issuer_name as issuer7_1_,
> security0_.industry_name as industry8_1_, security0_.exchange_code as
> exchange9_1_, security0_.mic_code as mic10_1_,
> security0_.def_sector_code as def11_1_, security0_.icb4_code as
> icb12_1_, security0_.def_gdr_security_id as def13_1_,
> security0_.is_live as is14_1_, security0_.shares_outstanding as
> shares15_1_, security0_.last_price_modified_time as last16_1_,
> security0_.last_price_time as last17_1_,
> security0_.last_price_validated_time as last18_1_,
> security0_.active_until as active19_1_ from drpg.Security security0_
> where primary_identifier=? limit ?
> 20:01:21,767 INFO  [HibernateUTC] got timestamp 2009-03-20
> 20:00:59.526338 nanos=526338000
> 20:01:21,768 INFO  [HibernateUTC] got timestamp 2009-03-20 20:00:59.6
> nanos=600000000
>
> I'm not through poking, but at least I know where the problem isn't.
Aarrgh!  User error.  The application copies the timestamps inside the
setter by creating a new one:

myval = new Timestamp(old_timestamp.getTime());

which only copies down to the milliseconds.


roland

--
               PGP Key ID: 66 BC 3B CD
Roland B. Roberts, PhD                             RL Enterprises
roland@rlenter.com                            6818 Madeline Court
roland@astrofoto.org                           Brooklyn, NY 11220