How can I cast a Integer to a String in EJBQL - ejbql

I got a Entity with a Integer
#Entity(name=customer)
public Customer {
...
#Column
private int number;
...
//getter,setter
}
Now I want to cast this Integer in a query to compare it with an other value.
I tried this Query:
"SELECT c FROM customer c WHERE CAST(c.number AS TEXT) LIKE '1%'"
But it doesn't work.

This works in some of my code using Hibernate:
SELECT c FROM customer c WHERE STR(c.number) LIKE '1%'
In general, this is what the Hibernate docs (14.10 Expressions) say on casting:
str() for converting numeric or temporal values to a readable string
cast(... as ...), where the second argument is the name of a Hibernate
type, and extract(... from ...) if ANSI cast() and extract() is
supported by the underlying database

Since EJB3 EJBQL has been (almost) replaced by JPQL. In EJBQL and according to http://docs.oracle.com/cd/E11035_01/kodo41/full/html/ejb3_langref.html in JPQL as well there is no functionality to CAST a property of an entity.
So like I already told there are two options left:
Use a native Query.
Add special cast methods to the entities.

You need to specify the column you're selecting from table alias c, and since EJBQL doesn't support a cast function, pass a string into the query instead of text. (This effectively allows your program to do the cast before it gets to EJBQL.)
The example below is in SQL Server, but should give you the idea:
declare #numberText as varchar(50)
set #numberText = '1'
SELECT c.CustomerNumber FROM customer c
WHERE c.CustomerNumber LIKE #numbertext + '%'
So instead of private int number use private string numberText.
NOTE: I edited this answer after OP confirmed EJBQL does not support a CAST function.

Related

Spring JPA criteria builder greaterThan : Can I pass String value for comparing Number type in database

I am using Spring Data JPA criteria builder greaterThan API for doing a comparison.
The database field rating is of type NUMBER(5,0). Entity class has a field rating which is of type Integer. And I am passing the value of type String for the comparison.
Even though I am not passing the Integer value for the comparison, still it is returning valid results. Please help me understand how this is possible.
Does it mean, I can pass the java String version of the database field to the greaterThan method when the actual data type of the field in the database is of a different type.
and my comparison block of code looks like this
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery();
Root<Customer> root = cq.from(Customer.class);
//you can see here , value 20 I am passing as String
Predicate comparisonPredicate = cb.greaterThan(root.get("rating"), "20");
cq.where(comparisonPredicate );
cq.select(root);
TypedQuery<Customer> query = entityManager.createQuery(cq);
This behavior is not documented.
Looking at the openJPA source code for Expressions.GreaterThan, and more specifically the toKernelExpression,
#Override
org.apache.openjpa.kernel.exps.Expression toKernelExpression(ExpressionFactory factory, CriteriaQueryImpl<?> q){
Value val1 = Expressions.toValue(e1, factory, q);
Value val2 = Expressions.toValue(e2, factory, q);
Expressions.setImplicitTypes(val1, val2, e1.getJavaType(), q);
return factory.greaterThan(val1, val2);
}
JPA uses the type of the first expression to build the query.
This behavior looks to be an accidental feature more than an intentional implementation. You should not rely on this and should explicitly cast the variable. You could also use a MetaModel to get a compiler error when trying to compare apples to oranges.

Using Postgres JSONB query with Spring Data and bind parameter fails with InvalidDataAccessApiUsageException

I am currently looking for a solution for the exception
org.springframework.dao.InvalidDataAccessApiUsageException: Parameter with that position [1] did not exist;
My current #Query annotation is:
#Query(
nativeQuery = true,
value = "SELECT * FROM thgcop_order_placement WHERE \"order_info\" #> '{\"parentOrderNumber\":\" :param \"}'")
I guess the position [1] did not exist comes from it being in double quotes plus double quote plus single quote.
How can I make this work?
The query is using Postgres JSONB datatype. The column definition is ORDER_INFO JSONB
The following native query works just fine in the Postgres client:
SELECT * FROM thgcop_order_placement
WHERE "order_info" #> '{"parentOrderNumber":"ORD123"}'
None of the above worked for me except the below,
Service Layer code :-
OrderInfo orderInfo = new OrderInfo();
orderInfo.setParentOrderNumber("ORD123");
....
String param = objectMapper.writeValueAsString(orderInfo);
List<Order> list = jpaRepository.getByParentOrderNumber(param);
JpaRepository.java code :-
#Query(nativeQuery = true, value = "select * from thgcop_order_placement where order_info #> CAST(:condition as jsonb)")
List<Order> getByParentOrderNumber(#Param("condition") String parentOrderNumber);
This is how I achieve the result. I hope this will be very helpful for all enthusiastic!!
Thank you all for your help !!!
TL;DR: Make it work with Bind Parameter and plain JDBC first. Then move on to Spring Data, possibly falling back on a custom implementation.
You are facing problems on many levels here.
Let's start with ignoring Spring Data for now.
The statement you showed is very dissimilar from the one you try to construct with Spring Data because it doesn't contain a bind variable.
So instead of
SELECT * FROM thgcop_order_placement WHERE "order_info" #> '{"parentOrderNumber":"ORD123"}'
We should compare it to
SELECT * FROM thgcop_order_placement WHERE "order_info" #> '{"parentOrderNumber": ? }'
Note that we are losing the quotes since they denote a literal String but we aren't providing a literal String but a bind parameter.
I haven't found any indication that you can use bind parameters in parts of JSON expressions. So instead of the statement above we would need to use:
SELECT * FROM thgcop_order_placement WHERE "order_info" #> ?
Of course, the bind parameter should then contain the complete JSON expression
Unfortunately, this doesn't seem to work either because now Postgres considers the bind parameter a VARCHAR instead of a JSON expression. See https://blog.2ndquadrant.com/processing-json/. I think the correct version should be
SELECT * FROM thgcop_order_placement WHERE "order_info" #> ?::json
But I couldn't get this to work either.
In any case, you are left to transform your parameter to the JSON structure.
Normally I'd suggest using a SpEL expression for this. But it won't work because Spring Data chokes on the curly braces needed in the SpEL expression and considers them the end of the SpEL expression.
If you get something like this to work with a simple JDBC connection or JdbcTemplate you can start to think about #Query annotations.
#Query(
value= "SELECT * FROM thgcop_order_placement WHERE \"order_info #> :name::json",
nativeQuery = true)
This might trigger more problems since Spring Data will either consider ::json part of the parameter name. If this is the case you'll have to fall back on custom implementations.
I ran a couple of experiments, which you can look at and play with here.
Try to bind parameters as following
#Query(nativeQuery = true, value = "SELECT * FROM thgcop_order_placement"
+ " WHERE \"order_info\" #> '{\"parentOrderNumber\":\" ?1 \"}'")
I was stuck at the same problem for a while as well. It seems that springboot messes up while parsing the query present in string format. However this is the solution that I found, which will work directly as a native query in the repository:
Since order_info is of the type jsonb, the value being searched can be casted as a jsonb value.
Value to be searched: {"parentOrderNumber":"ORD123"}
Let's escape the whole string to be parsed by java.
String searchString = "{\"parentOrderNumber\":\"ORD123\"}"
Now, let's type the postgres query in a manner that spring will understand.
#Query(
value = "SELECT * from thgcop_order_placement where ((?1\\:\\:jsonb) <# (order_info\\:\\:jsonb))",
nativeQuery=true
)
List<Order> getByParentOrderNumber(String searchString);
Where,
Spring will replace ?1 with the value of searchString, as defined above.
:: is the typecast operator using which, we are explicitly typecasting the passed parameter (searchString) into jsonb. Therefore the value {\"parentOrderNumber\":\"ORD123\"} is converted into jsonb before an attempt is made for a search.
Also the values of column order_info is explicitly typecasted into jsonb.
Now, when both items (value to be searched and the column) are the same data type, we can use the <# operator to check if the search string is contained in the column values.
At the service level we just have to do this:
String orderNumber = "-- some order value e.g. ORD123 --"
String searchString = "{\"parentOrderNumber\":\"" + orderNumber + "\"}"
List<Order> list = jpaRepository.getByParentOrderNumber(searchString);
More details on Postgres JSON operators can be found in the official documentation HERE: https://www.postgresql.org/docs/9.5/functions-json.html

Can I control how Oracle maps the integer types in ADO.NET?

I've got a legacy database that was created with the database type INTEGER for many (1.000+) Oracle columns. A database with the same structure exists for MS SQL. As I was told, the original definition was created using a tool that generated the scripts from a logical model to the specific one for MS SQL and Oracle.
Using C++ and MFC the columns were mapped nicely to the integer type for both DBMs.
I am porting this application to .NET and C#. The same C# codebase is used to access both MS SQL and Oracle. We use the same DataSets and logic and we need the same types (int32 for both).
The ODP.NET driver from Oracle maps them to Decimal. This is logical as Oracle created the integer columns as NUMBER(37) automatically. The columns in MS SQL map to int32.
Can I somehow control how to map the types in the ODP.NET driver? I would like to say something like "map NUMBER(37) to int32". The columns will never hold values bigger than the limits of an int32. We know this because it is being used in the MS SQL version.
Alternatively, can I modify all columns from NUMBER(37) to NUMBER(8) or SIMPLE_INTEGER so that they map to the right type for us? Many of these columns are used as primary keys (think autoincrement).
Regarding type mapping, hope this is what you need
http://docs.oracle.com/cd/E51173_01/win.122/e17732/entityDataTypeMapping.htm#ODPNT8300
Regarding type change, if table is empty, you may use following script (just replace [YOUR_TABLE_NAME] with table name in upper case):
DECLARE
v_table_name CONSTANT VARCHAR2(30) := '[YOUR_TABLE_NAME]';
BEGIN
FOR col IN (SELECT * FROM user_tab_columns WHERE table_name = v_table_name AND data_type = 'NUMBER' AND data_length = 37)
LOOP
EXECUTE IMMEDIATE 'ALTER TABLE '||v_table_name||' MODIFY '||col.column_name||' NUMBER(8)';
END LOOP;
END;
If some of these columns are not empty, then you can't decrease precision for them
If you have not too much data, you may move it to temp table
create table temp_table as select * from [YOUR_TABLE_NAME]
then truncate original table
truncate [YOUR_TABLE_NAME]
then run script above
then move data back
insert /*+ append */ into [YOUR_TABLE_NAME] select * from temp_table
commit
If data amount is substantial it is better to move it once. In such case it is faster to create new table with correct datatypes and all indexes, constraints and so on, then move data, then rename both tables to make new table have proper name.
Unfortunately the mapping of numeric types between .NET and Oracle is hardcoded in OracleDataReader class.
In general I usually prefer to setup appropriate data types in the database, so if possible I would change the column datatypes because they better represent the actual values and their constraints.
Another option is to wrap the tables using views casting to NUMBER(8) but will negatively impact execution plans because it prohibits index lookups.
Then you have also some application implementation options:
Implement your own data reader or subset of ADO.NET classes (inheriting from DbProviderFactory, DbConnection, DbCommmand, DbDataReader, etc. and wrapping Oracle classes), depending on how complex is your implementation. Oracle.DataAccess, Devart and all providers do exactly the same because it gives total control over everything including any magic with the data types. If the datatype conversion is the only thing you want to achieve, most of the implementation would be just calling wrapped class methods/properties.
If you have access to OracleDataReader after command is executed and before you start to read it you can do a simple hack and set the resulting numeric type using reflection (following implementation is just simplified demonstration).
However this will not work with ExecuteScalar as this method never exposes the underlying data reader.
var connection = new OracleConnection("DATA SOURCE=HQ_PDB_TCP;PASSWORD=oracle;USER ID=HUSQVIK");
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "SELECT 1 FROM DUAL";
var reader = command.ExecuteDatabaseReader();
reader.Read();
Console.WriteLine(reader[0].GetType().FullName);
Console.WriteLine(reader.GetFieldType(0).FullName);
public static class DataReaderExtensions
{
private static readonly FieldInfo NumericAccessorField = typeof(OracleDataReader).GetField("m_dotNetNumericAccessor", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly object Int32DotNetNumericAccessor = Enum.Parse(typeof(OracleDataReader).Assembly.GetType("Oracle.DataAccess.Client.DotNetNumericAccessor"), "GetInt32");
private static readonly FieldInfo MetadataField = typeof(OracleDataReader).GetField("m_metaData", BindingFlags.NonPublic | BindingFlags.Instance);
private static readonly FieldInfo FieldTypesField = typeof(OracleDataReader).Assembly.GetType("Oracle.DataAccess.Client.MetaData").GetField("m_fieldTypes", BindingFlags.NonPublic | BindingFlags.Instance);
public static OracleDataReader ExecuteDatabaseReader(this OracleCommand command)
{
var reader = command.ExecuteReader();
var columnNumericAccessors = (IList)NumericAccessorField.GetValue(reader);
columnNumericAccessors[0] = Int32DotNetNumericAccessor;
var metadata = MetadataField.GetValue(reader);
var fieldTypes = (Type[])FieldTypesField.GetValue(metadata);
fieldTypes[0] = typeof(Int32);
return reader;
}
}
I implemented extension method for command execution returning the reader where I can set up the desired column numeric types. Without setting the numeric accessor (it's just internal enum Oracle.DataAccess.Client.DotNetNumericAccessor) you will get System.Decimal, with accessor set you get Int32. Using this you can get all Int16, Int32, Int64, Float or Double.
columnNumericAccessors index is a column index and it will applied only to numeric types, if column is DATE or VARCHAR the numeric accessor is just ignored. If your implementation doesn't expose the provider specific type, make the extension method on IDbCommand or DbCommand and then safe cast the DbDataReader to OracleDataReader.
EDIT: Added the hack for GetFieldType method. But it might happen that the static mapping hashtable might be updated so this could have unwanted effects. You need to test it properly. The fieldTypes array holds the types returned for all columns of the data reader.

SimpleJpaRepository Count Query

I've modified an existing RESTful/JDBC application i have to work with new features in Spring 4... specifically the JpaRepository. It will:
1) Retrieve a list of transactions for a specified date. This works fine
2) Retrieve a count of transactions by type for a specified date. This is not working as expected.
The queries are setup similarly, but the actual return types are very different.
I have POJOs for each query
My transactions JPA respository looks like:
public interface MyTransactionsRepository extends JpaRepository<MyTransactions, Long>
//My query works like a charm.
#Query( value = "SELECT * from ACTIVITI_TMP.BATCH_TABLE WHERE TO_CHAR(last_action, 'YYYY-MM-DD') = ?1", nativeQuery = true )
List< MyTransactions > findAllBy_ToChar_LastAction( String lastActionDateString );
This returns a list of MyTransactions objects as expected. Debugging, i see the returned object as ArrayList. Looking inside the elementData, I see that each object is, as expected, a MyTransactions object.
My second repository/query is where i'm having troubles.
public interface MyCountsRepository extends JpaRepository<MyCounts, Long>
#Query( value = "SELECT send_method, COUNT(*) AS counter FROM ACTIVITI_TMP.BATCH_TABLE WHERE TO_CHAR(last_action, 'YYYY-MM-DD') = ?1 GROUP BY send_method ORDER BY send_method", nativeQuery = true )
List<MyCounts> countBy_ToChar_LastAction( String lastActionDateString );
This DOES NOT return List as expected.
The object that holds the returned data was originally defined as List, but when I inspect this object in Eclipse, I see instead that it is holding an ArrayList. Drilling down to the elementData, each object is actually an Object[2]... NOT a MyCounts object.
I've modified the MyCountsRepository query as follows
ArrayList<Object[]> countBy_ToChar_LastAction( String lastActionDateString );
Then, inside my controller class, I create a MyCounts object for each element in List and then return List
This works, but... I don't understand why i have to go thru all this?
I can query a view as easily as a table.
Why doesn't JPA/Hibernate treat this as a simple 2 column table? send_method varchar(x) and count (int or long)
I know there are issues or nuances for how JPA treats queries with counts in them, but i've not seen anything like this referenced.
Many thanks for any help you can provide in clarifying this issue.
Anthony
That is the expected behaviour when you're doing a "group by". It will not map to a specific entity. Only way this might work is if you had a view in your database that summarized the data by send_method and you could map an entity to it.

Entity Framework - Linq - Unable to create a constant value of type ‘System.Object’. Only primitive types

I have a method to build an expression for a linq query for a given type, property, and value. This works wonderfully as long as the property on the type is NOT nullable. Here is the example I am working from (http://www.marcuswhitworth.com/2009/12/dynamic-linq-with-expression-trees) I am calling the Equals method on the property. However I have discovered that the Equals method for Nullable types takes an Object as a parameter instead of the Nullable type. I attempted to use GetValueOrDefault to hide the null values but EF doesn't support that method. As a simple example the following code will throw an error:
decimal? testVal = new decimal?(2100);
var test = (from i in context.Leases where i.AnnualRental.Equals(testVal) select i).ToList();
However if you use == instead of the Equals() method it will work OK. I am not sure how to convert the code to use == instead of Equals() however. Any suggestions will be greatly appreciated.
If you say ==, you generate a BinaryExpression with NodeType as ExpressionType.Equal. http://msdn.microsoft.com/en-us/library/bb361179.aspx
If you say .Equals(x), you generate a MethodCallExpression. The MethodCallExpressions that LinqToEntities may translate into Sql is a limitted list (for example none of your own undecorated methods are in that list). Nullable<T>.Equals(x) is apparently not on that list.
Don't say .Equals(x) to LinqToEntities.

Resources