I need to get a very long string from Oracle DB in Spring App, but using a function returning clob leads to internal server error 500. How to get a custom query result containing Clob?
PL/SQL function returning a very long string in clob:
create or replace function getVeryLongString(id_ in number)
return clob as
CURSOR cur IS select x.TARGET || ' pX=' || x.px as tpx -- to get concat string in order of PX desc
from (
select distinct a.ID,
a.TARGET,
MAX(a.PX) OVER (PARTITION BY a.TARGET, a.ID) as px
from BA_ACTIVITYDATA A
where A.TARGET is not null
and a.ID = id_
order by px desc
) x;
l_data clob := '';
begin
FOR fld IN cur
LOOP
l_data := l_data || fld.tpx || ' | ';
end loop;
return rtrim( l_data, ' | ');
end
;
I use this function in Query in Java Spring app:
public interface TargetRepository extends JpaRepository<List, Long> {
#Query(
value = "select t1.fieldA,\n" +
" t1.fieldB,\n" +
" t2.fieldC,\n" +
" getVeryLongString(t3.value1) as fieldD\n" +
"from tableA t1\n" +
"join tableB t2 on t1.id = t2.id\n" +
"join tableC t3 on t3.id = t2.id\n" +
"where t.id = :id",
nativeQuery = true
)
DataTypePX getDataById(Long id);
}
TargetActivity interface looks like:
public interface DataTypePX {
String getFIELDA();
String getFIELDB();
String getFIELDC();
String getFIELDD();
}
Using the above code leads to 500 ERROR.
to fix it I need to change function getVeryLongString as returning varchar2 - it works, but it leads to cutting of result string only to 4000 symbols:
create or replace function getVeryLongString(id_ in number)
return varchar2 as
CURSOR cur IS select x.TARGET || ' pX=' || x.px as tpx -- to get concat string in order of PX desc
from (
select distinct a.ID,
a.TARGET,
MAX(a.PX) OVER (PARTITION BY a.TARGET, a.ID) as px
from BA_ACTIVITYDATA A
where A.TARGET is not null
and a.ID = id_
order by px desc
) x;
-- l_data varchar2(32767) := '';
l_data clob := '';
begin
FOR fld IN cur
LOOP
l_data := l_data || fld.tpx || ' | ';
end loop;
return rtrim( substr(l_data, 1, 4000), ' | ');
end;
Related
I have a native query in plsql like this;
create or replace package body DBPK_Monitoring is
PROCEDURE monitoring_Search(cur OUT SYS_REFCURSOR,) is
query VARCHAR(30000);
whereClause VARCHAR(10000);
BEGIN
whereClause := whereClause || ' and ( cnt.contract_status!=7)';
query := ' select itemTitle as "itemTitle", numberOfRow as "numberOfRow" ,
orderNumber as "orderNumber",createDate as "createDate",
priorityNumber as "priorityNumber"
from buy_buy_order bbo
inner join buy_buy_order_item bbi
on bbi.buy_order_id = bbo.id
left outer join core_User_Role shoUserRole
on shoUserRole.id = bbi.buy_Expert_Id
' || whereClause || ' order by ' || orderBy ||
') e WHERE e.rowNO> ' || lower_Bound || ' and e.rowNO<=' ||
upper_Bound;
OPEN cur FOR query
end monitoring_Search;
end DBPK_Monitoring;
I use of this query in repository with hql like this :
Session session = getSession();
Query query = session.getNamedQuery("monitoring_Search").setResultTransformer(Transformers.aliasToBean(MonitoringDto.class));
int firstResultIndex = searchOption.getPageNumber() * searchOption.getPageSize();
query.setInteger("lower_Bound", firstResultIndex);
query.setParameter("upper_Bound", firstResultIndex + searchOption.getPageSize());
query.setString("orderBy", searchOption.getOrder());
query.setParameter("buyOrderNumber", buyOrderNumber);
List<MonitoringDto> list = query.list();
int count = (list.size() > 0L) ? list.get(0).getNumberOfRow() : 0;
return new QueryResult<MonitoringDto>(searchOption.getPageNumber(), count, searchOption.getPageSize(), list);
When I run program, I get this error:
java.lang.illegalargumentexception no query defined for that name
[monitoring_Search]
How can I fix this?
you have to define monitoring_search as a Hibernate named query , you can see the detail about this in this link : https://www.baeldung.com/hibernate-named-query
have a nice day and kiss people around you ;)
My stored procedure isn't bringing results as it should
All parameters are set OK, like my table name (that is variable according to the year/month).
When I execute the query manually on Management Studio, the results come, but when I call the procedure on my Controller, it doesn't.
PS: the code executed directly is with other dates, but the code executed in the IDE with the informed dates should had brought a result too, I've tried with different dates, the problem seems to be in the Stored Procedure query.
STORED PROCEDURE CODE:
ALTER PROCEDURE [dbo].[usp_listarRegistrosMov]
--PARĂ‚METROS
#NomeTabela VARCHAR(20),
#DataInicial VARCHAR(20),
#DataFinal VARCHAR(20),
#Cracha FLOAT
AS
BEGIN
Declare #Comando Varchar(1000)
Set #Comando = 'SELECT * FROM ' + #NomeTabela + ' WHERE mov_data BETWEEN ' + #DataInicial + ' AND ' + #DataFinal + ' AND mov_cracha = ' + CAST(#Cracha AS VARCHAR(20))
Exec(#Comando)
END
GO
CONTROLLER CODE:
public void consultar() {
LocalDate dataInicio = dataInicial.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate dataFim = dataFinal.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
String mesInicio = String.valueOf(dataInicio.getMonthValue());
String mesFim = String.valueOf(dataFim.getMonthValue());
String anoInicio = (String.valueOf(dataInicio.getYear())).substring(2,4);
String anoFim = (String.valueOf(dataFim.getYear())).substring(2,4);
String empresaString = String.valueOf(Math.round(funcionario.getEmpresa().getCodigo()));
long mesesDiferencaGlob = ChronoUnit.MONTHS.between(dataInicio, dataFim) + 1;
if (dataInicio.isAfter(dataFim)) {
Messages.addGlobalError("Informe uma data inicial posterior Ă data final");
return;
}
if (dataInicio.getMonthValue() != dataFim.getMonthValue()) {
if (dataInicio.getYear() == dataFim.getYear()) {
do {
System.out.println(mesesDiferencaGlob);
String tabela = ("M00"+(String.valueOf(Math.round(funcionario.getEmpresa().getCodigo())))+anoInicio+"0"+mesInicio);
System.out.println(tabela);
DateTimeFormatter formatadorInicio = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String dataInicioString = dataInicio.format(formatadorInicio);
String dataFimString = dataFim.format(formatadorInicio);
System.out.println(dataInicioString + dataFimString);
setRegistrosTemp(eventoEspelhoPontoRepository.findAllRegistrosByFuncionarioTableUnica(tabela, dataInicioString, dataFimString, funcionario.getCracha()));
for (EventoEspelhoPonto item : registrosTemp) {
registros.add(item);
}
int mesInicioInt = Integer.parseInt(mesInicio) + 1;
mesInicio = Integer.toString(mesInicioInt);
mesesDiferencaGlob--;
} while (mesesDiferencaGlob != 0);
}
}
}
REPOSITORY CODE:
#Query(value = "EXECUTE usp_listarRegistrosMov :tabela, :dataInicial, :dataFinal, :cracha", nativeQuery = true)
List<EventoEspelhoPonto> findAllRegistrosByFuncionarioTableUnica(#Param("tabela") String tabela,
#Param("dataInicial") String dataInicial,
#Param("dataFinal") String dataFinal,
#Param("cracha") Double cracha);
In your stored procedure, because you're using strings for dates in a dynamic sql string, you need to add 2 single quotes around them so that the string that gets executed will enclose them in single quotes.
In other words, change this:
Set #Comando = 'SELECT * FROM ' + #NomeTabela +
' WHERE mov_data BETWEEN ' + #DataInicial + ' AND ' + #DataFinal + ' AND mov_cracha = ' + CAST(#Cracha AS VARCHAR(20))
to this:
Set #Comando = 'SELECT * FROM ' + #NomeTabela +
' WHERE mov_data BETWEEN ''' + #DataInicial + ''' AND ''' + #DataFinal + ''' AND mov_cracha = ' + CAST(#Cracha AS VARCHAR(20))
I don't promise that's your only problem because I don't speak java, but that is definitely a problem in your stored procedure.
I am trying to execute a procedure in oracle, but i get a exception while executing the procedure.Please help me where i am wrong.
Error :
java.sql.SQLException: ORA-06550: line 1, column 36:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
. ( ) , * # % & = - + < / > at in is mod remainder not rem =>
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || indicator multiset member submultiset
The symbol ")" was substituted for ";" to continue.
Procedure :
CREATE OR REPLACE PROCEDURE "TEST_101"."AVG_UNLOADING"
(
P_CODE IN VARCHAR2,
P_DATE IN VARCHAR2,
P_VANID IN NUMBER,
P_CURSOR OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN P_CURSOR
FOR
select CARGO_NAME,IMP_EXP_NAME,'TEMP_CARGO_Import',ROUND(SUM(QTY)/((((extract(day from max(END_TIME)-DISCHRG_CMNCD_ANCHRG_TM)*(24*60))+
(extract(day from max(END_TIME)- DISCHRG_CMNCD_ANCHRG_TM)*(60)) + (EXTRACT(DAY FROM max(END_TIME)- DISCHRG_CMNCD_ANCHRG_TM)))-DELAYS)/(24*60))) QTY
from(SELECT (SELECT C.CARGO_CATEGORY_NAME FROM IPT_CARGOMASTER C WHERE C.CARGO_CODE =LL.CARGO_CODE )CARGO_NAME,IL.IMP_EXP_NAME,CASE L.CARGO_TYPE_CODE WHEN 'VC001' THEN LL.DISCHARGE_QUANTITY WHEN 'VC002' THEN LL.QUANTITY_GMT WHEN 'VC004' THEN LL.QUANTITY_GMT
else LL.DISCHARGE_QUANTITY end QTY ,L.DISCHRG_CMNCD_ANCHRG_TM,LL.END_TIME ,(SELECT SUM(T3.TOTAL_TIME) FROM IPT_LOADUNLOADDELAYLINES T3 WHERE T3.ID = L.ID AND T3.MINUS_DELAY_HOURS = 'true') AS DELAYS
,LL.LINE_ID,L.VAN_ID FROM IPT_LOADINGUNLOADING L JOIN IPT_LOADUNLOADOPERATIONLINES LL ON L.ID=LL.ID LEFT JOIN IPT_IMPORTEXPORTFORM I ON I.VAN_ID=L.VAN_ID JOIN IPT_IGMEPCARGOLINES IL ON I.ID=IL.ID AND LL.CARGO_CODE=IL.CARGO_CODE
where L.PORTDETAIL_CODE= P_CODE and I.PORTDETAIL_CODE=L.PORTDETAIL_CODE and IL.IMP_EXP_NAME like '%KKR%' AND LL.END_TIME<=TO_TIMESTAMP(TO_CHAR(P_DATE || ' 06:59'),'dd/MM/yyyy HH:MI')
and L.VAN_ID in (P_VANID )
)t group by CARGO_NAME, IMP_EXP_NAME, 'TEMP_CARGO_Import',DISCHRG_CMNCD_ANCHRG_TM,DELAYS;
END AVG_UNLOADING;
Executing procedure
stkagentlist = "{call AVG_UNLOADING(?,?,?,?}";
callableStatement = conn.prepareCall(stkagentlist);
callableStatement.setString(1, portCode);
callableStatement.setString(2, dt );
callableStatement.setString(3, vanids );
callableStatement.registerOutParameter(4, OracleTypes.CURSOR);
callableStatement.executeUpdate();
stkagentlist = "{call AVG_UNLOADING(?,?,?,?}";
I think in this statement the closing round bracket is missing, it should be:
stkagentlist = "{call AVG_UNLOADING(?,?,?,?)}";
Given my code below:
Query q = em.createNativeQuery(sql.toString(), SearchDTO.class);
for (String k : parameters.keySet()) {
q.setParameter(k, parameters.get(k));
}
q.setFirstResult((criteria.getPage()-1) * criteria.getLimit());
q.setMaxResults(criteria.getLimit());
return q.getResultList();
Where page is > 1, the sql generated is incorrect:
WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as hibernate_row_nr FROM ( select TOP(?) cc.company_id as page0_, cc.long_name as page1_, cc.reuters_org_id as page2_, ccdom.country_name as country_of_domicile, ccinc.country_name as country_of_incorporation, ccr.region_name as region, ci.industry_name as industry from zz_prp_common_company cc left join zz_prp_common_country ccdom on cc.country_of_domicile = ccdom.country_id left join zz_prp_common_region ccr on ccr.region_id = ccdom.region_id left join zz_prp_common_country ccinc on cc.country_of_domicile = ccinc.country_id left join zz_prp_common_industry ci on cc.industry_id = ci.industry_id where 1=1 order by cc.long_name ) inner_query ) SELECT page0_, page1_, page2_, country_of_domicile, country_of_incorporation, region, industry FROM query WHERE hibernate_row_nr >= ? AND hibernate_row_nr < ?
I cannot understand why it is replacing the alias to my columns to page0_, page1_ and page2_. Due to this the where page0_ replaced the company_id column alias, I am getting this error:
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The column name company_id is not valid.
I am using MS SQL Server 2008 R2, and I have setup my hibernate dialect to use org.hibernate.dialect.SQLServer2008Dialect.
I have found the answer to this issue. The sql native query I was using did NOT have any alias on the first 3 columns (company_id, long_name and reuters_org_id). When I debugged the SQLServer2008Dialect, it lead to the method getProcessedSql():
public String getProcessedSql() {
StringBuilder sb = new StringBuilder( sql );
if ( sb.charAt( sb.length() - 1 ) == ';' ) {
sb.setLength( sb.length() - 1 );
}
if ( LimitHelper.hasFirstRow( selection ) ) {
final String selectClause = fillAliasInSelectClause( sb );
int orderByIndex = shallowIndexOfWord( sb, ORDER_BY, 0 );
if ( orderByIndex > 0 ) {
// ORDER BY requires using TOP.
addTopExpression( sb );
}
encloseWithOuterQuery( sb );
// Wrap the query within a with statement:
sb.insert( 0, "WITH query AS (" ).append( ") SELECT " ).append( selectClause ).append( " FROM query " );
sb.append( "WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?" );
}
else {
hasOffset = false;
addTopExpression( sb );
}
return sb.toString();
}
The private method that sets the page0_, page1_, and page2, is done by fillAliasInSelectClause, an excerpt that does this is:
// Inserting alias. It is unlikely that we would have to add alias, but just in case.
alias = StringHelper.generateAlias( "page", unique );
The solution that worked for me is to provide the column alias on the 3 columns that initially did not have any. So basically, you will need to put alias on all your columns.
I am getting the error of not all named parameters have been set. Below is my code.
my SqlQuery which is running fine at mysql prompt, You can refer schema in the question SQL Query
SELECT t.*
FROM (
SELECT #lim := 2,
#cg := ''
) vars,
(select * from Table1 order by product,amount, make) t
WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0
AND (#r := #r - 1) >= 0
AND (#cg := product) IS NOT NULL
ORDER BY
product,amount, make
my java code
try {
context.dbl.startTransaction();
Session session = context.dbl.getSession();
//String sqlQuery = "from com.infibeam.inventoryservice.dbObjects.PopularBrandDO";
String sqlQuery = "SELECT t.* ";
sqlQuery=sqlQuery + "FROM (";
sqlQuery=sqlQuery + "SELECT #lim := 2,";
sqlQuery=sqlQuery + "#cg := ''";
sqlQuery=sqlQuery + ") vars, ";
sqlQuery=sqlQuery + "(select * from Table1 order by product,amount, make) t";
sqlQuery=sqlQuery + " WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0";
sqlQuery=sqlQuery + " AND (#r := #r - 1) >= 0 ";
sqlQuery=sqlQuery + " AND (#cg := product) IS NOT NULL ";
sqlQuery=sqlQuery + " ORDER BY product,amount, make";
//Query query = session.createQuery(sqlQuery);
SQLQuery query = session.createSQLQuery(sqlQuery);
listItems = query.list();
}catch(RuntimeException e) {
e.printStackTrace();
}
Below is the exception i am getting
org.hibernate.QueryException: Not all named parameters have been set: [] [SELECT t.* FROM (SELECT #lim := 2,#cg := '') vars, (select * from Table1 order by product,amount, make) t WHERE CASE WHEN #cg <> product THEN #r := #lim ELSE 1 END > 0 AND (#r := #r - 1) >= 0 AND (#cg := product) IS NOT NULL ORDER BY product,amount, make]
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:291)
at org.hibernate.impl.SQLQueryImpl.verifyParameters(SQLQueryImpl.java:199)
at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:143)
at com.infibeam.weaverbird.helper.PopularBrandFacetHelper.bootstrap(PopularBrandFacetHelper.java:48)
Thanks in advance...
The problem is the assignments with :=, which are by the way no standard SQL.
In SQL after a : always a parameter is expected, like in where value = :param and :param has the be set as a parameter then. Now hibernate is scanning the select and find colons where no set parameters follow.
Solution: Redesign your selection using hibernate standards.
You can use two different HQL queries.
First: Select all product: select distinct product from Table1
Second: For each product you do from Table1 where product = :prod, :prod you set as a parameter with the actual product, and with setMaxResults(2) you can limit the number of rows as you need.
Now it is many selects and not a single one, but nevertheless they might be faster than the single query (the single query is complicated and risks an inefficient search strategy in the database). And a big advantage, now it is purely HQL and so your program is portable to different databases.