Problem using JPA with Oracle and grouping by hour - java

I have a problem using JPA with Oracle and grouping by hour.
Here's the scenario:
I have an entiry named EventData. This entity has a java.util.Date field named startOfPeriod. This field maps in a table field of datatype DATE.
The query I'm using is something like:
select min(ed.startOfPeriod) as eventDate,
(...)
from
Event e inner join e.eventDatas ed
(...)
group by
year(ed.startOfPeriod),
month(ed.startOfPeriod),
day(ed.startOfPeriod),
hour(ed.startOfPeriod)
order by 1
If I remove the group by "hour(ed.startOfPeriod)" it works fine (it doesn't produce any errors but it doesn'to do what I want).
When I insert this group by clause it makes this exception:
Caused by: java.sql.SQLException: ORA-30076: campo de extração inválido para origem de extração
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:288)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java:745)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:219)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:813)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1049)
at oracle.jdbc.driver.T4CPreparedStatement.executeMaybeDescribe(T4CPreparedStatement.java:854)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1154)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3370)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3415)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:92)
at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208)
at org.hibernate.loader.Loader.getResultSet(Loader.java:1812)
at org.hibernate.loader.Loader.doQuery(Loader.java:697)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.doList(Loader.java:2232)
Analysing the error code, it happens when "The extract source does not contain the specified extract field.". But the source of extraction (the startOfPeriod field) is of datatype DATE (which has an hour part).
The same code works like a charm in SQL Server.
Anyone knows what is going on?
Tnhks!

Have you tried TO_CHAR(d, 'HH24') instead? You could also trunc() to hours...

Not quite an answer, but it sounds suspiciously like a driver issue to me (especially as this works fine on SQLServer). What version of the Oracle driver are you using? Have you tried different versions?

I didn't convert all my DATE fields to TIMESTAMP ones.
Instead, I extended the Oracle's dialect to rewrite the hour function.
Like that:
public class Oracle9Dialect extends org.hibernate.dialect.Oracle9Dialect
{
public Oracle9Dialect()
{
super();
registerFunction("hour", new SQLFunctionTemplate(Hibernate.INTEGER, "to_number(to_char(?1, 'hh24'))"));
}
}
Using this dialect (org.hibernate.dialect.Dialect), the hour function will not use the ANSI hour function (extract(hour from <FIELD>)). It will use to_number(to_char(<FIELD>, 'hh24')) instead.
The bad thing is that it always use the custom function (even when it doesn't need to be used, like with a TIMESTAMP field).
This is what I did to solve my problem.

Related

Criteria JPA - Call Postgres CAST function

I'm trying to call a Postgres function with Criteria but it's not working. I need to use the LIKE clause in a UUID field, so I need to convert into VARCHAR first.
The result I need:
SELECT * FROM my_table WHERE cast(uuid as varchar(36)) like '%1234%';
What I'm doing in Criteria:
final Path<UUID> uuidField = from.get("uuid");
var cast = cb.function("cast", String.class, uuidField, cb.literal("as varchar(36)"));
cb.like(cast, String.format("%%%s%%", stringValue));
The query which is being generated:
HQL: select generatedAlias0 from com.MyTable as generatedAlias0 where function('cast', generatedAlias0.uuid, 'as varchar(36)') like '%1234%' order by generatedAlias0.name asc
Error:
2022-08-08 18:38:48,549 WARN [io.ver.cor.imp.BlockedThreadChecker] (vertx-blocked-thread-checker) Thread Thread[vert.x-eventloop-thread-9,5,main] has been blocked for 2393 ms, time limit is 2000 ms: io.vertx.core.VertxException: Thread blocked
at antlr.ASTFactory.make(ASTFactory.java:342)
at antlr.ASTFactory.make(ASTFactory.java:352)
at org.hibernate.hql.internal.antlr.HqlBaseParser.jpaFunctionSyntax(HqlBaseParser.java:4633)
at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:1075)
The log is not so clear (I'm using Quarkus + Hibernate Reactive), but I suspect it crashed in database because the function('cast', generatedAlias0.uuid, 'as varchar(36)').
I think it should be something like: function('cast', generatedAlias0.uuid, as varchar(36)) (without quotes). But I don't know how to achieve this result to test my theory.
How can I call this CAST function?
After investigating some possible solutions (I'm avoiding to create custom database routines) I found something interesting in a answer from another question:
Currently JPA does not have APIs for replace() and cast(string as numeric). But you can use CriteriaBuilder.function(...) to create database native functions if database portability is not critical.
Source: JPA criteria builder: how to replace and cast a string to numeric in order-by?
I don't know if this is documented is some place, but assuming that there is no way to call CAST(x AS y) using Criteria, I tried a workaround to force the UUID to VARCHAR cast without using the probably unsupported CAST function.
I tested this direct SQL query to database:
SELECT * FROM my_table WHERE concat(uuid, '') like '%123%';
And it works. This CONCAT forces the cast to VARCHAR and the LIKE function does his job. Knowing this, I did:
final Path<UUID> uuidField = from.get("uuid");
var cast = cb.function("concat", String.class, uuidField, cb.literal(""));
cb.like(cast, String.format("%%%s%%", stringValue));
Worked perfectly. I hope this help someone else.
As #HaroldH said, it's a weird requirement, but happened in my project.

Caused by: java.lang.ClassCastException: java.sql.Timestamp cannot be cast to java.sql.Date

I am getting the below given error for the following code snippets:
try {
cRows = new CachedRowSetImpl();
while(cRows.next())
{
MyClass myClass = new MyClass();
myClass.setPrevDate(cRows.getDate("PREV_DATE")); // In debug mode, the error was throwing when I press Resume from here.
}
}
Error:
Caused by: java.lang.ClassCastException: java.sql.Timestamp cannot be cast to java.sql.Date
In the database, the datatype for the column is DATE only. I am not able to figure out where the Timestamp is coming here.
Obsolete:
Use java.util.Date for the field. java.sql.Timestamp is a direct subclass of it. As is java.sql.Date - that strips the time part. Why the java database driver takes DATE to be Timestamp is a bit weird. What is the database vendor? Did you specify a length or so? Are indeed only dates stored?
Researched:
I looked into CachedRowSetImpl.java, and Oracle's docs and Oracle does everything fine (java.sql.Date, java.sql.Time, java.sql.Timestamp convertible).
The CachedRowSetImpl does simply cast the DATE's Object (and getObject is likely to return the high resolution Timestamp - with time) to java.sql.Date, and that's wrong.
So override or substitute this sun's class.
/*
* The object coming back from the db could be
* a date, a timestamp, or a char field variety.
* If it's a date type return it, a timestamp
* we turn into a long and then into a date,
* char strings we try to parse. Yuck.
*/
switch (RowSetMD.getColumnType(columnIndex)) {
case java.sql.Types.DATE: {
long sec = ((java.sql.Date)value).getTime();
return new java.sql.Date(sec);
}
I have done a research on this issue and found some useful links. I found this confusion between DATE and TIMESTAMP is JDBC Driver specific. And most of the links suggest the use of -Doracle.jdbc.V8Compatible=true. For my JBoss I have set this in run.bat and the issue got resolved.
https://community.oracle.com/thread/68918?start=0&tstart=0
http://www.coderanch.com/t/90891/JBoss/oracle-jdbc-Compatible-true
https://community.oracle.com/message/3613155
The oracle doc shares different solutions:
Alter your tables to use TIMESTAMP instead of DATE. This is probably
rarely possible, but it is the best solution when it is.
Alter your application to use defineColumnType to define the columns
as TIMESTAMP rather than DATE. There are problems with this because
you really don't want to use defineColumnType unless you have to (see
What is defineColumnType and when should I use it? ).
Alter you application to use getTimestamp rather than getObject. This
is a good solution when possible, however many applications contain
generic code that relies on getObject, so it isn't always possible.
Set the V8Compatible connection property. This tells the JDBC drivers
to use the old mapping rather than the new one. You can set this flag
either as a connection property or a system property. You set the
connection property by adding it to the java.util.Properties object
passed to DriverManager.getConnection or to
OracleDataSource.setConnectionProperties. You set the system property
by including a -D option in your java command line.
java -Doracle.jdbc.V8Compatible="true" MyApp
Here is the link: http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#08_00

IntelliJ IDEA code inspection: HQL custom dialect & registered functions

My question is about
using registered functions for date/time manipulations in Hibernate Query Language and
IntelliJ IDEA's code inspection for these registered functions in HQL.
I'm using Hibernate 4.2.5 with Java 7, and SQL Server 2008 R2 as the database, and IntelliJ IDEA 12.1.6.
In an HQL query I need to perform the TSQL DATEADD function - or the equivalent HQL date operation. This doesn't seem to exist.
Here's what I'd like to achieve:
update MyTable set startTime = GETDATE(), targetTime = DATEADD(HOUR, allocatedTime, GETDATE()), endTime = null where faultReport.faultReportId = :faultReportId and slaTypeId = :slaTypeId
Searching for answers online has been disappointingly no help, and the most common advice (like the comment seen here: https://stackoverflow.com/a/18150333/2753571) seems to be "don't use date manipulation in hql." I don't see how I can get around performing the operation in the SQL statement in the general case (e.g. when you want to update one column based on the value in another column in multiple rows).
In a similar fashion to the advice in this post: Date operations in HQL, I've subclassed a SQLServerDialect implementation and registered new functions:
registerFunction("get_date", new NoArgSQLFunction("GETDATE", StandardBasicTypes.TIMESTAMP)); // this function is a duplication of "current_timestamp" but is here for testing / illustration
registerFunction("add_hours", new VarArgsSQLFunction(TimestampType.INSTANCE, "DATEADD(HOUR,", ",", ")"));
and added this property to my persistence.xml:
<property name="hibernate.dialect" value="my.project.dialect.SqlServerDialectExtended" />
and then I'm testing with a simple (meaningless, admitted) query like this:
select x, get_date(), add_hours(1, get_date()) from MyTable x
The functions appear to be successfully registered, and that query seems to be working because the following SQL is generated and the results are correct:
select
faultrepor0_.FaultReportSLATrackingId as col_0_0_,
GETDATE() as col_1_0_,
DATEADD(HOUR,
1,
GETDATE()) as col_2_0_,
... etc.
But I now have this problem with IntelliJ IDEA: where get_date() is used in the HQL, the code inspection complains "<expression> expected, got ')'". This is marked as an error and the file is marked in red as a compilation failure.
Can someone can explain how to deal with this, please, or explain what a better approach is? Am I using the incorrect SQLFunction template (VarArgsSQLFunction)? If yes, which is the best one to use?
I'd like the usage of the registered function to not be marked as invalid in my IDE. Ideally, if someone can suggest a better way altogether than creating a new dialect subclass, that would be awesome.

OJB / Oracle XE sql debug-display problem

I have a Java application, and use OJB as my ORM technology. I have an Oracle XE installation locally to develop against. The problem is when I need to debug a problem, I like looking at the SQL output. Here is an example of SQL I can view through the "Top SQL" interface in Oracle XE:
select a_bunch_of_fields
from KREW_DOC_TYP_T A0
WHERE ((UPPER(A0.DOC_TYP_NM) LIKE :1) AND A0.ACTV_IND = :2) AND A0.CUR_IND = :3
The problem is I would like to see the real value instead of ":1". I can't seem to find how I can configure this. I know the real values are working, because the application is responding as expected, for the most part (hence the bugs I am working on).
Thanks,
Jay
One quick and dirty way is to look in the provided database views (v$sql_bind_capture and v$sqlarea). In the SQL below, I just added a like clause to match the sql statement you have above, you will then get a row for each bind variable and it's value. To target a very specific SQL statement you want the sql_id for your query.
SELECT a.sql_text, b.NAME, b.POSITION, b.datatype_string, b.value_string
FROM v$sql_bind_capture b, v$sqlarea b
WHERE b.sql_id = a.sql_id
and a.sql_text like '%UPPER(A0.DOC_TYP_NM) LIKE :1%'
Output (without the SQL the result would look look something like this):
"NAME","POSITION","DATATYPE_STRING","VALUE_STRING"
":B1","2","NUMBER","1001"

Problem persisting a java.util.Date into MySql using Hibernate

I've been debugging this problem for the last couple of hours with no success and figured I'd throw it out to SO and see where that goes.
I'm developing a Java program that persists data into a MySql database using Hibernate and the DAO/DTO pattern. In my database, I have a memberprofile table with a firstLoginDate column. In the database, the SQL type of that column is a DateTime. The corresponding section of the Hibernate XML file is
<property name="firstLoginDate" type="timestamp">
<column name="firstLoginDate" sql-type="DATETIME"/>
</property>
However, when I try to save a Date into that table (in Java), the "date" (year/month/day) part is persisted correctly, but the "time of day" part (hours:minutes:seconds) is not. For instance, if I try to save a Java date representing 2009-09-01 14:02:23, what ends up in the database is instead 2009-09-01 00:00:00.
I've already confirmed that my own code isn't stomping on the time component; as far as I can see source code (while debugging) the time component remains correct. However, after committing changes, I can examine the relevant row using the MySql Query Browser (or just grabbing back out from the database in my Java code), and indeed the time component is missing. Any ideas?
I did try persisting a java.sql.Timestamp instead of a java.util.Date, but the problem remained. Also, I have a very similar column in another table that does not exhibit this behavior at all.
I expect you guys will have questions, so I'll edit this as needed. Thanks!
Edit #Nate:
...
MemberProfile mp = ...
Date now = new Date();
mp.setFirstLoginDate(now);
...
MemberProfile is pretty much a wrapper class for the DTO; setting the first login date sets a field of the DTO and then commits the changes.
Edit 2: It seems to only occur on my machine. I've already tried rebuilding the table schema and wiping out all of my local source and re-checking-out from CVS, with no improvement. Now I'm really stumped.
Edit 3: Completely wiping my MySql installation, reinstalling it, and restoring the database from a known good copy also did not fix the problem.
I have a similar setup to you (except mine works), and my mapping file looks like this:
<property name="firstLoginDate" type="timestamp">
<column name="firstLoginDate" length="19"/>
</property>
My database shows the column definition as datetime.
Edit:
Some more things to check...
Check that the mysql driver the same
on your local as on the working machines.
Try dropping the table, and have
hibernate recreate it for you. If
that works, then there's a problem in
the mapping.
This may or may not be your problem, but we have had serious problems with date/time info - if your database server is on a different time zone than the machine submitting the data, you can have inconsistencies in the data saved.
Beyond that, with our annotation configuration, it looks something like the following:
#Column(name="COLUMN_NAME", length=11)
If it is viable for you, consider using the JodaTime DateTime class which is much nicer than the built in classes and you can also persist them using Hibernate with their Hibernate Support
Using them I mark my fields or getters with the annotation for custom Hibernate Types as:
#org.hibernate.annotations.Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
#Column(name = "date")
This works fine for me and it also generates correct schema generation sql
This works fine in MySQL
Use TemporalType.TIMESTAMP beside your Temporal annonation.
Please check the example below.
#Temporal(TemporalType.TIMESTAMP)
public Date getCreated() {
return this.created;
}

Categories

Resources