I want to attach an eq() conditional statement to booleanExpression using or() as much as the list length.
What should I do?
For example
QStudyTime.studyTime.user.id.eq(rivalId).or(QStudyTime.studyTime.user.id.eq(rivalId2)).or(QStudyTime.studyTime.user.id.eq(rivalId3))
The above code is handled by method chaining when the number of rivaled is 3.
But my system doesn't have a fixed number of rivalId's.
What should I do in this case?
Can it be handled with a for statement?
Or is BooleanExpression a way to attach using the + operator just like String?
I solved it like this.
private BooleanExpression method(input parameter) {
List<Long> rivalIdList = new ArrayList<>();
for (int i = 0; i < rivalList.size(); i++) {
rivalIdList.add(rivalList.get(i).getRival().getId());
}
return QStudyTime.studyTime.user.id.in(rivalIdList);`
}
When we try to fetch data with Null values
field(TABLE_NAME.COLUMN_NAME.in(null))
with IN clause
getting null pointer exception.
Maybe because of this.
#Override
public final Condition in(Collection<?> values) {
Field<?>[] fields = new Field[values.size()];
Iterator<?> it = values.iterator();
for (int i = 0; it.hasNext(); i++)
fields[i] = Tools.field(it.next(), this);
return in(fields);
}
In the database, we can provide null in IN clause.
There is an existing "won't fix" issue in jooq https://github.com/jOOQ/jOOQ/issues/3867
There are some alternatives:
check null before IN(Cant do in my case its a really big select statement)
So if I want to make this possible is there any other workaround.
PS: On a similar note "eq" works perfectly fine:
#Override
public final Condition equal(Field<T> field) {
return compare(EQUALS, nullSafe(field, getDataType()));
}
Edit: 'field(TABLE_NAME.COLUMN_NAME.in(null))' here null is a collection.
Your example code doesn't compile:
TABLE_NAME.COLUMN_NAME.in(null)
There are 5 overloads of this in() method in jOOQ 3.14, and as such, you cannot pass the null literal to the in() method. Your real client code may be using a local variable like this:
Collection<?> collection = null;
TABLE_NAME.COLUMN_NAME.in(collection)
There might be a case for when this should behave the same as passing an empty collection, such as Collections.emptyList(), but this isn't what you seem to want. You probably want to pass actual null values inside of that collection, which you can do:
TABLE_NAME.COLUMN_NAME.in(1, null, 2)
But why would you do it? SQL implements three valued logic, meaning that NULL values have no effect in IN predicates, while they have an unintuitive, hardly desired effect in NOT IN predicates (the entire predicate becomes NULL)
I have just wrote a code to cach a table in the memory (simple java hashmap). Now one of the code that i am trying to replace is the find the objects based on criteria. it receives multiple field parameters and if those fields are not empty and not null, they were being added as part of hibernate query criteria.
To replace this, what i am thinking to do is
For each valid param (not null and no empty) I will create a HashSet which will satisfy this criteria.
Once i am done making hashsets for all valid criteria, I will call Set.retainAll(second_set) on all sets. So that at the end, I will have only that set which is intersection of all valid criteria.
Does it sound like the best approach or is there any better way to implement this ?
EDIT
Though, My original post is still valid and I am looking for that answer. I ended up implementing it in the following way. The reason is that it was kind a cumbersome with sets since after creating all sets, I had to first figure out which set is non empty so that the retainAll could be called. it was resulting in lots of if-else statements. My current implementation is like this
private List<MyObj> getCachedObjs(Long criteria1, String criteria2, String criteria3) {
List<MyObj> results = new ArrayList<>();
int totalActiveFilters = 0;
if (criteria1 != null){
totalActiveFilters++;
}
if (!StringUtil.isBlank(criteria2)){
totalActiveFilters++;
}
if (!StringUtil.isBlank(criteria3)){
totalActiveFilters++;
}
for (Map.Entry<Long, MyObj> objEntry : objCache.entrySet()){
MyObj obj = objEntry.getValue();
int matchedFilters = 0;
if (criteria1 != null) {
if (obj.getCriteria1().equals(criteria1)) {
matchedFilters++;
}
}
if (!StringUtil.isBlank(criteria2)){
if (obj.getCriteria2().equals(criteria2)){
matchedFilters++;
}
}
if (!StringUtil.isBlank(criteria3)){
if (game.getCriteria3().equals(criteria3)){
matchedFilters++;
}
}
if (matchedFilters == totalActiveFilters){
results.add(obj);
}
}
return results;
}
I'm developing a feature that will be used among some of my company's products so I can't have product-specific code.
I have the results of 2 queries stored on 2 JoinRowSet objects (I do have to use JoinRowSet because I have to join other queries' results with these ones later). Then, I need to intersect those 2 JoinRowSets but I don't know what class they "belong" to (I don't know if the query will return People, Contacts, Departments or any other thing). I was told I should use lambda expressions to do this but I'm finding some problems.
I have converted one of the JoinRowSet to a Collection (I don't know how to use lambda expressions directly on a JoinRowSet), then I'm looping over one of the JoinRowSets and, for each column of this JoinRowSet, I want to get the records on the initial Collection that have the same value on that column. But, as I don't know the class of the data, I can't do something like u.getAge() because I don't have the property name.
This is my method:
public static void testLambdas() throws SQLException {
... //set the needed stuff to connect to database and query data
JoinRowSet jrs1 = ... //result from query 1
JoinRowSet jrs2 = ... //result from query 2
Collection<Row> jrs2Collection = (Collection<Row>) jrs2.getRowSets();//collection to use on lambda expression
while (jrs1.next()) {
ResultSetMetaData metadata = jrs1.getMetaData();
for (int j = 1; j <= metadata.getColumnCount(); j++) {
String dataType = metadata.getColumnTypeName(j);
Object colValue = null;
if (dataType.equals("NUMBER")) {
colValue = jrs1.getBigDecimal(j);
} else if (dataType.equals("VARCHAR2")) {
colValue = jrs1.getString(j);
} else if (dataType.equals("DATE")) {
colValue = jrs1.getTimestamp(j);
}
System.out.println(metadata.getColumnName(j)+ " (" + dataType +"): "+ colValue);
Object o = jrs2Collection.stream()
.filter(u -> {
try {
return (u.getColumnObject(j).equals(colValue));
} catch (Exception e) {
e.printStackTrace();
return false;
}
})
.collect(Collectors.toCollection(TreeSet::new));
}
}
}
Please ignore the fact that this use of a lambda expression is inside a while and not being re-used on each iteration. I will take care of that if I can solve the issues I have now.
I had to add the try-catch to the filter because it was returning Unhandled exception type SQLException. After adding this try-catch, I get a Local variable j defined in an enclosing scope must be final or effectively final on u.getColumnObject(j).
I have no idea if what I'm doing is the best way to do this or if I'm doing something really wrong (2 of my colleagues have looked at my code and also have no idea how to solve this).
I would appreciate any inputs on this, please.
It’s not clear what you are trying to do as you are creating collections inside a loop without using them. So I can’t tell you whether there’s a better solution for this.
Regarding the error, the error message is pretty clear. You can’t use a mutable local variable inside a lambda. However, the solution is quite simple: you can assign the content of the mutable variable to an immutable variable:
for (int j = 1; j <= metadata.getColumnCount(); j++) {
// …
int currentJ = j;
Object currentColValue = colValue;
Object o = jrs2Collection.stream()
.filter(u -> {
try {
return (u.getColumnObject(currentJ).equals(currentColValue));
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
)
.collect(Collectors.toCollection(TreeSet::new));
}
Note that currentJ and currentColValue are effectively final which means you could add a final modifier to them without changing the program. It’s a requirement that local variables you use inside a lambda expression are either final or effectively final.
By the way, don’t think about “re-using lambda between iterations”. In most cases, the JRE will take care of reusing the instances created for lambda expressions where possible. And the JDBC operations outweigh any overhead imposed by temporary objects created for lambda expressions anyway.
This question already has answers here:
PreparedStatement IN clause alternatives?
(33 answers)
Closed 3 years ago.
i have a list of names e.g.:
List<String> names = ...
names.add('charles');
...
and a statement:
PreparedStatement stmt =
conn.prepareStatement('select * from person where name in ( ? )');
how to do the following:
stmt.setParameterList(1,names);
Is there a workaround? can someone explain why this method is missing?
using: java, postgresql, jdbc3
This question is very old, but nobody has suggested using setArray
This answer might help https://stackoverflow.com/a/10240302/573057
There's no clean way to do this simply by setting a list on the PreparedStatement that I know of.
Write code that constructs the SQL statement (or better replaces a single ? or similar token) with the appropriate number of questions marks (the same number as in your list) and then iterate over your list setting the parameter for each.
this method is missing due to type erasure the parameter type of the List is lost at runtime. Therefore the need to add several methods arires: setIntParameters, setLongParameters, setObjectParameters, etc
For postgres 9 I have used this approach:
jdbcTemplate.query(getEmployeeReport(), new PreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setTimestamp(1, new java.sql.Timestamp(from.getTime()));
ps.setTimestamp(2, new java.sql.Timestamp(to.getTime()));
StringBuilder ids = new StringBuilder();
for (int i = 0; i < branchIds.length; i++) {
ids.append(branchIds[i]);
if (i < branchIds.length - 1) {
ids.append(",");
}
}
// third param is inside IN clause
// Use Types.OTHER avoid type check while executing query
ps.setObject(3, ids.toString(), **Types.OTHER**);
}
}, new PersonalReportMapper());
In case the questions' meaning is to set several params in a single call...
Because the type validation is already defined in a higher level, I think the only need is for setObject(...).
Thus, a utility method can be used:
public static void addParams(PreparedStatement preparedStatement, Object... params) throws SQLException {
for (int i = 0; i < params.length; i++) {
Object param = params[i];
preparedStatement.setObject(i+1, param);
}
}
Usage:
SqlUtils.addParams(preparedStatement, 1, '2', 3d);
Feel free converting this to a Java 8 lambda :)
I was reviewing code this morning and one of my colleagues had a different approach, just pass the parameter using setString("name1','name2','name3").
Note: I skipped the single quote at the beginning and end because these are going to be added by the setString.
After examining various solutions in different forums and not finding a good solution, I feel the below hack I came up with, is the easiest to follow and code. Note however that this doesn't use prepared query but gets the work done anyway:
Example: Suppose you have a list of parameters to pass in the 'IN' clause. Just put a dummy String inside the 'IN' clause, say, "PARAM" do denote the list of parameters that will be coming in the place of this dummy String.
select * from TABLE_A where ATTR IN (PARAM);
You can collect all the parameters into a single String variable in your Java code. This can be done as follows:
String param1 = "X";
String param2 = "Y";
String param1 = param1.append(",").append(param2);
You can append all your parameters separated by commas into a single String variable, 'param1', in our case.
After collecting all the parameters into a single String you can just replace the dummy text in your query, i.e., "PARAM" in this case, with the parameter String, i.e., param1. Here is what you need to do:
String query = query.replaceFirst("PARAM",param1); where we have the value of query as
query = "select * from TABLE_A where ATTR IN (PARAM)";
You can now execute your query using the executeQuery() method. Just make sure that you don't have the word "PARAM" in your query anywhere. You can use a combination of special characters and alphabets instead of the word "PARAM" in order to make sure that there is no possibility of such a word coming in the query. Hope you got the solution.
Other method :
public void setValues(PreparedStatement ps) throws SQLException {
// first param inside IN clause with myList values
ps.setObject(1 , myList.toArray(), 2003); // 2003=array in java.sql.Types
}