I have documents stored in a couchbase lite database. I use the query builder to request these documents in Java.
I would like to to order the retrieved documents given two properties: if one is missing, I'd like to use the value of another for the ordering.
For example, considering these data stored in the couchbase lite:
{
"firstname":"Russell",
"lastname":"Macdonald"
},
{
"firstname":"Brielle"
"birthname":"Vaughn"
"lastname":"Bates"
},
{
"firstname":"Molly"
"birthname":"Arellano"
"lastname":"Nichols"
}
I would like to order by birthname. But if the birthname is missing, the lastname should be used instead. The resulting order would be:
Molly Arellano (married Nichols)
Russell Macdonald
Brielle Vaughn (married Bates)
I tried passing the two successive properties to the order by clause. But, unsurprisingly, it did not work:
List<Result> results = select(all())
.from(database(myDatabase))
.orderBy(Ordering.property("birthname"), Ordering.property("lastname"))
.execute().allResults()
I don't think you can do this with the QueryBuilder interface. SQL+, however, has a function: "IFMISSINGORNULL(arg1, arg2)" whose value is its first argument, if ISMISSINGORNULL is false for that argument and its second argument otherwise. You should be able to use the query:
"select * from _ order by IFMISSINGORNULL(birthname, lastname)"
FWIW, the ResultSet produced by Query.execute() should be closed. It is AutoClosable so you might do something like this:
final Query query = db.createQuery("select * from _ order by IFMISSINGORNULL(birthname, lastname)");
try (ResultSet results = query.execute()) {
// parse the results...
}
Related
I created a PostgreSQL function to get some data from an array of UUID.
i.e:
create function journey_statistics(journey_ids uuid[])
returns TABLE(project_id uuid, project_name character varying,...)
language plpgsql
If I run the next sql statement it returns the expected data:
select * from journey_statistics(array['0f36c7a5-04eb-4329-8e93-a13625a4ffa6'::uuid, 'bc10ee72-7b7f-4bbd-a70a-75477b484d58'::uuid])
But then, when I implement it on Java and run it. I am getting the next error:
o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: function journey_statistics(uuid, uuid) does not exist
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
This is the native query I am using to call it. And I have used same in other similar functions with no errors. So I can not understand what is the issue or what I am doing wrong.
#Query(value = "select cast(project_id as varchar(36)) as projectId, project_name as projectName, cast(project_leader as varchar(36)) as projectLeader" +
" from journey_statistics(:uuids)", nativeQuery = true)
Collection<JourneyStatisticsView> getJourneyStatisticsById(Collection<UUID> uuids);
I have tried to cast data to an array but it looks it is transform to a record[] array.
But more strange is if I pass a Collection of Strings and then I try to cast them I get
function journey_statistics(character varying, character varying) does not exist
Any help appreciated, thank you.
I found a workaround to pass the Collection into the function.
It is not as sophisticated as I wanted. But at least it works.
Basically, I have created a new Repository to use the entity manager and create my own sql statement.
#Repository
public class JourneyStatisticsCustomRepositoryImpl implements JourneyStatisticCustomRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public List getJourneyStatisticsByIds(Collection<UUID> uuids) {
final Collection<String> formattedUUID = uuids.stream().map(uuid -> "cast('" + uuid + "' as uuid)").collect(Collectors.toSet());
final String joinedUUIDs = Strings.join(formattedUUID.iterator(), ',');
return entityManager.createNativeQuery("select cast(project_id as varchar(36)) as projectId, ..." +
"... from journey_statistics(array[" + joinedUUIDs +"])", JourneyStatisticsView.class).getResultList();
}
This sends the proper collection to the function and returns the data as expected.
Hope this could help someone else with similar issues.
This question already has answers here:
PreparedStatement IN clause alternatives?
(33 answers)
Closed 5 years ago.
Say that I have a query of the form
SELECT * FROM MYTABLE WHERE MYCOL in (?)
And I want to parameterize the arguments to in.
Is there a straightforward way to do this in Java with JDBC, in a way that could work on multiple databases without modifying the SQL itself?
The closest question I've found had to do with C#, I'm wondering if there is something different for Java/JDBC.
There's indeed no straightforward way to do this in JDBC. Some JDBC drivers seem to support PreparedStatement#setArray() on the IN clause. I am only not sure which ones that are.
You could just use a helper method with String#join() and Collections#nCopies() to generate the placeholders for IN clause and another helper method to set all the values in a loop with PreparedStatement#setObject().
public static String preparePlaceHolders(int length) {
return String.join(",", Collections.nCopies(length, "?"));
}
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
Here's how you could use it:
private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)";
public List<Entity> find(Set<Long> ids) throws SQLException {
List<Entity> entities = new ArrayList<Entity>();
String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size()));
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
) {
setValues(statement, ids.toArray());
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
entities.add(map(resultSet));
}
}
}
return entities;
}
private static Entity map(ResultSet resultSet) throws SQLException {
Enitity entity = new Entity();
entity.setId(resultSet.getLong("id"));
entity.setName(resultSet.getString("name"));
entity.setValue(resultSet.getInt("value"));
return entity;
}
Note that some databases have a limit of allowable amount of values in the IN clause. Oracle for example has this limit on 1000 items.
Since nobody answer the case for a large IN clause (more than 100) I'll throw my solution to this problem which works nicely for JDBC. In short I replace the IN with a INNER JOIN on a tmp table.
What I do is make what I call a batch ids table and depending on the RDBMS I may make that a tmp table or in memory table.
The table has two columns. One column with the id from the IN Clause and another column with a batch id that I generate on the fly.
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
Before you select you shove your ids into the table with a given batch id.
Then you just replace your original queries IN clause with a INNER JOIN matching on your ids table WHERE batch_id equals your current batch. After your done your delete the entries for you batch.
The standard way to do this is (if you are using Spring JDBC) is to use the org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate class.
Using this class, it is possible to define a List as your SQL parameter and use the NamedParameterJdbcTemplate to replace a named parameter. For example:
public List<MyObject> getDatabaseObjects(List<String> params) {
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
String sql = "select * from my_table where my_col in (:params)";
List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
return result;
}
I solved this by constructing the SQL string with as many ? as I have values to look for.
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
First I searched for an array type I can pass into the statement, but all JDBC array types are vendor specific. So I stayed with the multiple ?.
I got the answer from docs.spring(19.7.3)
The SQL standard allows for selecting rows based on an expression that includes a variable list of values. A typical example would be select * from T_ACTOR where id in (1, 2, 3). This variable list is not directly supported for prepared statements by the JDBC standard; you cannot declare a variable number of placeholders. You need a number of variations with the desired number of placeholders prepared, or you need to generate the SQL string dynamically once you know how many placeholders are required. The named parameter support provided in the NamedParameterJdbcTemplate and JdbcTemplate takes the latter approach. Pass in the values as a java.util.List of primitive objects. This list will be used to insert the required placeholders and pass in the values during the statement execution.
Hope this can help you.
AFAIK, there is no standard support in JDBC for handling Collections as parameters. It would be great if you could just pass in a List and that would be expanded.
Spring's JDBC access supports passing collections as parameters. You could look at how this is done for inspiration on coding this securely.
See Auto-expanding collections as JDBC parameters
(The article first discusses Hibernate, then goes on to discuss JDBC.)
See my trial and It success,It is said that the list size has potential limitation.
List l = Arrays.asList(new Integer[]{12496,12497,12498,12499});
Map param = Collections.singletonMap("goodsid",l);
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);
There are different alternative approaches that we can use.
Execute Single Queries - slow and not recommended
Using Stored Procedure - database specific
Creating PreparedStatement Query dynamically - good performance but loose benefits of caching and needs recompilation
Using NULL in PreparedStatement Query - I think this is a good approach with optimal performance.
Check more details about these here.
sormula makes this simple (see Example 4):
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);
// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
for (Inventory inventory: inventoryTable.
selectAllWhere("partNumberIn", partNumbers))
{
System.out.println(inventory.getPartNumber());
}
One way i can think of is to use the java.sql.PreparedStatement and a bit of jury rigging
PreparedStatement preparedStmt = conn.prepareStatement("SELECT * FROM MYTABLE WHERE MYCOL in (?)");
... and then ...
preparedStmt.setString(1, [your stringged params]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
I put query param for my list services for example:
tablename/list?query=id:10
it is running but I added other param
'personTNo'
tablename/list?query=id:10&personTNo=101035678
id is Integer but personTNo is Long
when I try to this sql returns select * from TABLENAME WHERE personTNo=10L
but this I want to return without 'L' for Long value. It is my code's a bit section in RepositoryCustom class
public List<TABLENAME> getTable(Specification aTablenameSpec) {
CriteriaBuilder builder = mEntityManager.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery();
Root<TABLENAME> root = query.from(TABLENAME.class);
String queryWhere = null;
org.hibernate.query.Query hibernateQuery = null;
Predicate predicate = aTablenameSpec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
query.select(root);
TypedQuery<Object> typedQuery = mEntityManager.createQuery(query);
hibernateQuery = typedQuery.unwrap(org.hibernate.query.Query.class);
String queryString = hibernateQuery.getQueryString();
This row returns with L result, How to remove 'L' value in sql
Use INTEGER() function in the sql query. You can also try CAST() or CONVERT() functions in the query
Based on the problem description and code, it seems safe to assume the tech stack includes: JPA and Spring Data JPA.
And I understand that you want to remove the Long value L suffixes, but it's not clear if that's because the suffixes are causing a problem or exactly why you want the suffixes removed.
I only say that because the example query string appears to be a valid JPA query:
select * from TABLENAME WHERE personTNo = 10L
JPA support for the use of literal values in queries includes support for standard Java numeric (integer/long/float/double) literal value syntax.
Which means the L suffix on the literal Long value of personTNo, as defined in your query (10L), is legitimate, valid, and should not cause a problem.
Please let me know if I've missed the point, made an incorrect assumption, or overlooked something, and I will follow up.
{
"Account1" :
{
Push_key(): { Carplate: "ABC1234" }
Push_key(): { Carplate: "ABC" }
Push_key(): { Carplate: "A" }
}
}
This is how the database looks like.
I would like to retrieve the third data which contains "A" alone ONLY.
I am using startAt() and endAt() for data retrieval:
Query query = ref.child("Account1").orderByChild("Carplate").startAt("A").endAt("A"+"\uf8ff");
But it returns all 3 records. (I think its due to they are all started at "A".)
Need help! Please!
You should look at the equalTo() method for this (from the doc):
The equalTo() method allows you to filter based on exact matches. As is the case with the other range queries, it will fire for each matching child node.
To adapt it to your query you might try:
Query query = ref.child("Account1").orderByChild("Carplate").equalTo("A");
I'm using Hibernate to retrieve some data from a database. The result set returned by the SQL query I'm using, is something like the following:
SourceId | TargetId
1 | 10
1 | 11
1 | 12
2 | 13
2 | 14
Right now I'm having something like the following class:
#NamedNativeQuery(name = "myQuery", resultSetMapping = "myMapping",
query = "select source.id id1, target.id id2
+ "from someTable source "
+ "left join associationTable association on source.id=association.sourceId "
+ "left join someTable target on association.targetId=target.id "
+ "where source.id in(?1)")
#SqlResultSetMapping(name = "myMapping",
entities = #EntityResult(entityClass = DBCalculationElement.class, fields = {
#FieldResult(name = "targetId", column = "targetId"),
#FieldResult(name = "sourceId", column = "sourceId") }))
public class MyClass {
private String sourceId;
private String targetId;
public String getSourceId() {
return sourceId;
}
public String getTargetId() {
return targetId;
}
}
Even though everything is working fine and I'm getting the results I need, in some cases the result set is really huge (thousands of rows) so it takes several minutes to actually get the data. I'm aware that Hibernate is not the best solution performance-wise when it comes to really big result sets, but I'm trying to avoid using raw JDBC.
One workaround would be to have a list of strings as targetId. This way, and using the above resultSet as an example, I would get 2 objects, one with
sourceId = "1"
which also has a list of targetIds containing the following:
targetId = <"10", "11", "12">
and something similar for the second object with sourceId="2".
Does anyone know how can I do this using Hibernate? How can I map several rows to a list in Java?
Once, i solved this problem by writing a stored procedure.
u can write stored proc to retun comma separated values of target id for each source id and in your main query use distinct to get unique sourceId.
Hope, will work for u also.
I'm aware that Hibernate is not the best solution performance-wise when it comes to really big result sets
First of all that is not true.
...in some cases the result set is really huge (thousands of rows)...
This is not huge for a DBMS like postgres, mysql and so on...
You should consider using setMaxResults and setFirstResult for paginating your query. (it is like offset and limit).
And you should take a look at your machine and JVM configurations, if you are getting performance lacks with such a simple query (yes, there is joins. but still, simple).