JPA EclipseLink oracle db missing right parenthesis - oracle

I have a criteriaQuery:
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> root = criteriaQuery.from(Employee.class);
criteriaQuery.where(criteriaBuilder.equal(root.get("type"), 1));
Then build rowCount query from the existing query:
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<Employee> root = criteriaQuery.from(Employee.class);
Predicate restriction = criteriaQuery.getRestriction();
if (restriction != null)
countQuery.where(restriction);
countQuery.select(criteriaBuilder.countDistinct(root));
From debugging,
Predicate restriction = criteriaQuery.getRestriction();
criteriaQuery.where is (TYPE = ?), seen from debugger.
the restriction above returned is not (TYPE = ?), but ((TYPE = ?) = ?).
Eclipse generated SQL:
SELECT COUNT(DISTINCT(ID)) FROM EMPLOYEE WHERE ((TYPE = ?) = ?)
that causes error on oracle 11g db.
Error Code: 907
Call: SELECT COUNT(DISTINCT(ID)) FROM EMPLOYEE WHERE ((TYPE = ?) = ?)
bind => [1, true]
Query: ReportQuery(referenceClass=Employee.class sql="SELECT COUNT(DISTINCT(ID)) FROM EMPLOYEE WHERE ((TYPE = ?) = ?)")
00:31:24,718 ERROR [system] ORA-00907: missing right parenthesis
java.sql.SQLSyntaxErrorException: ORA-00907: missing right parenthesis
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:886)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1175)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1296)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3657)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1495)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeSelect(DatabaseAccessor.java:1007)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:642)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:1991)
at org.eclipse.persistence.sessions.server.ServerSession.executeCall(ServerSession.java:570)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeSelectCall(DatasourceCallQueryMechanism.java:299)
at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.selectAllRows(DatasourceCallQueryMechanism.java:694)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllRowsFromTable(ExpressionQueryMechanism.java:2738)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.selectAllReportQueryRows(ExpressionQueryMechanism.java:2675)
at org.eclipse.persistence.queries.ReportQuery.executeDatabaseQuery(ReportQuery.java:848)
at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1127)
at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:403)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1215)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1793)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1775)
at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1740)
at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
at org.eclipse.persistence.internal.jpa.QueryImpl.getSingleResult(QueryImpl.java:517)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getSingleResult(EJBQueryImpl.java:400)
what is the reason for generating ((TYPE = ?) = ?) instead of (TYPE = ?).
Thanks for any help.

Without knowing much about the actual value of "TYPE" being bound, it seems that embedded single quotes within the actual value being bound may be causing this error.
A quote from the following site explains this: ORA-00907: missing right parenthesis tips
If you are using single quotes in phrases that are surrounded by other single quotes, you have to add another single quote by the inner single quotes to avoid ORA-00907. For example, you have to use '''''' instead of '''' in order for ORA-00907 to not be thrown over syntax.
*EDIT 1
Consider also looking at static metamodel representations of your typed parameters. For example:
ParameterExpression<String> typeParam = criteriaBuilder.parameter( Integer.class );
criteriaQuery.where( criteriaBuilder.equal( root.get( Employee_.type ), typeParam ) );
Where Employee_ type is a static metamodel as defined by the JPA spec.
Here is an example of this using an older version of EclipseLink. YMMV:
JPA 2.0 Criteria API with Maven and EclipseLink

You cannot reuse predicates/restrictions between the queries as they are tied to a specific CriteriaBuilder which is tied to a specific query. EclipseLink does allow reusing a built query, but you have to access native API and clone the underlying EclipseLink query before making changes.

Related

HANA query with dynamic placeholder executed using jdbcTemplate in Spring Boot

I have a few HANA queries that rely on PLACEHOLDER input. The input to this is currently hardcoded which is leading to SQL injection vulnerability being detected by Veracode.
In order to fix that, I am trying to parameterize the value given to PLACEHOLDER using PreparedStatement, but getting the below error :
PreparedStatementCallback; uncategorized SQLException for SQL [SELECT * FROM some_table (PLACEHOLDER.\"$$<IP_SOME_COLUMN>$$\" => ?) WHERE some_flag = ?; ]; SQL state [HY000]; error code [2048]; SAP DBTech JDBC: [2048]: column store error: search table error: [34023] Instantiation of calculation model failed;exception 306002: An internal error occurred\n; nested exception is com.sap.db.jdbc.exceptions.JDBCDriverException: SAP DBTech JDBC: [2048]: column store error: search table error: [34023] Instantiation of calculation model failed;exception 306002: An internal error occurred
I have already checked this solution and gone through the documentation for input parameters in SAP HANA. Below is my code :
String sqlQuery = SELECT * FROM some_table ( PLACEHOLDER.\"$$<IP_SOME_COLUMN>$$\" => ? ) WHERE some_flag = ? ;
PreparedStatementSetter preparedStatementSetter = (PreparedStatement ps) -> {
ps.setString(1, firstInput);
ps.setString(2, secondInput);
}
ResultSetExtractor<T> rse = new DataResultSetExtractor();
getJdbcTemplate().query(sqlQuery, preparedStatementSetter, rse);
The same works well with the hardcoded way (prone to SQL injection) :
StringBuffer sql = new StringBuffer();
sql.append("SELECT * FROM some_table ").append("( 'PLACEHOLDER' = ('$$IP_SOME_COLUMN$$',").append(firstColumnValue).append("))");
//Map<String,Object> paramMap = new HashMap<String,Object>();
//getNamedParameterJdbcTemplate().query(sql.toString(), paramMap, rse);
How do I fix this error?
Figured the issue out. It seems, in the new syntax you need to provide the input parameter in single quotes and not in triple single quotes
Works : 'foo'
Doesn't work : '''bar'''

Joins Using QueryDsl and Boolean Expression

Need a help regarding QueryDsl using BooleanExpression. Find the requirement below.
I have Enquiry and Followup table. OneToMany mapping in between them. 1 Enquiry having many followup.
Suppose i have 5 enquiries. for each enquiry i have 3 followup.
Expectation is ..i need last record of followup for each enquiry.
Please find the different ways i have tried.
QBuyerFollowUp qBuyerfollowup = QBuyerFollowUp.buyerFollowUp;
ListPath<BuyerFollowUp, QBuyerFollowUp> followUpList = qBuyerEnquiry.followUpList;
BooleanExpression expression = commonExpression.and(new JPAQuery<>().select(qBuyerfollowup.nextDate).from(qBuyerfollowup)
.where(qBuyerfollowup.eq(followUpList.any()))
.orderBy(qBuyerfollowup.followUpId.desc()).limit(1).eq(new Date());
please ignore commonExpression which is an another expression.
Error: Subquery returns more than 1 row
BooleanExpression expression = followUpList.any().followUpId.in(new JPAQuery<QBuyerFollowUp>().select(qBuyerfollowup.followUpId.max())
.from(qBuyerfollowup).groupBy(qBuyerfollowup.buyerEnquiry.id)
.having(qBuyerfollowup.nextDate.eq(dateFormat.parse(dateFormat.format(new Date())))));
Error: unknown column nextDate.
Here generated query is:
select count(buyerenqui0_.id) as col_0_0_ from buyer_enquiry buyerenqui0_ where
buyerenqui0_.assigned_to= 79 and
(buyerenqui0_.created_at between "2020-05-01 00:00:00" and "2020-05-31 23:59:59") and buyerenqui0_.enq_status<> "Dropped" and
not ( not (exists (select followupli1_.id from buyer_followup followupli1_ where buyerenqui0_.id=followupli1_.buyer_enquiry_id)))
and (exists (select 1 from buyer_followup followupli2_ where buyerenqui0_.id=followupli2_.buyer_enquiry_id and
(followupli2_.id in (select max(a.id) from buyer_followup a group by a.buyer_enquiry_id having date(a.next_date)=date(now())))));
solution: If will remove alias name 'a' from this query
select max(a.id) from buyer_followup a group by a.buyer_enquiry_id having date(a.next_date)=date(now())))
it is working fine.
But we don't have any control on this because alias name is generated by Spring.
You can't join subqueries in JPQL, but you can project them, which should be sufficient in this case:
QInquiry inquiry = QInquiry.inquiry;
QFollowup followup = QFollowup.followup;
Map<Inquiry, Followup> results = query().from(inquiry)
.transform(GroupBy.groupBy(inquiry).as(query().from(followup)
.where(followup.inquiry.eq(inquiry)
.orderBy(followup.id.desc())
.limit(1)))

Generic criteriaUpdate set boolean = !boolean

I want to use criteriaUpdate to create an update query like this:
UPDATE <SOME TABLE>
SET SELECTED = !SELECTED
WHERE
[DYNAMIC QUERY HERE]
The closest I could get was with the code:
public <T> Query createRevertSelectionQuery(Class<T> clazz, EntityManager em, Specification<T> s) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<T> criteriaUpdate = cb.createCriteriaUpdate(clazz);
Root<T> root = criteriaUpdate.from(clazz);
Predicate p = cb.and(new Predicate[] {s.toPredicate(root, null, cb)});
Expression<Boolean> e =cb.not((root.get("selected").as(Boolean.class)));
Path<Boolean> selected = root.get("selected");
criteriaUpdate.set(selected, e);
criteriaUpdate.where(p);
Query q = em.createQuery(criteriaUpdate);
return q;
}
but it fails because I get the following query:
update com.redknee.suspense.mgt.model.Moc as generatedAlias0
set generatedAlias0.selected = generatedAlias0.selected <> true
where
[dynamic query]
giving me the error
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: <> near line 1, column 118
Anyone can help please?
I am not sure if this is a bug or if it is just not meant to be used this way .
In where-clause NOT and any other operands work like a charm. But, no matter what you try Hibernate query builder seems always to optimize those parenthesis away (in my opinion it might still be a good habit to always use parenthesis but its only an opinion).
One way to force parenthesis is to use JPA Subquery. See below example. Note that i have slightly altered the JPA object names by my own taste and not included the Specification because it is not relevant to this solution:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaUpdate<T> update = cb.createCriteriaUpdate(clazz);
Root<T> from = update.from(clazz);
Path<Boolean> selected = from.get("selected");
// Subquery just "joins back" to the same row and
// returns a negated boolean value of "selected" from the original row
Subquery<Boolean> subSelect = update.subquery(Boolean.class);
Root<T> subFrom = subSelect.from(clazz);
subSelect.select(cb.not(selected));
subSelect.where(cb.equal(from.get("id"), subFrom.get("id")));
update.set(selected, subSelect);

Hibernate null parameter native query Spring Boot

I am trying to pass a null parameter (:searchQuery) to a native query in Spring Boot, I have tried various different ways but whenever I pass null I get the error
ERROR: operator does not exist: text ~~ bytea
Query
#Query(value = "SELECT count(*) FROM analysis_history ah, analysis_group ag WHERE ah.analysis_group_id = ag.id "
+ "AND ah.creation_date BETWEEN :from AND :to AND ((:searchQuery IS NULL) OR (:searchQuery IS NOT NULL AND ag.name LIKE :searchQuery))", nativeQuery = true)
Long count(#Param("from") Date from, #Param("to") Date to, #Param("searchQuery") String searchQuery);
Can anyone help?
You cannot use like null
SELECT count(*) FROM analysis_history ah, analysis_group ag
WHERE ah.analysis_group_id = ag.id AND ah.creation_date
BETWEEN :from AND :to AND ag.name LIKE :searchQuery
And you pass '%' in the searchQuery when the searchQuery parameter is null? e.g.
repository.count(from, to, searchQuery == null ? "%" : searchQuery);
There is a way to bypass this, but you need access to the EntityManager, and not use the #Query annotation to create that implementation for you.
Long count(Date from,Date to, String searchQuery) {
Number n = em.createNativeQuery("... that query")
.setParameter("from", from, TemporalType.DATE) // assuming that's a Date, and not TimeStamp
.setParameter("to", to, TemporalType.DATE)
.setParameter("searchQuery", "")
.setParameter("searchQuery", searchQuery)
.getSingleResult();
return n.longValue();
}
The first call to .setParameter("searchQuery", "") tells Hibernate what type this is, the next one sets the value.
The problem comes from Postgres doing the typecheck during parsing, and not deferring the error in case the parameter set is a null.
An alternative workaround to the issue posed by #coladict, which is compatible with Query and performs as well as the original would.
SELECT count(*)
FROM analysis_history ah, analysis_group ag
WHERE ah.analysis_group_id = ag.id
AND ah.creation_date BETWEEN :from AND :to
AND (:searchQuery IS NULL OR ag.name LIKE CAST(:searchQuery AS CHARACTER VARYING))

How to use oracle NVL function in spring data repository nativeQuery

I am trying to use oracle' NVL function in nativeQuery of Spring Data Repository.
While i am passing null value in programId parameter then it is throwing exception (ORA-00932: inconsistent datatypes: expected NUMBER got BINARY) and if i am passing a valid value in "programId" then it is working properly.
public interface ProgramRulesRepository
{
public static final String FIND_PROGRAM_RULES_BY_PARTICIPANT_ID_AND_ROLE_OR_PROGRAM = " SELECT DISTINCT pr.id , pr.program_id , prgm.display_name , pr.group_id , pr.type , pr.cmmo_key FROM program prgm , program_rule pr , program_audience pa , participant_audience paa WHERE prgm.id = pa.program_id AND pr.program_id = pa.program_id AND pa.audience_id = paa.audience_id AND pr.type = :roleType AND paa.participant_id = :participantId "
+ " AND pr.program_id = NVL ( :programId ,pr.program_id )";
#Query( value = FIND_PROGRAM_RULES_BY_PARTICIPANT_ID_AND_ROLE_OR_PROGRAM, nativeQuery = true )
List<Object[]> findByParticipantIdAndRoleTypeOrProgramId( #Param( "participantId" ) Long participantId, #Param( "roleType" ) String roleType, #Param( "programId" ) Long programId );
}
Exception :
Caused by: java.sql.SQLSyntaxErrorException: ORA-00932: inconsistent datatypes: expected NUMBER got BINARY
Avoid NVL and COALESCE when using Hibernate. COALESCE function needs to have all parameters of the same type. NVL is using implicit casting which doesn't work well when there is BINARY or VARBINARY. And where this BINARY came from? Well, Hibernate is setting NULL value as type of BINARY and ignores the real datatype backed by Java. When you set logging level to trace you can see in output:
binding parameter [1] as [VARBINARY] - [null]
So when the other type of in COALESCE or NVL function is for example NUMBER, you will get that error ORA-00932.
A good solution for this problem is this:
" AND (:programId IS NULL OR pr.program_id = :programId)"
Doing this way, if your param is null this sentence will result TRUE and won't discard the register, and if it is not null will be compared with the value stored in its field.
I have faced this problem with MongoDB. But I could solve this problem by using mongoTemplate as like,
Query query = new Query();
Criteria criteria = new Criteria();
List<Criteria> orCriterias = new ArrayList<>();
if( dto.getId() != null) {
orCriterias.add(Criteria.where("id").is(Integer.parseInt(dto.getId())));
}
... so on for other fields
criteria.orOperator(orCriterias.toArray(new Criteria[orCriterias.size()]));
query.addCriteria(criteria);
List<StudentDTO> recordsList = mongoTemplate.find(query, StudentDTO.class,
"student_collection");

Resources