I have a section of code that used to utilize Optional<Department>, but due to some errors I worked out I am now converting it to List<Department>. Obviously this means I now have to change the return types and other method calls. Here are my questions:
I changed my returns to "new LinkedList<>()" (indicated in the code below) but is that correct?
There is a red error under ".isPresent" (indicated in the code below) with error message "The method isPresent() is undefined for the type List<Department>". What should I be changing that to?
Below is the updated code with comments indicating where errors are now occurring. Any help and explanations would be GREATLY appreciated!
public List<Department> delete(String department_ID) {
if ((department_ID == null) || (department_ID.isEmpty())) {
return new LinkedList<>(); //<<<<<<<<<<<<<<< used to be "return Optional.empty();"
}
List<Department> existing = get(department_ID);
if (existing.isPresent()) { //<<<<<<<<<<< red error under ".isPresent()"
String sql = "DELETE employee.*, department.* " + "FROM employee, department "
+ "WHERE employee.department_ID = :department_ID AND department.department_ID = :department_ID;";
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("department_ID", department_ID);
int rows = jdbcTemplate.update(sql, parameters);
if (rows > 0) {
return existing;
}
}
return new LinkedList<>(); //<<<<<<<<<<<<<<< used to be "return Optional.empty();"
}
I changed my returns to "new LinkedList<>()" (indicated in the code below) but is that correct?
I have googled the error message for my ".isPresent" error and cant find any explanations that fit
tl;dr
Change:
if (existing.isPresent()) { …
… to:
if ( ! list.isEmpty() ) { …
Details
You said:
red error under ".isPresent" (indicated in the code below) with error message "The method isPresent() is undefined for the type List".
The variable existing holds a reference to a List object. If you look at the Javadoc for List, you find no method named isPresent. So of course trying to call a non-existent method generates an error from the compiler.
That isPresent method was from the Optional class. The method checks to see if the optional holds a payload or if the optional is empty.
You seem to be switching to a style where you always expect a List, even if the list is empty (no elements).
If you want to be defensive, you can check that the list object exists.
if ( Objects.nonNull( existing ) ) { … }
But you can omit that null check if you are confident that such a condition cannot exist.
You may want to check if the list is empty, to avoid a needless call to your database. If so, change that code to:
if ( ! list.isEmpty() ) // Perform database work only if list has some elements.
You have other issues. Among them:
Generally the convention in Java is to avoid underscores in names. And generally best to avoid ALL-CAPS. So departmentId, not department_ID.
When returning lists, generally best to return an unmodifiable list. If the calling programmer needs a modifiable list, they can easily make from the returned unmodifiable list.
To get an unmodifiable list, use List.of rather than new LinkedList<>().
I cannot understand why your delete method would return a list. You may believe that you are reporting rows that got deleted, but technically you are not.
By the way, a tip: Text blocks can help with embedded SQL.
Instead of returning new LinkedList<>() you could return List.emptyList().
isPresent() is a method of Optional, but you assign the outcome of method get(department_ID) to an instance of List. You can check the List using
if(!(existing == null || existing.isEmpty())) {
Related
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 a class called CompressedCardInfo that has an MtgCard as a member of it, and CompressedCardInfo's (or classes extending it) have overloaded the equals method to take into account checking if a CompressedCardInfo equals an MtgCard.
Now this isn't extremely important, as they are only warnings (they bother me however), but when I have CompressedCardInfo in an ArrayList and call ArrayList.contains or .indexOf I get the warning telling me that the list might not have objects of type MtgCard in it.
How do I suppress this specific warning about this List? Or is there a better way of handling that kind of comparison?
Thanks.
for (Pair<MtgCard, Boolean> card : decklist) {
/* Translate the set code to TCG name of course it's not saved */
card.first.setName = CardDbAdapter.getSetNameFromCode(card.first.setCode, database);
if (changedCardName == null || changedCardName.equals(card.first.mName)) {
if (mCompressedDecklist.contains(card.first)) {
CompressedDecklistInfo existingCard = mCompressedDecklist.get(mCompressedDecklist.indexOf(card.first));
if (existingCard.mIsSideboard == card.second) {
mCompressedDecklist.get(mCompressedDecklist.indexOf(card.first)).add(card.first);
} else {
mCompressedDecklist.add(new CompressedDecklistInfo(card.first, card.second));
}
} else {
mCompressedDecklist.add(new CompressedDecklistInfo(card.first, card.second));
}
if (mShowTotalDecklistPrice) {
loadPrice(card.first.mName, card.first.setCode, card.first.mNumber);
}
}
}
The calls on lines 5 (contains), 6 (indexOf), and 8 (indexOf). I get warnings there because the ArrayList is of type CompressedDecklistInfo (which is extended from CompressedCardInfo. This also happens in other places.
So I'm just going to follow best practices and fix these issues properly.
So I have a method (of which I can't change the parameters, or else this could have been made easier through HashMaps...more on this later), that passes an item as a parameter. Now I have a list of instances from this other class, of which one of its attributes is of the same type of this item, and I want to find the instances in the list which correspond to this item (of which there should be only one). This is what I did to find this:
List<Instance> instances = ...
public static void checkItems(Item i) {
List<Instance> n = new ArrayList<>();
instances.forEach(p -> n.add(p.i == i ? p : null));
Instance currentInstance = n.get(0);
//Instance currentInstance = instances.stream().filter(p -> p.i == i).collect(Collectors.toList()).get(0);
}
You'll probably notice two things straight up:
I used a conditional operator which adds a null value to the list when the condition isn't passed
My commented code which was another attempt to solve this issue
So in the first case, I put null because it requires you to put something, and a null value is probably easier to work with, which is why the question arises: How do I access the first non-null value in a list (without resorting to iterating over the entire list to find it...)?
You may notice that I just assign the first value of the list with n.get(0) to currentInstance, because I know that only one value will have passed the test. However, due to some other code that I apply to currentInstance, this value cannot be null.
Just a note on the second point: the way I tried to solve it with streams actually works exactly as planned, except that for some reason the list of instances recovered is not a direct copy of the original instances. This resulted in the values of some of the attributed to have been reset to default values, therefore rendering this method useless.
EDIT: I just wanted to mention that the streams method wasn't working because of some silly mistake that I made in another class, there was nothing wrong with the code so I'll be using that bit to solve my problem :D
If you know that only one p passes the test, I don't know what the point of creating a list with a load of null values plus p is.
Your problem seems to stem from wanting to use forEach. In my opinion, you should almost always use a for loop in preference to forEach. With a simple for loop you can just use break when the item is found.
In detail:
Instance p = null;
for (Instance q : instances) {
if (q.i == i) {
p = q;
break;
}
}
if (p == null)
throw new IllegalStateException(); // It wasn't there.
// Do something with p.
You could do it this way:
Instance currentInstance = instances.stream()
.filter(p -> p.i == i)
.findFirst()
.get(); // you can use get if you are sure there's one instance
The predicate p -> p.i == i seems suspicious. Why not using equals() instead?
As described, this would normally be solved with streams in the following way:
Optional<Instance> first =
instances.stream().filter(p -> p.i == i).findFirst();
(of which there should be only one)
Of which there definitely is only one, or of which there might be more than one. (And if there's more than one, then what? Is that an error?) It sounds like it might be that you should have a Set<Instance>, not a List<Instance>. Just an observation.
You can do it like
instances.forEach(p -> {
if (p.i == i) n.add(p);
});
In the following code, the ssNo parameter should ensure that only a single Employee satisfies the query condition:
Employee employee = null;
List<Employee> results = (List<Employee>) query.execute(ssNo);
if (results.iterator().hasNext())
{
for (Employee r : results)
{
employee = r;
}
}
return employee;
But if there are multiple results, the for loop ensures that the last Employee in the loop will be returned:
for (Employee r : results)
{
employee = r;
}
Is there a cleaner way to perform this kind of check?
Following the mantra that "less code is good", this code is equivalent to your code, but expressed in much less code and more clearly.
List<Employee> results = (List<Employee>) query.execute(ssNo);
return results.isEmpty() ? null : results.get(results.size() - 1);
It's more common to see the first element returned:
return results.isEmpty() ? null : results.get(0);
Another common pattern is:
if (results.size() > 1)
throw new IllegalStateException("Multiple results found, but at most one was expected");
Note that you can over-abbreviate your code to the point of "encryption", but as long as the code is still clear, less code is always better than more code.
Choosing the last employee is not a good idea, because if you expect to get only one employee, but instead, you get several ones, then you are likely to have a bug in your application or some data integrity problem that will go unnoticed, because you just return an arbitrary one. I'd throw an exception instead.
A clean API would look something like this:
// This returns a list of employees matching your search criteria
// Typical criteria are names, age, salary ranges, etc
// It will never be null, but maybe an empty list
List<Employee> getEmployeesByCriteria(... criteria);
// This will return at most one employee, depending on your search criteria
// Typically, you'll use an ID as criteria. If you don't find the employee
// you can either return null, or throw an exception. If you find several
// employees, then you should always throw an exception.
Employee getEmployeeByCriteria(... criteria) throws SomeException;
A minor simplification could be to remove the if check since the loop will get the last result anyway if the results are more than one.
If the result is only one that the first one is the last one so the if check is not needed.
I have to agree with Lukas Eder. If you're using a database, you should make sure the ssNo field is unique. If you are working only in memory, you should use a Hashtable using the ssNo as a key, and that would throw an exception when you try to insert on a key already used.
In any case, this part of your code is not the place (or this should not be) to check the validity of your datas.
If the query return more the one expected results you should throw an exception that this is not correct.
But if you are 100% sure that this is good way for your case then this look better.
List<Employee> results = (List<Employee>) query.execute(ssNo);
if(results.size() > 0) {
return results.get(results.size() - 1);
}
My managed bean :
public List<String> getLiQuickNav(){
System.out.println("I'm here...");
List<String> l = new ArrayList<String>();
l.add("toto");
l.add("tata");
l.add("titi");
return l;
}
My forEach :
<c:forEach var="categorie" items="#{mainControleur.liQuickNav}">
<h:outputLabel value="${categorie}"/>
</c:forEach>
My output stream :
I'm here...
I'm here...
I'm here...
I'm here...
As you can see "getLiQuickNav( )" is call 4times by my ForEach. But I just wan't to call "getLiQuickNav( )" one time...
How to call it just one time ?
Bonus question:
Why "getLiQuickNav( )" is call 4time whereas I have only 3item "tata,titi,toto" ?
Thank's
You can't control the number of times that getLiQuickNav() is called - consider caching your list so it isn't rebuilt between calls.
private List<String> l;
public List<String> getLiQuickNav()
{
if ( l == null )
{
System.out.println("I'm here...");
l = new ArrayList<String>();
l.add("toto");
l.add("tata");
l.add("titi");
}
return l;
}
As well you should be using <ui:repeat/> rather than <c:forEach/>. See this blog entry for why.
Answer
You're reinitializing the state in a getter every time. That is meant to be an accessor, not a way to initialize state. Don't create the list in getLiQuickNav, create in a constructor or setter.
Bonus
The first time you call getLiQuickNav() you initialize the list, the reference to this list gets returned and stored in a scope to evaluate your expression (.liQuickNav) and then the getLiQuickNav() is called by convention 3 more times for each item in the list.
It should get called once if you return the same list every time. You're returning a new one every time.
Getter's in Java (in any context, to include for Faces Managed Beans) should not generate anything - they should just return a value. Create the list before hand and return it.