How to use oracle NVL function in spring data repository nativeQuery - spring

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");

Related

How to add optional parameter in JPQL?

How to use/add optional parameter in JPQL?
#Query(value = "SELECT stud FROM Student stud where stud.name = :studentName AND stud.age IN :studentAgeList")
List<Student> getStudents(
#Param("studentName ") String studentName,
#Param("studentAgeList") List<Integer> studentAgeList
)
How to make studentAgeList parameter in above query ?
I tried below :
#Query(value = "SELECT stud FROM Student stud where stud.name = :studentName AND (:studentAgeList IS NULL OR stud.age IN :studentAgeList))
List<Student> getStudents(
#Param("studentName ") String studentName,
#Param("studentAgeList") List<Integer> studentAgeList
)
But getting error : unexpected AST node:
Tried above but getting error
JPQL does not support optional parameters, you can use overload methods with different queries or criteria API or JPA specifications.
may be get answer here: Set optional parameters in JPQL Query

Spring JPA - Custom function with array input

I have the following function script:
CREATE or REPLACE FUNCTION test1(_ids bigint[])
RETURNS TABLE
(
businessId BIGINT,
businessName VARCHAR
)
LANGUAGE plpgsql
AS
$$
BEGIN
RETURN QUERY (
SELECT id, name
FROM business b
WHERE id = any(_ids)
);
END;
$$
I can do the following and it works fine in normal psql commands:
SELECT * FROM test1(ARRAY [1,2,3])
How can I do this in Java? I tried passing an array of long, but it throws an sql grammar error.
repository:
#Query(nativeQuery = true, value = "SELECT * FROM public.test1(?)")
List<TestView> test1(long[] ids);
service:
long[] ids = {1L, 2L, 3L};
return businessRepository.test1(ids); // <-- does not work
If this is the incorrect way to pass an array / list of ids as an input parameter to a psql function, please advise.
Use a list rather than an array
#Query(nativeQuery = true, value = "SELECT * FROM public.test1(:ids)")
List<TestView> test1(#Param("ids") List<Long> ids);
Long[] ids = {1L, 2L, 3L};
return businessRepository.test1(Arrays.asList(ids));

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 pass Byte value to JPQL in Spring?

I have an Interface wich
extends JpaRepository
I have method in it:
#Query("SELECT a FROM ArticlesEntity a " +
"WHERE ((:approved = 2 ) or (a.approved = :approved)) ")
Page<ArticlesEntity> filterByAllPage(
#Param("approved") Byte approved,
Pageable pageable);
When this method is invoked I have an exeption:
java.lang.IllegalArgumentException: Parameter value [2] did not match expected type [java.lang.Integer (n/a)]
When I change
(:approved = 2 )
to
(:approved is null)
or
#Param("approved") Byte approved
to
#Param("approved") Integer approved
or
"WHERE ((:approved = 2 ) or (a.approved = :approved))"
to
"WHERE (a.approved = :approved) "
it works.
It seems that should be something like
(:approved = (byte) 2 )
or
(:approved=2.byteValue())
but both didn't work also.
Is there any way to use Byte here or it's better to switch to Integer ?
The database is MySQL and it has table "articles" with column "approved" of "TINYINT(1)" type.

Linq Error: InvalidOperationException: Could not translate expression

Get value out of DateTime column
if null to return String.Empty
else
DateTime.ToShortDateString
What am I doing wrong => query produced below:
var queryable = from p in Products
select new {
selldate = p.SellEndDate == null
? string.Empty
: p.SellEndDate.Value.ToShortDateString() };
Error: InvalidOperationException: Could not translate expression 'Table(Product).Select(p => new <>f__AnonymousType01(selldate = IIF((p.SellEndDate = null), Invoke(value(System.Func1[System.String])), p.SellEndDate.Value.ToShortDateString())))' into SQL and could not treat it as a local expression.
Basically what's happening here is that LINQ to SQL is taking your entire query and trying to convert it into something that SQL Server can understand. The problem, though, is that SQL Server has no concept of DateTime.ToShortDateString, so the conversion to SQL fails.
You'll have to change your query so that it just selects SellEndDate (which will get it as a Nullable<DateTime>) and then when you use the results of that query you can do the conversion to string. For example:
var list = (from p in Products
select p.SellEndDate).ToList();
// calling ToList() above means we have the entire resultset in memory and
// no longer have to pass the query back to SQL Server
var stuff = from p in list select new
{
selldate = p.SellEndDate == null ?
string.Empty :
p.SellEndDate.Value.ToShortDateString()
};
ToShortDateString doesn't seem to have equivalent SQL translation.
Use ToString instead.
If the date time field allows nulls:
from order in repository.Order
select order.OrdShipDate == null ? "" : order.OrdShipDate.GetValueOrDefault(DateTime.Now).Month.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Day.ToString() + "/" + order.OrdShipDate.GetValueOrDefault(DateTime.Now).Year.ToString();
If the date time field doesn't allow nulls:
from order in repository.Order
select order.OrdShipDate.Month.ToString() + "/" + order.OrdShipDate.Day.ToString() + "/" + order.OrdShipDate.Year.ToString();

Resources