Proper way to to handle null in getSingleResult aggregate - java

is there any proper way to handle null for example there is no existing record in the Users table, so it wont throw null pointer:

If the record does not exist in the database, do not use getSingleResult(). Looking at the javadoc, you need to be sure the record exists to use getSingleResult()
getSingleResult
java.lang.Object getSingleResult()
Execute a SELECT query that returns a single untyped result.
Returns:
the result
Throws:
NoResultException - if there is no result
NonUniqueResultException - if more than one result
Instead, use getResultList()
public long generateNextId() {
Query query = getEntityManager().createQuery("SELECT MAX(id)+1 from Users");
List<Object> objs = query.getResultList();
return objs.isEmpty() ? 1 : Long.parseLong(objs.get(0));
}
Also, further, I'd use a TypedQuery<Long> if I were you
public long generateNextId() {
TypedQuery<Long> query = getEntityManager().createQuery("SELECT MAX(id)+1 from Users", Long.class);
List<Long> objs = query.getResultList();
return objs.isEmpty() ? 1 : objs.get(0);
}
If, like documented in this question, the query can return null then you can just use a null check or use an Optional#ofNullable wrapper

Related

Criteria API throws exception instead of null [duplicate]

I have an insertOrUpdate method which inserts an Entity when it doesn't exist or update it if it does. To enable this, I have to findByIdAndForeignKey, if it returned null insert if not then update. The problem is how do I check if it exists? So I tried getSingleResult. But it throws an exception if the
public Profile findByUserNameAndPropertyName(String userName, String propertyName) {
String namedQuery = Profile.class.getSimpleName() + ".findByUserNameAndPropertyName";
Query query = entityManager.createNamedQuery(namedQuery);
query.setParameter("name", userName);
query.setParameter("propName", propertyName);
Object result = query.getSingleResult();
if (result == null) return null;
return (Profile) result;
}
but getSingleResult throws an Exception.
Thanks
Throwing an exception is how getSingleResult() indicates it can't be found. Personally I can't stand this kind of API. It forces spurious exception handling for no real benefit. You just have to wrap the code in a try-catch block.
Alternatively you can query for a list and see if its empty. That doesn't throw an exception. Actually since you're not doing a primary key lookup technically there could be multiple results (even if one, both or the combination of your foreign keys or constraints makes this impossible in practice) so this is probably the more appropriate solution.
Try this in Java 8:
Optional first = query.getResultList().stream().findFirst();
I encapsulated the logic in the following helper method.
public class JpaResultHelper {
public static Object getSingleResultOrNull(Query query){
List results = query.getResultList();
if (results.isEmpty()) return null;
else if (results.size() == 1) return results.get(0);
throw new NonUniqueResultException();
}
}
Here's a good option for doing this:
public static <T> T getSingleResult(TypedQuery<T> query) {
query.setMaxResults(1);
List<T> list = query.getResultList();
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
I've done (in Java 8):
query.getResultList().stream().findFirst().orElse(null);
From JPA 2.2, instead of .getResultList() and checking if list is empty or creating a stream you can return stream and take first element.
.getResultStream()
.findFirst()
.orElse(null);
Spring has a utility method for this:
TypedQuery<Profile> query = em.createNamedQuery(namedQuery, Profile.class);
...
return org.springframework.dao.support.DataAccessUtils.singleResult(query.getResultList());
If you wish to use the try/catch mechanism to handle this problem.. then it can be used to act like if/else. I used the try/catch to add a new record when I didn't find an existing one.
try { //if part
record = query.getSingleResult();
//use the record from the fetched result.
}
catch(NoResultException e){ //else part
//create a new record.
record = new Record();
//.........
entityManager.persist(record);
}
Here's a typed/generics version, based on Rodrigo IronMan's implementation:
public static <T> T getSingleResultOrNull(TypedQuery<T> query) {
query.setMaxResults(1);
List<T> list = query.getResultList();
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
There is an alternative which I would recommend:
Query query = em.createQuery("your query");
List<Element> elementList = query.getResultList();
return CollectionUtils.isEmpty(elementList ) ? null : elementList.get(0);
This safeguards against Null Pointer Exception, guarantees only 1 result is returned.
So don't do that!
You have two options:
Run a selection to obtain the COUNT of your result set, and only pull in the data if this count is non-zero; or
Use the other kind of query (that gets a result set) and check if it has 0 or more results. It should have 1, so pull that out of your result collection and you're done.
I'd go with the second suggestion, in agreement with Cletus. It gives better performance than (potentially) 2 queries. Also less work.
Combining the useful bits of the existing answers (limiting the number of results, checking that the result is unique) and using the estabilshed method name (Hibernate), we get:
/**
* Return a single instance that matches the query, or null if the query returns no results.
*
* #param query query (required)
* #param <T> result record type
* #return record or null
*/
public static <T> T uniqueResult(#NotNull TypedQuery<T> query) {
List<T> results = query.setMaxResults(2).getResultList();
if (results.size() > 1) throw new NonUniqueResultException();
return results.isEmpty() ? null : results.get(0);
}
The undocumented method uniqueResultOptional in org.hibernate.query.Query should do the trick. Instead of having to catch a NoResultException you can just call query.uniqueResultOptional().orElse(null).
I solved this by using List<?> myList = query.getResultList(); and checking if myList.size() equals to zero.
Look this code :
return query.getResultList().stream().findFirst().orElse(null);
When findFirst() is called maybe can be throwed a NullPointerException.
the best aproach is:
return query.getResultList().stream().filter(Objects::nonNull).findFirst().orElse(null);
Here's the same logic as others suggested (get the resultList, return its only element or null), using Google Guava and a TypedQuery.
public static <T> getSingleResultOrNull(final TypedQuery<T> query) {
return Iterables.getOnlyElement(query.getResultList(), null);
}
Note that Guava will return the unintuitive IllegalArgumentException if the result set has more than one result. (The exception makes sense to clients of getOnlyElement(), as it takes the result list as its argument, but is less understandable to clients of getSingleResultOrNull().)
Here's another extension, this time in Scala.
customerQuery.getSingleOrNone match {
case Some(c) => // ...
case None => // ...
}
With this pimp:
import javax.persistence.{NonUniqueResultException, TypedQuery}
import scala.collection.JavaConversions._
object Implicits {
class RichTypedQuery[T](q: TypedQuery[T]) {
def getSingleOrNone : Option[T] = {
val results = q.setMaxResults(2).getResultList
if (results.isEmpty)
None
else if (results.size == 1)
Some(results.head)
else
throw new NonUniqueResultException()
}
}
implicit def query2RichQuery[T](q: TypedQuery[T]) = new RichTypedQuery[T](q)
}
So all of the "try to rewrite without an exception" solution in this page has a minor problem. Either its not throwing NonUnique exception, nor throw it in some wrong cases too (see below).
I think the proper solution is (maybe) this:
public static <L> L getSingleResultOrNull(TypedQuery<L> query) {
List<L> results = query.getResultList();
L foundEntity = null;
if(!results.isEmpty()) {
foundEntity = results.get(0);
}
if(results.size() > 1) {
for(L result : results) {
if(result != foundEntity) {
throw new NonUniqueResultException();
}
}
}
return foundEntity;
}
Its returning with null if there is 0 element in the list, returning nonunique if there are different elements in the list, but not returning nonunique when one of your select is not properly designed and returns the same object more then one times.
Feel free to comment.
I achieved this by getting a result list then checking if it is empty
public boolean exist(String value) {
List<Object> options = getEntityManager().createNamedQuery("AppUsers.findByEmail").setParameter('email', value).getResultList();
return !options.isEmpty();
}
It is so annoying that getSingleResult() throws exceptions
Throws:
NoResultException - if there is no result
NonUniqueResultException - if more than one result
and some other exception that you can get more info on from their documentation
I prefer #Serafins answer if you can use the new JPA features, but this is one fairly straight forward way to do it which I'm surprised hasn't been mentioned here before:
try {
return (Profile) query.getSingleResult();
} catch (NoResultException ignore) {
return null;
}
`public Example validate(String param1) {
// TODO Auto-generated method stub
Example example = new Example();
Query query =null;
Object[] myResult =null;
try {
query = sessionFactory.getCurrentSession()
.createQuery("select column from table where
column=:p_param1");
query.setParameter("p_param1",param1);
}
myResult = (Object[])query.getSingleResult();//As your problem occurs here where the query has no records it is throwing an exception
String obj1 = (String) myResult[0];
String obj2 = (String) myResult[1];
example.setobj1(ISSUtil.convertNullToSpace(obj1))
example.setobj2(ISSUtil.convertNullToSpace(obj2));
return example;
}catch(Exception e) {
e.printStackTrace();
example.setobj1(ISSUtil.convertNullToSpace(""));//setting
objects to "" in exception block
example.setobj1(ISSUtil.convertNullToSpace(""));
}
return example;
}`
Answer : Obviously when there is no records getsingleresult will throw an exception i have handled it by setting the objects to "" in the exception block even though it enter the exception you JSON object will set to ""/empty
Hope this is not a perfect answer but it might help
If some needs to modify my code more precisely and correct me always welcome.
Thats works to me:
Optional<Object> opt = Optional.ofNullable(nativeQuery.getSingleResult());
return opt.isPresent() ? opt.get() : null;

How to send empty list to IN clause

I want to use this SQL query:
String hql = "select e from " + Terminals.class.getName() + " e WHERE e.merchantId IN :merchant_ids";
TypedQuery<Terminals> query = entityManager.createQuery(hql, Terminals.class).setParameter("merchant_ids", merchant_ids);
List<Terminals> merchants = query.getResultList();
But I get error: the right syntax to use near ') So IN clause list into IN (....) can't be empty. Is there some solution to this problem?
It is allowable and even very fine not executing the query:
if (merchant_ids.isEmpty()) {
return new ArrayList<>();
} else {
String hql = "select e from " + Terminals.class.getName()
+ " e WHERE e.merchantId IN :merchant_ids";
return entityManager.createQuery(hql, Terminals.class)
.setParameter("merchant_ids", merchant_ids)
.getResultList();
}
I do not know what would happen if one would pass null instead of an empty list;
SQL ... IN NULL could do. On the other hand it might do a full table scan in order to return 0 results.
If x IN() would not result in 0 records (when there is an OR ...) then:
if (merchant_ids.isEmpty()) {
merchant_ids.add(-1);
String hql = "select e from " + Terminals.class.getName() + ...
Very often, I used to stuck this kind of case. I couldn't find out a proper solution. Since you are using Spring JPA But I have some workaround to suggest to you.
Implement EntityManger and create your SQL queries in runtime. So you can populate your where cause and everything.
Like this: entityManager.createNativeQuery(sql.toString())
Implement if-else block. Check if the list is empty or not, if false call actual query (with IN block) or else write another query without IN block.
Again I am telling, this may not be a proper solution. But I see this is proper workaround.
I am not familiar with hibernate but since it is an SQL error, the following should work :
TypedQuery<Terminals> query = entityManager
.createQuery(hql, Terminals.class)
.setParameter("merchant_ids",merchant_ids.size()==0?null:merchant_ids);
But as #Richard Barker mentioned , best solution is to not even execute the query when the list is empty.
You will even save on the unnecessary database call , when you already know that the query is not going to return anything.
I followed #Rambler's suggestion and created a method to return a null:
public static <T> Collection<T> nullIfEmpty(Collection<T> collection) {
return (collection == null || collection.isEmpty()) ? null : collection;
}
This was easier to add in place, but I agree that it is better to not make the call to the database.

org.springframework.orm.hibernate4.HibernateQueryException: Not supported for DML operations [UPDATE]

I am trying to update a record in the database and I getting this error
org.springframework.orm.hibernate4.HibernateQueryException: Not supported for DML operations [UPDATE com.xxx.models.User u set u.notifiable = true WHERE u.emailAccess = :emailAccess AND u.isAdmin = false]; nested exception is org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [UPDATE com.xxx.models.User u set u.notifiable = true WHERE u.emailAccess = :emailAccess AND u.isAdmin = false]
This is my hql attempt
#Modifying
public User updateUser(String emailAccess) {
String hql = "UPDATE User u set u.notifiable = true WHERE u.emailAccess = :emailAccess AND u.isAdmin = false";
return (User) _sessionFactory.getCurrentSession().createQuery(hql).setParameter("emailAccess", emailAccess).list();
}
After researching, I added the #Modifying annotation to the top of the method but the error still persists. Please what could be wrong?
#Modifying is a Spring Data annotation and you do not appear to be using Spring Data so that is of no use to you.
You need to call executeUpdate() and make the return method void as executeUpdate
Returns: The number of entities updated or deleted..
https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Query.html#executeUpdate()
or return the result of a second query.
public void updateUser(String emailAccess) {
String hql = "UPDATE User u set u.notifiable = true WHERE u.emailAccess = :emailAccess AND u.isAdmin = false";
_sessionFactory.getCurrentSession().createQuery(hql).setParameter("emailAccess", emailAccess).executeUpdate();
}
You should invoke executeUpdate() on your query when you're going to update or delete your entities.
list() method which you're using is to select entities by given query, not to update them.
Your method signature and return types are incorrect. You can never be sure that update operation will actually update only a particular user. executeUpdate() method returns number of rows affected by your query, not the updated entity.
Moreover even list() from your original answer won't return a particular user. It (in case of select query) will return a list of users according to your conditions.

How to properly determine whether an "exists" JPA Criteria Query clause returned true or false?

I don't know how to perform a JPA criteria query that returns with a boolean output.
The goal is to have a criteria query that looks like this when rendered on Oracle:
select 1 from dual where exists ( ... );
The where exists (...) part I performed with a subquery. I'm struggling with the external query.
The practical use of this is to determine whether that subquery in the exists clause returns true or false.
This is what I've written:
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Object> query = criteriaBuilder.createQuery();
query.from(Boolean.class);
query.select(criteriaBuilder.literal(true));
Subquery<Location> subquery = query.subquery(Location.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(subRootEntity);
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
TypedQuery<Object> typedQuery = em.createQuery(query);
The last line outputs an error, stating that "Boolean is not an entity". I think my issue is not knowing how to express the "from" part of the query so that the result outputs 1 or 0/ true or false - not an entity.
I know I could retrieve any entity and then check if the list of results has size of 1.
I'm asking how to get a boolean result, both to avoid the unnecessary task of retrieving those columns and also to learn how to do it.
Is this possible at all?
Thanks!
Eduardo
Yes, this is possible. Assuming that you have an entity corresponding to your dual table, you will want to use that entity class in CriteriaQuery#from.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Boolean> query = criteriaBuilder.createQuery(Boolean.class);
query.from(dual.class);
query.select(criteriaBuilder.literal(true));
Subquery<Location> subquery = query.subquery(Location.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(subRootEntity);
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
TypedQuery<Boolean> typedQuery = em.createQuery(query);
Hibernate 5 is working:
Subquery<Integer> subquery = query.subquery(Integer.class);
Root<Location> subRootEntity = subquery.from(Location.class);
subquery.select(criteriaBuilder.literal(1));
Path<?> attributePath = subRootEntity.get("State");
Predicate predicate = criteriaBuilder.equal(attributePath, criteriaBuilder.literal("TX"));
subquery.where(predicate);
query.where(criteriaBuilder.exists(subquery));
You could do a select for one property (e.g. the ID) and set the max results returned to 1 so that you make sure the DB does not do more work than necessary (like counting all instances). Then your results list will either be empty (exists = false) or have one element (exists = true).
I know this is an older question, but for anyone else looking: How about trying to use Spring's jpa #Query annotation and a select case query (depending on your db implementation) with a method that returns a boolean. For example (MySQL):
#Query("SELECT CASE WHEN COUNT(l) > 0 THEN TRUE ELSE FALSE END FROM Location l WHERE l.state=?1")
boolean locationForStateExists(String state);
Sometimes just using the query string in #Query can be a life saver when the named JPA or query builder methods don't quite do what you want them to.
The maximum optimized and full solution for existance checking via Criteria API based on dimo answer and with help of user3158918 answer:
EntityManager em = ...;
val cb = em.getCriteriaBuilder();
val query = cb.createQuery(Integer.class);
val root = query.from(YourEntity.class);
val predicate = cb.equal(root.get(attribute), attributeValue);
query.select(cb.literal(1)).where(predicate);
return !em.createQuery(query).setMaxResults(1).getResultList().isEmpty();
I used a lombok to be clean.
This code generates SQL query:
SELECT 1 FROM your_entity_table WHERE your_entity_table.attribute = attributeValue LIMIT 1
Please post your solution, if its faster...
I think the problem is the the query.from(Boolean.class). It tries to create a "select object from boolean" query. If you want a boolean as return type you need to use
CriteriaQuery<Boolean> query = criteriaBuilder.createQuery(Boolean.class)
Then query from any existing entity table to create a valid query (perhaps from the subquery's table). I don't think that create from dual works except if you managed to map the dual table.
Is there any reason that all the logic has to be in JPA? If not, why not use SELECT COUNT and then a conditional to set the boolean?
Boolean exists = false;
int count = selectCountQuery();
if (count > 0) {
exists = true;
}

Type mismatch: cannot convert from List to ILogin

private ILogin results;
public ILogin authenticate(Login login) {
System.out.println(login);
System.out.println(login.getEmail());
String query = "SELECT email, id FROM Login WHERE email='"
+ login.getEmail() + "' AND password='" + login.getPassword() + "'";
results = getHibernateTemplate().find(query);
System.out.println(results);
return results;
}
How do i change results = getHibernateTemplate().find(query); I get the error in this line. But i want that in ILogin type not of List type. How do i do an Type Conversion here.
The problem isn't the type conversion per se - it's that you've executed a query which may return multiple results, but you only want a single result.
You probably want to check that the resulting list has exactly one value (0 means login not found; more than 1 probably means there's a problem in your data somewhere) and then return that first value (return (ILogin) list.get(0);).
As a slightly separate matter, you shouldn't be including the data directly in your query like that, IMO. Use query parameters, which is pretty easy in Hibernate:
String query = "SELECT email, id FROM Login WHERE email=? AND password=?";
Object[] parameters = { login.getEmail(), login.getPassword() };
List results = getHibernateTemplate().find(query, parameters);
if (results.size() != 1) {
// Probably throw an exception
}
// I'm assuming your mapping has been set up appropriately such that
// the returned value will *be* an `ILogin`.
return (ILogin) results.get(0);
Finally, you almost certainly don't want results to be an instance variable - it should probably be a local variable, as per my example above.
The dummy way is to use getHibernateTemplate().find(query).get(0); but this will result in exception in case when no such login found.
As Jon said, check your query for emptiness.
I assume spring :).
List resultsList = getHibernateTemplate().find(query);
if ( resultsList.size() == 1 ) {
results = (ILogin)resultsList.get(0);
} else {
// error no entity or mutiple entities
}
return results.
This should work.

Categories

Resources