I have a query with xpath. The values in the xpath is filled dynamically.
Query:
SELECT app_prof.pk_szid, app_prof.xmldata
FROM tblappprofile AS app_prof
WHERE 'Self' =
CAST((xpath('/ApplicationProfile/ComponentIDs/ComponentID[#Family="Core"]/text()', xmldata))[1] AS TEXT)
For preparedStatement:
SELECT app_prof.pk_szid, app_prof.xmldata
FROM tblappprofile AS app_prof
WHERE ? =
CAST((xpath('/ApplicationProfile/ComponentIDs/ComponentID[#Family= ? ]/text()', xmldata))[1] AS TEXT)
When I use,
preparedStatement.setString(1, "Self");
preparedStatement.setString(2, "Core");
results in org.postgresql.util.PSQLException: The column index is out of range: 2, number of columns: 1
The 'Self' is filled correctly. ? in attribute is not recognized. How to use PreparedStatement for attributes in Xpath?
Question marks inside string literals are not considered as parameter placeholders.
You need to pass the whole XPath expression as a parameter:
WHERE ? = CAST((xpath(?, xmldata))[1] AS TEXT)
another option is to dynamically create a string using the format() function:
where ? = CAST((xpath(format('/ApplicationProfile/ComponentIDs/ComponentID[#Family="%s"]/text()',?), xmldata))[1]
That way you can pass the value for #Familiy as a parameter and still keep the XPath inside the SQL if you want.
Related
I am using POSTGRES SQL JSON.
In json column the value is stored as array which I want to update using SQL query
{"roles": ["Admin"]}
The output in table column should be
{"roles": ["SYSTEM_ADMINISTRATOR"]}
I tried different queries but it is not working.
UPDATE public.bo_user
SET json = jsonb_set(json, '{roles}', to_jsonb('SYSTEM_ADMINISTRATOR')::jsonb, true);
UPDATE public.bo_user
SET json = jsonb_set(json, '{roles}', to_jsonb('["SYSTEM_ADMINISTRATOR"]')::jsonb, true);
ERROR: could not determine polymorphic type because input has type unknown
SQL state: 42804
Kindly help me with the query
but at the moment it is to update the value at 0 index
That can be done using an index based "path" for jsonb_set()
update bo_user
set "json" = jsonb_set("json", '{roles,0}'::text[], '"SYSTEM_ADMINISTRATOR"')
where "json" #>> '{roles,0}' = 'Admin'
The "path" '{roles,0}' references the first element in the array and that is replaced with the constant "SYSTEM_ADMINISTRATOR"' Note the double quotes inside the SQL string literal which are required for a valid JSON string
The WHERE clause ensures that you don't accidentally change the wrong value.
So this worked.
UPDATE public.bo_user
SET json = jsonb_set(json, '{roles}', ('["SYSTEM_ADMINISTRATOR"]')::jsonb, true)
where id = '??';
Oracle DB.
Spring JPA using Hibernate.
I am having difficulty inserting a Clob value into a native sql query.
The code calling the query is as follows:
#SuppressWarnings("unchecked")
public List<Object[]> findQueryColumnsByNativeQuery(String queryString, Map<String, Object> namedParameters)
{
List<Object[]> result = null;
final Query query = em.createNativeQuery(queryString);
if (namedParameters != null)
{
Set<String> keys = namedParameters.keySet();
for (String key : keys)
{
final Object value = namedParameters.get(key);
query.setParameter(key, value);
}
}
query.setHint(QueryHints.HINT_READONLY, Boolean.TRUE);
result = query.getResultList();
return result;
}
The query string is of the format
SELECT COUNT ( DISTINCT ( <column> ) ) FROM <Table> c where (exact ( <column> , (:clobValue), null ) = 1 )
where "(exact ( , (:clobValue), null ) = 1 )" is a function and "clobValue" is a Clob.
I can adjust the query to work as follows:
SELECT COUNT ( DISTINCT ( <column> ) ) FROM <Table> c where (exact ( <column> , to_clob((:stringValue)), null ) = 1 )
where "stringValue" is a String but obviously this only works up to the max sql string size (4000) and I need to pass in much more than that.
I have tried to pass the Clob value as a java.sql.Clob using the method
final Clob clobValue = org.hibernate.engine.jdbc.ClobProxy.generateProxy(stringValue);
This results in a java.io.NotSerializableException: org.hibernate.engine.jdbc.ClobProxy
I have tried to Serialize the Clob using
final Clob clob = org.hibernate.engine.jdbc.ClobProxy.generateProxy(stringValue);
final Clob clobValue = SerializableClobProxy.generateProxy(clob);
But this appears to provide the wrong type of argument to the "exact" function resulting in (org.hibernate.engine.jdbc.spi.SqlExceptionHelper:144) - SQL Error: 29900, SQLState: 99999
(org.hibernate.engine.jdbc.spi.SqlExceptionHelper:146) - ORA-29900: operator binding does not exist
ORA-06553: PLS-306: wrong number or types of arguments in call to 'EXACT'
After reading some post about using Clobs with entities I have tried passing in a byte[] but this also provides the wrong argument type (org.hibernate.engine.jdbc.spi.SqlExceptionHelper:144) - SQL Error: 29900, SQLState: 99999
(org.hibernate.engine.jdbc.spi.SqlExceptionHelper:146) - ORA-29900: operator binding does not exist
ORA-06553: PLS-306: wrong number or types of arguments in call to 'EXACT'
I can also just pass in the value as a String as long as it doesn't break the max string value
I have seen a post (Using function in where clause with clob parameter) which seems to suggest that the only way is to use "plain old JDBC". This is not an option.
I am up against a hard deadline so any help is very welcome.
I'm afraid your assumptions about CLOBs in Oracle are wrong. In Oracle CLOB locator is something like a file handle. And such handle can be created by the database only. So you can not simply pass CLOB as bind variable. CLOB must be somehow related to database storage, because this it can occupy up to 176TB and something like that can not be held in Java Heap.
So the usual approach is to call either DB functions empty_clob() or dbms_lob.create_temporary (in some form). Then you get a clob from database even if you think it is "IN" parameter. Then you can write as many data as you want into that locator (handle, CLOB) and then you can use this CLOB as a parameter for a query.
If you do not follow this pattern, your code will not work. It does not matter whether you use JPA, SpringBatch or plan JDBC. This constrain is given by the database.
It seems that it's required to set type of parameter explicitly for Hibernate in such cases. The following code worked for me:
Clob clob = entityManager
.unwrap(Session.class)
.getLobHelper()
.createClob(reader, length);
int inserted = entityManager
.unwrap(org.hibernate.Session.class)
.createSQLQuery("INSERT INTO EXAMPLE ( UUID, TYPE, DATA) VALUES (:uuid, :type, :data)")
.setParameter("uuid", java.util.Uuid.randomUUID(), org.hibernate.type.UUIDBinaryType.INSTANCE)
.setParameter("type", java.util.Uuid.randomUUID(), org.hibernate.type.StringType.INSTANCE)
.setParameter("data", clob, org.hibernate.type.ClobType.INSTANCE)
.executeUpdate();
Similar workaround is available for Blob.
THE ANSWER: Thank you both for your answers. I should have updated this when i solved the issue some time ago. In the end I used JDBC and the problem disappeared in a puff of smoke!
I have a xpath= //*[#id='00QE000000gQ9fv_ACTION_COLUMN']/a[2]/span
in this xpath 00QE000000gQ9fv is dynamic and _ACTION_COLUMN remains same.
I stored 00QE000000gQ9fv in a String variable as recordId i.e
String recordId = 00QE000000gQ9fv
Now i want a xpath which contains recordId variable.
your xpath will be - //*[contains(#id,'_ACTION_COLUMN')]/a[2]/span
Make sure _ACTION_COLUMN is unique or is the first element in your html so that u grab hold of the right element.
Edit 1 according to your comment.
Try this: //*[contains(#id,'"+recordId +"')]/a[2]/span
or
//*[#id='"+ recordId +"_ACTION_COLUMN']/a[2]/span
I was trying to figure out how can I set multiple parameters for the IN clause in my SQL query using PreparedStatement.
For example in this SQL statement, I'll be having indefinite number of ?.
select * from ifs_db where img_hub = ? and country IN (multiple ?)
I've read about this in
PreparedStatement IN clause alternatives?
However I can't figure it out how to apply it to my SQL statement above.
There's not a standard way to handle this.
In SQL Server, you can use a table-valued parameter in a stored procedure and pass the countries in a table and use it in a join.
I've also seen cases where a comma-separated list is passed in and then parsed into a table by a function and then used in a join.
If your countries are standard ISO codes in a delimited list like '#US#UK#DE#NL#', you can use a rather simplistic construct like:
select * from ifs_db where img_hub = ? and ? LIKE '%#' + country + '#%'
Sormula will work for any data type (even custom types). This example uses int's for simplicity.
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
ArrayListSelectOperation<Inventory> operation =
new ArrayListSelectOperation<Inventory>(inventoryTable, "partNumberIn");
// show results
for (Inventory inventory: operation.selectAll(partNumbers))
System.out.println(inventory.getPartNumber());
You could use setArray method as mentioned in the javadoc below:
http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setArray(int, java.sql.Array)
Code:
PreparedStatement statement = connection.prepareStatement("Select * from test where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"AA1", "BB2","CC3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();
Is there a way to check that value of an element in the XML field has an empty size using SQLXML? Consider I have the following data in the column Conf of the table Test:
<Conf>
<UserData>
<data type="str" value="" />
</UserData>
</Conf>
I can check that data exists by using the following SQL request:
SELECT Test.Conf.exist('/Conf/UserData/data') FROM Test;
But how can I check that data has an empty value? It could be something like the following, but it doesn't work:
SELECT Test.Conf.value('(/Conf/UserData/data/#value)[1]', 'nvarchar(max)')='' FROM Test;
My final solution is to use the following SQL statement:
SELECT Test.Conf.value('string-length(/Conf[1]/UserData[1]/data[1]/#value)', 'int') FROM Test;
Using XPath 1.0. string(#someattribute) test should return false if empty. I know nothing about SQLXML, but it will work if you can use a control sequence.
Possibly this will work.
SELECT Test.Conf.exist('data(/Conf/UserData/data[#value=''''])') FROM Test;
This checks to see if a data element with #value = '' exists.
The problem is not with XPath but with TSQL syntax.
XML.Exist returns a BIT datatype, indicating whether it exists. SQL Server has no native BOOLEAN datatype (a real true/false).
So, your query should have been
SELECT CASE WHEN Test.Conf.value('(/Conf/UserData/data/#value)[1]', 'nvarchar(max)')=''
THEN 1 ELSE 0 END
FROM Test;