Oracle CHAR column and trailing spaces with Hikari CP and JDBC - spring

We have solved the problem selection from a column with trailing spaces CHAR type in Oracle db with Spring like
jdbcTemplate.query("SELECT * from PAYMENTS where CURRENCY_CODE=?",new SqlParameterValue(OracleTypes.FIXED_CHAR, currencyCode));
We would like to use datasource connection property fixedString=true as a general solution, but given properties has not been applied through configurations, but seen in debugging. Is there anything need to be applied that forgotten or is there any other approach we can try?
Properties, have been tried:
spring.datasource.hikari.additional.fixedString=true
spring.datasource.connectionProperties=fixedString=true
spring.datasource.connection-properties=fixedString=true;

Related

Migration to SpringBoot 3: trunc a date with oracle driver no longer works (hibernate)

I am currently migrating a SpringBoot 2.7 application to SpringBoot 3. The following query is used within a SpringData repository:
#Query("select b from #{#entityName} b where (trunc(b.date) <= trunc(:date))")
List<T> findByDate(LocalDateTime date);
While this works great in SpringBoot 2.7, with SpringBoot 3 the following message is thrown:
org.hibernate.QueryException: Parameter 1 of function trunc() has type NUMERIC, but argument is of type java.time.LocalDateTime
Unfortunately, a simple migration to datetrunc was unsuccessful:
Error Msg = ORA-00904: "DATETRUNC": ungültige ID
Does anyone have a solution for this?
Best regards
So, as I noted in my previous answer, there's no standard HQL function for date truncation. That's because it would be quite difficult to implement in most SQL dialects (I have not really investigated to see quite how difficult, but it's at least nontrivial.)
However, just especially for you, in Hibernate 6.2, which should go into CR today, what I have done is added undocumented (but tested) support for the date_trunc() function under Postgres, DB2, Oracle, and H2. On Oracle, this translates to trunc(), of course.
For example, you could write:
date_trunc(year,current_timestamp)
When I say it's "undocumented" I mean you use it at your own risk. (The documented way to do this remains to use a FunctionContributor.)
I hope that helps.
UPDATE:
Oh and by the way, I just noticed that you're not actually using the full form of the trunc() function in your query. You're actually just stripping off the time part of a timestamp.
There's actually multiple ways to do that in HQL without needing to use the Oracle-specific trunc() function. (But of course trunc()/date_trunc() can do more things that you're not using here.)
JPA-standard way
One of the improvements I added to the new JPA 3.1 specification was to let you write:
extract(date from current_timestamp)
To strip off the time part of the timestamp.
HQL alternative
But you can also do it using the HQL cast() function if you prefer:
cast(current_timestamp as Date)
These two options translate to the same SQL on Oracle.
In Hibernate 6 we started checking the argument types of HQL functions.
Since trunc(numeric,places) is a very common function across many dialects of SQL, we register it as one of the known functions, though we have not yet promoted it to a “standard” HQL function (perhaps we should).
On the other hand, Oracle’s trunc(date) is extremely specific to Oracle and is more like the date_trunc() function on DB2 and Postgres. We have not (so far) attempted to standardize on any sort of function for timestamp truncation, and I don’t consider it a high priority. This function is not registered by OracleDialect.
So, therefore, if you try to call trunc() on a Date, you will get a typing error.
Now, in general, in Hibernate 6.x:
We do not guarantee that Hibernate Dialects know about every single SQL function in your database, and you can't depend on that.
Instead we have a rather long list of documented HQL functions that we promise work portably across every database we support. (Right now trunc() is not on that list, but it almost made it onto the list.)
And that's perfectly fine, because we also provide APIs that make it very easy to register new SQL functions, either platform-specific functions like this one, or functions you've written yourself, either by:
writing a custom Dialect, or
providing a FunctionContributor.
https://docs.jboss.org/hibernate/orm/6.1/userguide/html_single/Hibernate_User_Guide.html#hql-user-defined-functions
Alternatively, if that's too much work, you can just use the JPA-standard syntax for calling native SQL functions, which is function('trunc', date, 'YEAR'), but I consider this a bit unlovely.
I'm using Oracle database and after migration to new hibernate I had the same issue.
I solved it by overriding org.hibernate.dialect.OracleDialect and adding ORACLE_TRUNC function to a functionRegistry. I've used the same definition trunc function definition as in Hibernate < 6 Oracle8iDialect.
public class CustomOracleDialect extends OracleDialect {
public CustomOracleDialect() {
super();
}
#Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
functionContributions.getFunctionRegistry().register("ORACLE_TRUNC", new StandardSQLFunction("trunc"));
}
}
Configure hibernate to use this dialect inapplication.properties / application.yml file:
spring:
jpa:
database-platform: com.example.CustomOracleDialect
In your queries use ORACLE_TRUNC() instead of TRUNC().
I tried using DATE_TRUNC(date, my_date) function from hibernate 6.2 RC2 as Gavin King said - but it transformed my query to CAST(my_date AS DATE) instead of TRUNC(my_date) causing date comparison issues.

PreparedStatement - set param to DEFAULT (keyword)

Although this question seems to be close to this one, it is actually different.
Question
Is there any way to specify DEFAULT value as a parameter in JDBC's PreparedStatement?
Use-case
I'd like to have a single statement used for several inserts (or batch) into the table having some column defined as, say:
updated TIMESTAMP NOT NULL DEFAULT TIMESTAMP.
Now, assume that I got a non-uniform set of entries to insert, some of them DO have a value for that column while others DOESN'T (effectively relying on the DB to generate it).
Instead of 'divide and conquer' pattern (which obviously may become exponentially complex if there are more columns like this), I'm looking to run the same PreparedStatement in the single batch, while specifying DEFAULT value for all those entries that DOESN'T have the required values.
Well, seems that a statement of the #a_horse_with_no_name is straight forwardly to the point.
Gone over the PreparedStatement Java 9 docs again and found no hints to anything even close to this.
I'm missing a functionality to set parameters to a DB functions/keywords like DEFAULT, CURRENT_TIMESTAMP etc, but that's the state of PreparedStatement as of now.

Mapping a long text string in Oracle and NHibernate

Using NHibernate 3.1 with both SQL Server and Oracle DBs, we need to store a text string that is longer than 4000 characters. The text is actually XML, but that is not important - we just want to treat it as raw text. With SQL Server, this is easy. We declare the column as NVARCHAR(MAX) and map it thusly:
<property name="MyLongTextValue" length="100000"/>
The use of the length property tells NHibernate to expect a string that may be longer than 4000 characters.
For the life of me, I cannot figure out how to make this work on Oracle 11g. I've tried declaring the column as both XMLTYPE and LONG with no success. In the first case, we end up with ORA-01461: can bind a LONG value only for insert into a LONG column when trying to insert a row. In the second case, the data is inserted correctly but comes back as an empty string when querying.
Does anyone know how to make this work? The answer has to be compatible with both SQL Server and Oracle. I'd rather not have to write custom extensions such as user types and driver subclasses. Thanks.
You should use something like this
<property name="MyLongTextValue" length="100000" type="StringClob"
not-null="false"/>
This should work with Oracle CLOB type and SqlServer NTEXT type.
Make sure the property on your model is nullable
public virtual string MyLongTextValue {get;set;}
You should always use the Oracle.DataAccess when dealing with CLOBs
For whom this may interest, I solved my problem following the step 3 of this article:
3. Using correct Mapping attributes: type="AnsiString"
Normally we can use type="String" default for CLOB/NCLOB. Try to use > type="AnsiString" if two steps above not work.
<property name="SoNhaDuongPho" column="SO_NHA_DUONG_PHO" type="AnsiString"/>
In my case I set it with FluentNHibernate:
.CustomType("AnsiString")
You might be interested in this article.
<property column="`LARGE_STRING`" name="LargeString" type="StringClob" sql-type="NCLOB" />

Remove padding added by legacy DB2 databases on query results

I have the following setup.
'Apps/Reports' <---------> 'DB2 Connect' <------------> 'Legacy DB2 on AS400'
`Hibernate` `native calls`
When data is retrieved from by the application, it will be padded with extra spaces if the length is less that the column length. Of note when running a query, if the WHERE cause parameter is not padded, its automatically padded with extra spaces such that the query will retrieve the same records for a padded and non-padded parameter.
Is there a way (preferably on IBM DB2 Connect or connection string parameter) to remove extra whitespaces from a resultset?
You could implement an hibernate UserType that automatically trim the strings. There are good exmaples on
https://forum.hibernate.org/viewtopic.php?t=928294
http://java.dzone.com/articles/annotating-custom-types
http://santescas.blogspot.de/2014/02/creando-un-usertype-de-hibernate-que-se.html
Are the columns in the iSeries defined as CHAR()? I'm assuming so, because this is how CHAR() works -- it's a fixed field length, not a variable field length (that's what VARCHAR is for).

LINQ FormatException

I currently have an existing database and I am using the LINQtoSQL generator tool to create the classes for me. The tool is working fine for this database and there are no errors with that tool.
When I run a LINQ to SQL query against the data, there is a row that has some invalid data somehow within the table and it is throwing a System.FormatException when it runs across this row. Does anyone know what that stems from? Does anyone know how I can narrow down the effecting column without adding them one by one to the select clause?
Do you have a varchar(1) that stores an empty string?
You need to change the type from char to string in the designer (or somehow prohibit empties). The .net char type cannot hold an empty string.

Resources