Setting database-agnostic default column timestamp using Hibernate - java

I'm working on a java project full of Hibernate (3.3.1) mapping files that have the following sort of declaration for most domain objects.
<property name="dateCreated" generated="insert">
<column name="date_created" default="getdate()" />
</property>
The problem here is that getdate() is an MSSQL specific function, and when I'm using something like H2 to test subsections of the project, H2 screams that
getdate()
isn't a recognized function. It's own timestamping function is
current_timestamp().
I'd like to be able to keep working with H2 for testing, and wanted to know whether there was a way of telling Hibernate "use this database's own mechanism for retrieving the current timestamp". With H2, I've come up with the following solution.
CREATE ALIAS getdate AS $$ java.util.Date now() { return new java.util.Date(); } $$;
CALL getdate();
It works, but is obviously H2 specific.
I've tried extending H2Dialect and registering the function getdate(), but that doesn't seem to be invoked when Hibernate is creating tables. Is it possible to abstract the idea of a default timestamp away from the specific database engine?

Could you try the following (without generated since your database is not generating the value):
<column name="DATE_CREATED" sql-type="timestamp" default="CURRENT_TIMESTAMP"/>

Have you tried using a <timestamp> mapping inside your <class>?
The docs aren't very clear but it sounds like this should result in mapping a column whose value is a timestamp.
You can specify if Hibernate should use a database generated value by setting either generated="insert" or generated="always".

Related

Hibernate throwing validation exception "wrong column type encountered in column" even when my DDL script and JPA entity are in sync

I am starting my spring container in validate mode
autoddl=validate
I am getting a validation exception like this
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-
validation: wrong column type encountered in column [amount] in table [Balance];
found [numeric (Types#NUMERIC)], but expecting [int8 (Types#BIGINT)]
and my DDL script goes like this
CREATE TABLE Balance(stratr VARCHAR(25), histFromDate TIMESTAMP WITHOUT TIME
ZONE,amount numeric(11, 0))
and my attribute in JPA entity goes like this
#Column(name="amount", precision=11, scale=0) //have specified precision and scale
private Long amount ;
where I have used import javax.persistence.Column.Since I have annotated the exact precision and scale, Shouldn't hibernate validate with these info that I have provided through the column annotation? What could have I missed ?
I cannot do the following
#Column(
columnDefinition = "NUMERIC(11,0)"
)
private Long amount;
because I don't know the data store of this JPA entity.
I also tried generating the script by the following property
<prop key="javax.persistence.schema-generation.scripts.action">drop-and-create</prop>
<prop key="javax.persistence.schema-generation.scripts.create-target">./l/create.sql</prop>
<prop key="javax.persistence.schema-generation.scripts.drop-target">./l/drop.sql</prop>
This is also generating as int8 and not numeric(11,0). What can be done to solve this ?
It's really quite difficult to grasp what you're trying to accomplish, but if I understood correctly:
you want to keep your application portable by not fixing the column definition on the entity level to be NUMERIC(11,0), which would make it Postgres-specific
at the same time, you want your column to use NUMERIC(11,0) for Postgres and not INT8 that Hibernate would normally use for a Long in Postgres (and is hoping to find in your schema upon validation)
In short, you want a per-database customization that is not reflected in your entity mapping. The only way to accomplish that is to customize the dialect that Hibernate is using for your version of Postgres. What you need to do is:
determine which dialect version is being selected for your Postgres database (it will be one of the following: PostgresPlusDialect, PostgreSQL81Dialect, PostgreSQL82Dialect, PostgreSQL91Dialect, PostgreSQL92Dialect,PostgreSQL93Dialect, PostgreSQL94Dialect, PostgreSQL95Dialect, PostgreSQL9Dialect)
extend from that class, adding the following definition:
public MyCustomPostgresDialect() {
super();
registerColumnType(Types.BIGINT, "NUMERIC(11, 0)");
}
(If you want to be able to control the precision and scale using #Column(precision = ..., scale = ...), use registerColumnType(Types.BIGINT, "NUMERIC($p, $s)") instead)
add the hibernate.dialect property to persistence.xml, pointing to the fully qualified class name of your custom dialect
Note that this will, of course, affect all Long properties in your data model, not just the specific field in question.
I can think on only reason is because in your entity amount type is Long but in JPA creation script your DDL specified as amount numeric(11, 0) here second param suggest decimal precision.
As you can see java tries to enter data in Long type (ie. 10.0000), similar to BigInt in Database but database does not accept such decimal value being type numeric (11,0)
You should be able to resolve it by either changing your java code to have entity amount type int or change DDL to have scaleInt. ie. NUMERIC(11,5).
However best bet would be to have DECIMAL type for any non Integer type.
http://www.h2database.com/html/datatypes.html#decimal_type

Mapping an oracle number type in Hibernate

I have a column (OCPR_TIMESTAMP) in oracle of type NUMBER(18) and want to select this column using hibernate.
I have the hibernate mapping file mapped as..
<property name="timestamp" column="OCPR_TIMESTAMP" type="java.lang.Double" />
and the timestamp property in my VO as a Double type.
When I select a record, the timestamp is always returned as null.
An example of the data stored in this column is:
7.18575602814979E15
I have tried various different types in the hibernate mapping, any ideas?
Thanks
Apologies - the persistence method being used was not hibernate, this was a web service using jdbc template so column needed adding to query string. Thanks for your response anyway Tim.

How to express a Serializable Blob type in a hibernate mapping file

I have a legacy application that uses hibernate for mapping objects into a database. It uses the Hibernate Mapping XML file to do so. The java class contains two properties abc and def that implement java Serializable. The mapping is defined this way:
<property name="abc" column="ABC" type="serializable" length="32672"/>
<property name="def" column="DEF" type="serializable" length="32672"/>
When I try to set this up with oracle, I get a nasty error "ORA-01754: a table may contain only one column of type LONG" which essentially is complaining about creating two 'long raw' columns in one table. Oracle does not like this. After reading up on the issue, the recommended approach is to use blobs instead of 'long raw' types.
My question is, how can I express in the hibernate mapping file to use a serializable type mapped into a blob column? I would think there would be a serializable_blob type but there does not seem to be.
I know this is possible with JPA annotations using #Basic and #Lob. It should also be possible using the hibernate mapping file. How can this be done in the hibernate mapping file?
Update:
The following do not work as Serializable works:
type=binary - This one expects a byte[]. Does not work for Serializable classes. Gives ClassCastException.
type=blob - - This one expects a java.sql.Blob. Does not work for Serializable classes. Gives ClassCastException.
type=materialized_blob - - This one expects a byte[]. Does not work for Serializable classes. Gives ClassCastException.
<property
name="data"
type="blob"
column="DATA"/>
...
should work.
Ok, did some more research following my comment above and, by Jove, I found it.
In Hibernate 3.5 + Spring 3.1, I used Spring's org.springframework.orm.hibernate3.support.BlobSerializableType. Now I'm upgrading to Hibernate 4.3, that option isn't available anymore. I did find the type to column mappings as OP, but in my application there are various Strings (legacy) that are mapped to BLOB fields.
So, as I reported in the comment above, I found the org.hibernate.type.SerializableToBlob type, which is parameterize. Below how I got the mapping to work (using good old-fashioned hbm.xml mappings)
<property name="description" column="TEXT">
<type name="org.hibernate.type.SerializableToBlobType">
<param name="classname">java.lang.String</param>
</type>
</property>
And that appears to do the trick. (the classname value should be the type of the attribute you are mapping, I think)

How to create two-column unique key constrain with HBM mapping?

I have a hibernate mapping:
<properties name="lrt_bps_bpr_acs_uk" unique="true">
<many-to-one name="activitySummary" column="bps_acs_id" class="xyz.ActivitySummary"
unique-key="lrt_bps_bpr_acs_uk"/>
<many-to-one name="bestPractice" column="bps_bpr_id" class="xyz.BestPractice"
unique-key="lrt_bps_bpr_acs_uk"/>
</properties>
I expected that hibernate would create unique constraint on database for this mapping. Nothing like that happened however.
What am I doing wrong?
Seems like you do nothing wrong. We do it the same way in our project and it works fine.
I thought to have the same problem once because I couldn't find the constaints in our oracle DB because I thought the specified unique-key attribute value (in your case 'lrt_bps_bpr_acs_uk') is used as constraint_name in the DB. But that isn't the case (at least with oracle DB).
What's the actual DDL that Hibernate is generating? What would be the DDL if you remove the unique-key parameters? What's the Hibernate version?
From the documentation, it seems you are doing it correctly:
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-properties
Dont specify name="lrt_bps_bpr_acs_uk" unique="true" in the properties element. It automatically takes it and applies the constraint with unique-key

Querying "extension tables" using Hibernate

I am having a querying issue in Hibernate. I have a table, 'test', with existing data. I have a requirement where I can not modify the schema of the test table, so I created another table, 'testExtension', whose primary key is a foreign key to the primary key of Test. Data in testExtension is a subset of the data in test. i.e. There will be less rows in 'testExtension' than in 'test'.
I have defined this relationship in a configuration file as follows:
<class name="Test" table="test">
<id name="testId" column="test_id">
<generator class="sequence">
<param name="sequence">test_id_seq</param>
</generator>
</id>
<property name="name"/>
<joined-subclass name="TestExtension" table="testExtension">
<key column="test_id"/>
<property name="summary" />
<property name="homepage"/>
</joined-subclass>
With this setup, I am able to create a TestExtension object in my Java program, populate it with data, 'save' it via Hibernate, and commit the transaction. And it correctly saves data in both Test and TestExtension.
My problem is occurring when I am trying to query data from these tables. Right now if I query for a particular test_id using the TestExtension.class to QBE, it will only return a row if that id exists in both Test and TestExtension. If I use the Test.class to QBE, it will return the row but I will not have access to any of the data stored in TestExtension.
My question is: how can I query these tables so that the results are based off a 'left outer join' of both Test and TestExtension? Any solution is appreciated, whether it's query by example, HQL, or something else (though preferably not raw SQL).
Thanks!
HQL is probably the easiest way to do this. Docs are here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/queryhql-joins.html
Sounds like what you might want to do is remap your relationships so that Test and TestExtension use a one-to-one relationship instead of inheritance. Then you can query for Test and TestExtension using a left outer join across the one-to-one.
If you use HQL to write a query for the Test class, it should do what you want. I assume QBE is effectively adding the class of your example entity as one of the query parameters.
So sth like:
from Test t where t.property = :value
should return either Test or TestExtension entities. Note that (at least with the versions of Hibernate I've used). In this case, Hibernate should immediately give you back the actual entities rather than a proxy too--- be aware that TestExtension entities can sometimes be returned as plain Test lazy-loading proxies.

Categories

Resources