Oracle is having issues with hibernate generated query, generates query with _ in row num - oracle

I am using hibernate and using criteria query, database is Oracle. The below is the query that gets generated.
"
WITH query AS (
SELECT inner_query.*,
ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__
FROM (
select this_.MODIFIEDTIME as MODIFIED1_1_1_,
this_.FIELDNAME as FIELDNAM2_1_1_,
this_.EMPNBR as EMPNBR3_1_1_
from EMPLOYEEAUDIT this_
left outer join EMPLOYEE employee2_ on this_.EMPNBR=employee2_.EMPNBR
) inner_query
)
SELECT MODIFIED1_1_1_,FIELDNAM2_1_1_,EMPNBR3_1_1_
FROM query WHERE __hibernate_row_nr__ >= 1
AND __hibernate_row_nr__ < 10;"
It is throwing the below error:-
SQL Error: 911, SQLState: 22019
ORA-00911: invalid character
However, if i take the SQL query and repalce the hibernate_row_nr with hibernate_row_nr__ and run it in DbVisualizer it works. It seems Oracle is not able to recognize starting '_' as valid characters. How to fix this issue?
Below is my java code:-
public List<Employee> getEmployeeList(int start, int limit, AgSort sorter) throws UIFilterException {
DetachedCriteria detached = getEmployeeListCriteria(sorter, filters);
return (List<DealSetupAudit>) hibernateTemplate.findByCriteria(detached, start, limit);
}
private DetachedCriteria getEmployeeListCriteria(AgSort sorter) throws UIFilterException {
DetachedCriteria detached = getBaseCriteria(DealSetupAudit.class, sorter);
return detached;
}
protected <T> DetachedCriteria getBaseCriteria(Class<T> genericType, AgSort sorter) throws UIFilterException {
DetachedCriteria criteria = DetachedCriteria.forClass(genericType);
addSorter(criteria, sorter);
return criteria;
}
protected void addSorter(DetachedCriteria criteria, AgSort sort) {
if (sort != null) {
Order order = null;
if (sort.getDir().equalsIgnoreCase(Constants.DESC)) {
order = Order.desc(sort.getColumn());
} else {
order = Order.asc(sort.getColumn());
}
criteria.addOrder(order);
}
}
I am using Hibernate4, below is my application.properties :-
oracle.datasource.driver-class-name=oracle.jdbc.OracleDriver
oracle.datasource.url=******
oracle.datasource.username=*****
oracle.datasource.password=*****
oracle.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
oracle.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle10gDialect

Double quotes? Usually a really, really bad idea. I'd suggest you to avoid such things and use valid column names / aliases.
SQL> select dname _invalid_alias_
2 from dept
3 where deptno = 10;
select dname _invalid_alias_
*
ERROR at line 1:
ORA-00911: invalid character
SQL> select dname "_valid_alias_"
2 from dept
3 where deptno = 10;
_valid_alias_
--------------
ACCOUNTING
SQL>

Related

EF Core with GroupBy - column ambiguously defined

Trying to filter and group by an IQueryable I get an oracle exception "column ambiguously defined".
The code looks as follows:
public class UserRepository{
public IEnumerable<UserDto> GetUsers(UserFilter filter){
IQueryable<User> users = _context.Users;
return filter.Set(users)
.Select(user => new UserDto(){
Id = user.Id,
NoOfTweets = user.NoOfTweets
}
.ToListAsync();
}
}
public class UserFilter : Filter{
public string? Name {get; set;}
public UserGroupBy? GroupBy{get; set;} //Enum
public override IQueryable<Model> Set(IQueryable<Model> models){
IQueryable<User> users = models.Cast<User>();
users = !string.IsNullOrEmpty(Name)
? users.Where(user => user.Name.Equals(Name))
: users;
if(GroupBy.HasValue)
{
users = GroupBy.Value switch{
UserGroupBy.Name => users
.GroupBy(user => new{
user.Name,
user.Birthday
})
.Select(group => new User(){
Name = group.Key.Name,
NoOfTweets = group.Sum(e => e.NoOfTweets)
})
_ => throw new NotImplementedException()
}
}
return base.Set(users); // in base class the list gets paginated
}
}
I guess the error is because of the two selects (in group-by and finally into userGet) but how can I workaround that cleanly?
I hoped that the entity Framework solves this using aliases, but it doesn't.
My Versions:
Oracle.EntityFrameworkCore: 6.21.61
.Net 6
Additional Thoughts
Doing the Group by after the paginate leads to an incorrect result because the wrong list gets paginated.
It seems that the second Select reexecutes the first on so that the group.Sum(...) is called twice
I know nothing about EF Core, but - as far as Oracle is concerned & based on what you said so far, there are (at least) two positions that might raise the error. I'll try to illustrate it on a simple query which selects sum of salaries per each department.
Asterisk points to position which raised the error:
SQL> select deptno, sum(sal)
2 from emp join dept on emp.deptno = dept.deptno
3 group by deptno;
group by deptno
*
ERROR at line 3:
ORA-00918: column ambiguously defined
OK, so let's add table name - in vain, it still doesn't work because column in select column list also misses its source table name:
SQL> select deptno, sum(sal)
2 from emp join dept on emp.deptno = dept.deptno
3 group by dept.deptno;
select deptno, sum(sal)
*
ERROR at line 1:
ORA-00918: column ambiguously defined
Finally, when fixed:
SQL> select dept.deptno, sum(sal)
2 from emp join dept on emp.deptno = dept.deptno
3 group by dept.deptno;
DEPTNO SUM(SAL)
---------- ----------
30 9400
20 10875
10 8750
SQL>
As I said: I can't interpret code you wrote and don't see anything like what I posted, but I hope that these examples might help you find the culprit.

EclipseLink with Oracle: "limit by rownum" does not use index

we're facing performance issues with EclipseLink 2.7.7 when accessing Oracle 12.1 tables with paging. Investigation showed that Oracle does not use its indexes with EclipseLink paging.
I've extracted the sql sent to the database and was able to reproduce the issue using a database tool (DataGrip).
Example:
-- #1: without paging
SELECT col1 AS a1, col2 AS a2, col3 AS a3, ...
FROM <TABLE>
WHERE colN > to_timestamp('2021-12-08', 'yyyy-mm-dd'))
ORDER BY col1 DESC;
Explain plan shows that the index on colN is used. Fine.
When the same query is executed with paging, the original query is wrapped in two subselects:
-- #2 with EclipseLink paging
SELECT * FROM (
SELECT a.*, ROWNUM rnum FROM (
SELECT col1 AS a1, col2 AS a2, col3 AS a3, ...
FROM <TABLE>
WHERE colN > to_timestamp('2021-12-08', 'yyyy-mm-dd'))
ORDER BY col1 DESC
) a WHERE ROWNUM <= 100
) WHERE rnum > 0;
For this query, the explain plan shows that the index on colN is not used.
As a result, querying a table with millions of rows takes 50-90 seconds (depending on the hardware).
Side note: on my test database, this query returns 0 records since colN values are before 2021-12-08.
Oracle 12c introduced the OFFSET/FETCH syntax:
-- #3
SELECT col1 AS a1, col2 AS a2, col3 AS a3, ...
FROM <TABLE>
WHERE colN > to_timestamp('2021-12-08', 'yyyy-mm-dd'))
ORDER BY col1 DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY;
Using this syntax, indexes are at least sometimes used as expected. When they are used, execution time is below 1s which is acceptable.
However, I could not figure out how to convince EclipseLink to use this syntax.
If ORDER BY col1 DESC is removed from the original paged query (#2), the index is used the query returns fast enough. However, it will not return the desired records, so that does not help.
How can I implement performant paged queries using EclipseLink and Oracle 12?
How can I force oracle to use the index on colN when using paging and order by?
The OraclePlatform printSQLSelectStatement method is responsible for building the query used, nesting the queries to use rownum for the query you've seen. To use a new form, you would extend one of the OraclePlatform classes you are using (maybe Oracle12Platform) and override that method to append the syntax you want instead. Something like:
#Override
public void printSQLSelectStatement(DatabaseCall call, ExpressionSQLPrinter printer, SQLSelectStatement statement) {
int max = 0;
int firstRow = 0;
ReadQuery query = statement.getQuery();
if (query != null) {
max = query.getMaxRows();
firstRow = query.getFirstResult();
}
if (!(this.shouldUseRownumFiltering()) || (!(max > 0) && !(firstRow > 0))) {
super.printSQLSelectStatement(call, printer, statement);
return;
}
call.setFields(statement.printSQL(printer));
printer.printString("OFFSET ");
printer.printParameter(DatabaseCall.MAXROW_FIELD);
printer.printString(" ROWS FETCH NEXT ");
printer.printParameter(DatabaseCall.FIRSTRESULT_FIELD);
printer.printString(" ROWS ONLY");
call.setIgnoreFirstRowSetting(true);
call.setIgnoreMaxResultsSetting(true);
}
You would then specify your custom OraclePlatform class using a persistent property:
<property name="eclipselink.target-database" value="my.package.MyOracle12Platform"/>
If something like that works for you, please submit it as an enhancement request - though you might want to work some way to use the old behaviour into it, as the performance differences you've experienced might depend on the query/data involved.
Thanks to #Chris, I came up with the following Oracle12Platform. This solution currently ignores "Bug #453208 - Pessimistic locking with query row limits does not work on Oracle DB". See OraclePlatform.printSQLSelectStatement for details):
public class Oracle12Platform extends org.eclipse.persistence.platform.database.Oracle12Platform {
/**
* the oracle 12c `OFFSET x ROWS FETCH NEXT y ROWS ONLY` requires `maxRows` to return the row count
*/
#Override
public int computeMaxRowsForSQL(final int firstResultIndex, final int maxResults) {
return maxResults - max(firstResultIndex, 0);
}
#Override
public void printSQLSelectStatement(final DatabaseCall call, final ExpressionSQLPrinter printer, final SQLSelectStatement statement) {
int max = 0;
int firstRow = 0;
final ReadQuery query = statement.getQuery();
if (query != null) {
max = query.getMaxRows();
firstRow = query.getFirstResult();
}
if (!(this.shouldUseRownumFiltering()) || (!(max > 0) && !(firstRow > 0))) {
super.printSQLSelectStatement(call, printer, statement);
} else {
statement.setUseUniqueFieldAliases(true);
call.setFields(statement.printSQL(printer));
if (firstRow > 0) {
printer.printString(" OFFSET ");
printer.printParameter(DatabaseCall.FIRSTRESULT_FIELD);
printer.printString(" ROWS");
call.setIgnoreFirstRowSetting(true);
}
if (max > 0) {
printer.printString(" FETCH NEXT ");
printer.printParameter(DatabaseCall.MAXROW_FIELD); //see #computeMaxRowsForSQL
printer.printString(" ROWS ONLY");
call.setIgnoreMaxResultsSetting(true);
}
}
}
}
I had to override computeMaxRowsForSQL in order to get the row count instead of "lastRowNum" when calling printer.printParameter(DatabaseCall.MAXROW_FIELD);
I also try to deal with missing firstRow xor maxResults

jdbctemplate equivalent of following query

I have a long list of argument which I need to send to oracle database. I was able to do it by splitting the query but I am unable to find a way to do similar using jdbcTemplate. my query is:
select name,age from person where personId in (A1,F2,D3...G900)
or personId in (A901, C902 , ... R1800)
or personId in (A1801,G1802 .... H2700)
or personId in (P2701, G2702 ... R3600)
or since oracle allow more than 1000 touple but does not allow in so JDBC equivalent for
SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
List<Map<String, Object>> findPeeps(List<Long> personIds) {
String sql = "select name,age from person where personId in (:personIds)";
return namedParameterJdbcTemplate.queryForList(sql, new MapSqlParameterSource("personIds", personIds));
}
As #zaki said you can use that but the error that you are getting is from Oracle since there is limit of records you can put in WHERE IN clause. You can try something like this
insert into TEMP values ( ... );
select * from T where (a,b) in (select x,y from temp);
delete from TEMP;

JdbcTemplate queryForList changing the database order

I'm using the queryForList method to fetch the data for the following sql.
String sql = "select * from my_table ORDER BY ? ? LIMIT ?, ?";
return jdbcTemplate.queryForList(sql,new Object[]{param1,param2,
param3,param4});
I can see that the order is altered when the data is returned.To confirm, I tried using simple JDBC as follows
try {
Connection conn = jdbcTemplate.getDataSource().getConnection();
//Sample values param1 -> field1, param2 -> asc/desc, param3 -> 0, param4 -> 25
String sql = "select * from my_table ORDER BY "+param1+" "+param2+" LIMIT "+param3+", "+param4;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.println("Name = "+rs.getString("field1")+" Type = "+rs.getString("field2"));
}
} catch (SQLException e) {
e.printStackTrace();
}
Here, the print statement gives the output as desired. I want to use queryForList as it suits our data format requirements. How can I make sure that the order is maintained in queryForList?
You cannot pass the order by as parameters - ORDER BY ? ?. This will result in ORDER BY "firstSort", "secondSort"rather than ORDER BY firstSort, secondSort.

Comparisons of Oracle DATE column with java.sql.timestamp via JOOQ

I am using jooq to build queries for Oracle. Everything works fine except for dates:
public static void main(String[] args) throws SQLException {
java.sql.Timestamp now = new java.sql.Timestamp(new Date().getTime());
Connection con = DriverManager.getConnection(... , ... , ...);
final Factory create = new OracleFactory(con);
Statement s = con.createStatement();
s.execute("create table test_table ( test_column DATE )");
s.execute("insert into test_table values (to_date('20111111', 'yyyymmdd'))");
// -- using to_date
ResultSet rs = s.executeQuery("select count(1) from test_table where test_column<to_date('20121212', 'yyyymmdd')");
rs.next();
System.out.println(""+rs.getInt(1));
rs.close();
// -- using a preparedstatement with java.sql.timestamp
PreparedStatement ps = con.prepareStatement("select count(1) from test_table where test_column<?");
ps.setTimestamp(1,now);
rs = ps.executeQuery();
rs.next();
System.out.println(""+rs.getInt(1));
rs.close();
// -- using jooq with java.sql.timestamp
final org.jooq.Table<org.jooq.Record> table = create.tableByName("TEST_TABLE");
final org.jooq.SelectSelectStep sss = create.select(create.count());
final org.jooq.SelectJoinStep sjs = sss.from(table);
final org.jooq.SelectConditionStep scs = sjs.where(create.fieldByName("TEST_COLUMN").lessThan(now));
System.out.println(scs.toString());
rs = s.executeQuery(scs.toString());
rs.next();
System.out.println(""+rs.getInt(1));
rs.close();
s.close();
}
Gives the following output:
1
1
select count(*) from "TEST_TABLE" where "TEST_COLUMN" < '2012-12-12 19:42:34.957'
Exception in thread "main" java.sql.SQLDataException: ORA-01861: literal does not match format string
I would have thought that JOOQ would check the type of Object in lessThan(Object)
to determine whether it can come up with a reasonable conversion, but apparently it
just does an Object.toString() in this case. I also remember that I never had issues with date queries via JOOQ in MySQL (although this is a while back). What am I doing wrong?
I suspect that this issue is due to the fact that create.fieldByName() doesn't know the type of the column (hence, Object), and coerces that unknown type on the right hand side of the comparison predicate. That should be fixed in jOOQ. I have registered #2007 for this:
https://github.com/jOOQ/jOOQ/issues/2007
In the mean time, try explicitly setting the type on your field:
create.fieldByName(Timestamp.class, "TEST_COLUMN").lessThan(now)

Resources