Can we get single object from hql query? - java

I am writing a select query in hql , my task is to activate the bus. First I will get a messege from client as busId#busStatus, so first I look for this perticular busId is active or inactive So I have to write select query but in hibernate query.list() returns list. Here I think list is unnecessary , a single object is enough .
Here is my code ,
String hql="from BusDetailBean where Busid= :busId and bus_status=:busStatus";
Query query = session.createQuery(hql);
query.setParameter("busId", busId);
query.setParameter("busStatus", busStatus);
List<BusDetailBean> busDetails=(List<BusDetailBean>)query.list();
if(busDetails.isEmpty())
{
//my other stuff
}
else
{
//bus ativation stuff
}
My question is the select query returns only one object if list is not empty I have to use for loop in else part. So how can I optimise this code. can anyone help me in this.

You can use query.getSingleResult()

You can use query.setMaxResults(1);

You can get the object at index 0 in the list:
List l = query.list()
if (l.size()>0) {
return l.get(0)
}

I don't think persitence should be mixed with business logic.
What about returning Optional from the persitence layer and whether result is present/absent do something in higher level?
In persistance layer something like:
return query.list()
.stream()
.findFirst()
This, according to docs, will return first result or empty optional if the collection was empty.
And then:
Optional<Bus> optionalBus = repository.find(busId, busStatus);
if (optionalBus.isPresent()) {
something here
} else {
something else
}

by using query.uniqueResult() you don't ensure that if you have many results , then you will get only one of them.
With uniqueResult() you place a guard/contraint at your result set to be aware that this query should always return a unique result.

For this type of problem, the out of the box solution in Hibernate is to use the uniqueResult() method in the Query class:
public Object uniqueResult()
From the Hibernate JavaDocs:
Convenience method to return a single instance that matches the query,
or null if the query returns no results.
Returns: the single result or null
Throws:
NonUniqueResultException - if there is more than one matching result
HibernateException

Related

How to use ScrollableResults for Hibernate Queries when joining many different entities

I am using Spring Boot endpoints to return results from database queries. It works fine when using getResultList() on the TypedQuery. However I know I will have to managed very large data sets. I am looking into using ScrollableResults via hibernate but I cannot figure out how to actually reference the contents of each row.
StatelessSession session = ((Session) entityManager.getDelegate()).getSessionFactory().openStatelessSession();
criteriaQuery.multiselect(selections);
criteriaQuery.where(predicates.toArray(new Predicate[]{}));
Query<?> query = session.createQuery(criteriaQuery);
query.setMaxResults(5);
query.setFetchSize(1000);
query.setReadOnly(true);
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
while(results.next()){
Object row = results.get();
}
results.close();
session.close();
I have tried results.get(0), results.get(0)[0], results.getLong(0), Object[] row vs Object row, etc. With and without toString() on all of the options. Nothing I do gets more out of the row than the java object reference. I've tried casting as well and get a "cannot cast error". Sometimes I get an error, "query specifies a holder class". Not sure what that means because my criteria query is built by joining 1 or more entities where the entities and selected columns are not known before hand. So I am not actually specifying a class. They entities and selects are specified by user input. Any thoughts? Thanks!
UPDATE:
I can do System.out.println(scroll.getType(0)); and in this case observe a long.
But when I try to save that long (.getLong(0)) I get the error, "query specifies a holder class". Or again the cannot cast error.
Got it figured out. queryDetails is a CriteriaQuery<Tuple>
StatelessSession session = entityManagers.get("DatasourceName").unwrap(Session.class).getSessionFactory().openStatelessSession();
Stream<Tuple> resultStream = session.createQuery(queryDetails)
.setReadOnly(true)
.setMaxResults(100)
.setFetchSize(1000)
.setCacheable(false)
.getResultStream();
Iterator<Tuple> itr = resultStream.iterator();
while (itr.hasNext()){
//Get the next row:
Tuple row = itr.next();
}
A CriteriaQuery that uses multiselect produces an Object[] or javax.persistence.Tuple as result type. Maybe you should try debugging to see the actual object type and from there you can work further.
If you are processing and returning all rows anyway, there is no need to use the ScrollableResults API as you will have to create objects for all rows anyway. If your use case is to do some kind of aggregation, I would recommend you use an aggregate function instead and let the database do the aggregation.

JOOQ Insert List of records

Currently i am mapping from list of pojos to Record, and i want to be able to insert multiple rows at once. how can i do that in JOOQ with one transaction?
List<Record> recordList = receiverList.stream().map(r -> {
return dslContext.newRecord(Table, r);
}).collect(Collectors.toList());
I have tried put the list in the "values", but getting exception "The number of values must match the number of fields"
dslContext.insertInto(Table).values(recordList);
Your error is because .values(...) is waiting for field values not Record.
Maybe you can do a batch execution :
dslContext.batchInsert(recordList);
As Lukas mentioned it, it will execute it in a single jdbc statement which is atomic
instead of batchInsert you can also do:
var insertStepN = dslContext.insertInto(Table).set(dslContext.newRecord(Table, recordList.get(0));
for (var record : recordList.subList(1, recordList.size()) {
insertStepN = insertStepN.newRecord().set(dslContext.newRecord(Table, record));
}
insertStepN.returning().fetch().into(YourClass.class);
this way you can get the inserted values back using returning(), which you won't get with batchInsert().

Eclipselink Expression filter OR clause

I'm having problems creating a proper query using an eclipselink expression when it comes to needing an OR clause. This is my code:
Expression filter = /* filter is not null */;
if (secondStatus != null) {
Integer second = ticketService.getStatusIdByName(secondStatus);
filter = filter.and(
builder.get(Complaint.propertyName.status)
.equal(second)
.or(builder.get(Complaint.propertyName.status)
.equal(status)));
// Also tried the below
// filter = filter.or(builder.get(Complaint.propertyName.status).equal(second));
}
The first way only returned a query including one of the statuses, meanwhile the second (commented out) way returned it's own subquery. Both ways I was getting incorrect results. What is the correct way to incorporate an OR clause into a query?
I figured out what I was doing wrong. I was missing the initial filter in the second statement assuming it would produce the proper SQL automatically.
filter = filter.and(builder.get(Complaint.propertyName.status)
.equal(second)
.or(filter.and(builder.get(Complaint.propertyName.status)
.equal(status))));
This works as intended.

Why is this Java query failing? returning 0 when there are results

Hi can anyone tell me why this java query is failing?
Query q = entityManager.createNativeQuery("SELECT m.* FROM MdmAudit m WHERE m.correlationID = :correlationId AND m.verb = :verb", MdmAuditDAO.class);
//Query q = entityManager.createNamedQuery("MdmAuditDAO.GetData");
q.setParameter("correlationId", resp.getHeader().getCorrelationID());
q.setParameter("verb", resp.getHeader().getVerb());
long result = (long) q.getFirstResult();
The namedQuery:
#NamedQuery( name="MdmAuditDAO.GetData", query="SELECT m FROM MdmAuditDAO m WHERE m.correlationId = :correlationId AND m.verb = :verb")
public class MdmAuditDAO implements Serializable {
I have getters and setter in my MdmAuditDAO class, and I have checked the naming of the variables, and they are identical as in the NamedQuery, so the problem does not lie there.
My problem is that I have three entries in my database, I should at least get one answer back but I get 0 in my result.
MdmAuditDAO is defined in my persistence.xml and in my ehcache.xml. So why is it that the result I get returned is 0? I have also tried to get an object returned or a list of objects, and it is the same result, nothing gets returned, but when I run my query in my mssql database I get results see picture below. It has nothing to do with the m.* I aslo get results when I use that in my SELECT statement.
EDIT 1: This is what I get from my hibernate log, and I do not know how to read this?
Hibernate:
select
mdmauditda0_.id as id1_7_,
mdmauditda0_.correlationID as correlat2_7_,
mdmauditda0_.messageID as messageI3_7_,
mdmauditda0_.meter_no as meter_no4_7_,
mdmauditda0_.noun as noun5_7_,
mdmauditda0_.payload as payload6_7_,
mdmauditda0_.source as source7_7_,
mdmauditda0_.subtype as subtype8_7_,
mdmauditda0_.time as time9_7_,
mdmauditda0_.verb as verb10_7_
from
MdmAudit mdmauditda0_
where
mdmauditda0_.correlationID=?
Anything I have to set, to get more information? I am using the following jars
And my java version is 1.7.0_79.
I found the solution http://www.objectdb.com/api/java/jpa/Query/getFirstResult returns the position of the first element, but I was a bit confused by the phrase
Returns 0 if setFirstResult was not applied to the query object.
Could not get my head around it to make any sense of it.
My solution now is that I just return a list of objects
Query q = entityManager.createNativeQuery("SELECT m.* FROM MdmAudit m WHERE m.correlationId = :correlationId AND verb = :verb", MdmAuditDAO.class);
//Query q = entityManager.createNamedQuery("MdmAuditDAO.GetData");
q.setParameter("correlationId", resp.getHeader().getCorrelationID());
q.setParameter("verb", resp.getHeader().getVerb());
List<MdmAuditDAO> mdmAuditList = q.getResultList();
And then it works fine and I get results. So instead of the the result == 0 check I am doing later in my code I just do a NULL and isEmpty() check instead().
Side note: I have not tried to delete entries and then see what the result would be then in the q.getFirstResult() call but that would be a possibility and see what i get returned and then check on that value, properbly null?

org.hibernate.NonUniqueResultException: query did not return a unique result: 2?

I have below code in my DAO:
String sql = "SELECT COUNT(*) FROM CustomerData " +
"WHERE custId = :custId AND deptId = :deptId";
Query query = session.createQuery(sql);
query.setParameter("custId", custId);
query.setParameter("deptId", deptId);
long count = (long) query.uniqueResult(); // ERROR THROWN HERE
Hibernate throws below exception at the marked line:
org.hibernate.NonUniqueResultException: query did not return a unique result:
I am not sure whats happening as count(*) will always return only one row.
Also when i run this query on db directly, it return result as 1. So whats the issue?
It seems like your query returns more than one result check the database. In documentation of query.uniqueResult() you can read:
Throws: org.hibernate.NonUniqueResultException - if there is more
than one matching result
If you want to avoid this error and still use unique result request, you can use this kind of workaround query.setMaxResults(1).uniqueResult();
Hibernate
Optional findTopByClientIdAndStatusOrderByCreateTimeDesc(Integer clientId, Integer status);
"findTop"!! The only one result!
I don't think other answers explained the key part: why "COUNT(*)" returns more than one result?
I just encountered the same issue today, and what I found out is that if you have another class extending the target mapped class (here "CustomerData"), Hibernate will do this magic.
Hope this will save some time for other unfortunate guys.
Generally This exception is thrown from Oracle when query result (which is stored in an Object in your case), can not be cast to the desired object.
for example when result is a
List<T>
and you're putting the result into a single T object.
In case of casting to long error, besides it is recommended to use wrapper classes so that all of your columns act the same, I guess a problem in transaction or query itself would cause this issue.
It means that the query you wrote returns more than one element(result) while your code expects a single result.
Received this error while doing otherwise correct hibernate queries. The issue was that when having a class extend another hibernate was counting both. This error can be "fixed" by adding a method to your repository class.
By overriding the class count you can manually determine the way this is counted.
#Override
public Integer count(Page<MyObject> page) {
// manual counting method here
}
I was using JPQL and wanted to return Map. In my case, the reason was that I wanted to get Map<String, String>, but had to expect List<Map<String, String>> :)
Check your table, where one entity occurring multiple time's.
I had the same error, with this data :
id
amount
clientid
createdate
expiredate
428
100
427
19/11/2021
19/12/2021
464
100
459
22/11/2021
22/12/2021
464
100
459
22/11/2021
22/12/2021
You see here clientid occurring two times with 464.
I solved it by deleting one row :
id
amount
clientid
createdate
expiredate
428
100
427
19/11/2021
19/12/2021
464
100
459
22/11/2021
22/12/2021
I have found the core of the problem:
result of SELECT COUNT(*) can be a list, if there is a GROUP BY in the query,
and sometimes Hibernate rewrite your HQL and put a GROUP BY into it, just for fun.
Basically your query returns more than one result set.
In API Docs uniqueResult() method says that
Convenience method to return a single instance that matches
the query, or null if the query returns no results
uniqueResult() method yield only single resultset
Could this exception be thrown during an unfinished transaction, where your application is attempting to create an entity with a duplicate field to the identifier you are using to try find a single entity?
In this case the new (duplicate) entity will not be visible in the database as the transaction won't have, and will never be committed to the db. The exception will still be thrown however.
Thought this might help to someone, it happens because "When the number of data queries is greater than 1".reference
As what Ian Wang said, I suspect you are using a repository from spring. And a few days ago you just copy past a class and forgot to delete it when it is finally unused. Check that repository, and see if there is multiple same class of table you use. The count is not the count of rows, but the count of the table problem.
This means that orm technology is not preprogrammed to give you which results you are looking for because there are too many of the same results in the database. for example If there is more than one same value in my database and I want to get it back, you will encounter the error you get with the select query.
For me the error is caused by
spring.jpa.hibernate.ddl-auto=update
in application.properties file replacing it with
spring.jpa.hibernate.ddl-auto=create solved the issue, but it still depends on your needs to decide which configuration you need in your project, for more insights on the topic check this.
First you must test the query list size; here a example:
long count;
if (query.list().size() > 0)
count=(long) criteria.list().get(0);
else
count=0;
return count;

Categories

Resources