I want to write a method that returns a list of last added objects grouped by field 'serviceId'.
The following HQL works, but I want to write this using Criteria API:
FROM Notification WHERE date IN
(SELECT MAX(date) FROM Notification GROUP BY serviceId)
ORDER BY date ASC
Something like this:
Criteria crit = session.createCriteria(Notification.class);
crit.add(Restrictions.in("date", <MAX dates>));
criteria.addOrder(Order.desc("date"));
Thanks in advance.
EDIT:
Now I need a similar query that works using eclipselink API =/
Basically, I need the last N rows (max date), which status is one of the five described bellow, grouped by serviceId column.
Due to my inexperience, it was the best I could:
ExpressionBuilder builder = new ExpressionBuilder();
Expression exStatus1 = builder.get("status").equal(MessageType.START.toString());
Expression exStatus2 = builder.get("status").equal(MessageType.RUNNING.toString());
Expression exStatus3 = builder.get("status").equal(MessageType.PAUSED.toString());
Expression exStatus4 = builder.get("status").equal(MessageType.END_ERROR.toString());
Expression exStatus5 = builder.get("status").equal(MessageType.END_SUCCESS.toString());
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Notification.class);
query.setSelectionCriteria(((exStatus1).or(exStatus2).or(exStatus3).or(exStatus4).or(exStatus5)));
query.setMaxRows(listSize);
query.addDescendingOrdering("date");
The clause to avoid duplicates serviceIds in result rows is missing...
You're going to want to use the Criteria projections API with a detached subquery:
Criteria crit = session.createCriteria(Notification.class, "main");
DetachedCriteria notificationSubQuery = DetachedCriteria.forClass(Notification.class, "sub");
notificationSubQuery.setProjection(Projections.max("date"));
notificationSubQuery.add(Restrictions.eqProperty("sub.serviceId", "main.serviceId"));
crit.add(Subqueries.propertyIn("date", notificationSubQuery));
crit.addOrder(Order.desc("date"));
This mirrors the technique you are using in your HQL query.
EDIT:
I updated the query to match serviceId between your main notification class and your sub query, essentially the same as this HQL Query:
FROM Notification main WHERE date IN
(SELECT MAX(sub.date) FROM Notification sub WHERE sub.serviceId = main.serviceId)
ORDER BY date ASC
This prevents the case where you would have a non-maximum date matching between two different serviceIds like so:
serviceId = 1: date = 3,4,5
serviceId = 2: date = 4,5,6
Old query return:
serviceId: 1, date: 5
serviceId: 2, date: 5,6
New query return:
serviceId: 1, date: 5
serviceId: 2, date: 6
Let me know if this works for you.
Related
I am using the below #Query annotation to get the first few record from MS-SQL. It's showing error saying "< operator > or AS expected..."
#Query("SELECT Top 1 * FROM NEVS010_VEH_ACTV_COMMAND C WHERE C.EVS014_VIN = :vin ORDER BY C.EVS010_CREATE_S DESC")
CommandStatus findCommandStatusByVinOrderByCreatedTimestampDesc(#Param("vin") String vin);
You can also use findFirst and findTop as mentioned in the Docs:
findFirstByVinOrderByCreatedTimestampDesc(String vin)
Since the query is SQL (and not JPQL) one needs to set nativeQuery = true in the annotation:
#Query(nativeQuery = true, value = "SELECT Top 1 * FROM NEVS010_VEH_ACTV_COMMAND C WHERE C.EVS014_VIN = :vin ORDER BY C.EVS010_CREATE_S DESC")
CommandStatus findCommandStatusByVinOrderByCreatedTimestampDesc(#Param("vin") String vin);
For custom Queries without using nativeQuery, the field ROWNUM can be used.
Ex (in Kotlin but the same idea works in Java):
#Query("""
SELECT a
FROM Account a
WHERE a.bla = :ble
AND ROWNUM = 1
ORDER BY a.modifiedDate DESC
""")
fun findWhatever(#Param("ble") someParam: String)
I haven't found that on any doc so far. I just tested and it worked for Oracle, MySQL and H2
I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;
Below is mysql query which is working fine and giving me expected results on mysql console.
select * from omni_main as t where t.date_time BETWEEN STR_TO_DATE(CONCAT('2011', '08', '01'),'%Y%m%d') AND LAST_DAY(STR_TO_DATE(CONCAT('2012', '08','01'), '%Y%m%d')) group by year(date_time),month(date_time)
I need its JPA equivalent query. Below is what I am trying but its returning nothing.
String queryStr = "select * from OmniMainEntity o where o.dateTime BETWEEN STR_TO_DATE(CONCAT('"+fromYear+"', '"+fromMonth+"','01'), '%Y%m%d') AND "
+"LAST_DAY(STR_TO_DATE(CONCAT('"+toYear+"', '"+toMonth+"','01'), '%Y%m%d'))";
Query query = manager.createQuery(queryStr);
System.out.println("Result Size: "+query.getResultList().size());
Here fromYear, fromMonth, toYear, toMonth are method parameters using in creating queryStr.
Please suggest where I may wrong!
Any other way to achieve goal is also welcome!
As you are using JPA Query, it would be better to not use database-specified sql function, such as STR_TO_DATE.
You can have a try by this way.(A Hibernate way, JPA should be similiar):
First, you can parse a java.util.Date object from "fromYear" and "fromMonth" like below:
DateFormat df = new SimpleDateFormat("yyyyMMdd");
Date startDate = df.parse(fromYear + "" + fromMonth + "01");
Date endDate = df.parse(.....);
Then, set them into the JPA query.
String queryStr = "select * from OmniMainEntity o where o.dateTime BETWEEN :startDate AND :endDate)"; // The query now changed to database independent
Query query = manager.createQuery(queryStr);
query.setDate("startDate", startDate);
query.setDate("endDate", endDate);
At last, doing the search:
System.out.println("Result Size: "+query.getResultList().size());
Your query doesn't have a verb in it. You probably want SELECT in there:
SELECT o FROM OmniMainEntity o WHERE...
Also, you should be using parameterized and typed queries, and it's usual to use short names (o instead of omniMainEnt) to make your queries readable.
How can we implement the Hibernate sqlprojection in my query?
Here is my query
SELECT sum(total_amount) as total,created_at from order where created_at < DATE_SUB(curdate(), INTERVAL 7 DAY) and doctor_id = 193 GROUP BY created_at
I have implement DATE_SUB function using sqlRestriction like this:
String sqlWhere = "created_at > DATE_SUB(curdate(), INTERVAL "+activityGraph+" DAY) AND doctor_id = "+id +" GROUP BY created_at";
Criteria criteria = Hibernatesession.createCriteria(Order.class);
criteria.add(Restrictions.sqlRestriction(sqlWhere));
But I don't know how I get the sum of a field using Hibernate query.
I found out that setProjection in Hibernate is used to get the sum as we desired but I don't know how to use it. Also here I want to use sqlRestriction to write WHERE condition for date_sub function.
So I will use setProjection and sqlRestriction in a single query.
You're making you life difficult. Why don't you simply compute the date limit in Java before executing the query?
Date today = DateUtils.truncate(new Date(), Calendar.DATE);
Date limit = DateUtils.addDays(today, -7);
And since the query is completely static, why using the Criteria API. HQL is much easier:
String hql = "SELECT sum(o.totalAmount) as total, o.createdAt from Order o"
+ " where o.createdAt < :limit"
+ " and o.doctor.id = 193"
+ " group by o.createdAt";
criteria.setProjection((Projections.sum("/* name of the mapping variable for total_amount*/")));
public int getSum() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Criteria criteria = session.createCriteria(Product.class);
criteria.setProjection(Projections.sum("productCount"));
List list = criteria.list();
session.getTransaction().commit();
return (int) list.get(0);
}
//hibernate mpping
<property name="productCount" type="int" column="PRODUCT_COUNT"/>
In JPQL I want to construct the equivalent query to this:
select *, count(*) as finger_count from page_delta_summary
where delta_history_id = ? and change_type = ? group by fingerprint;
where fingerprint is a varchar field in table page_delta_summary. What I have is this:
select d, count(d) as finger_count from PageDeltaSummary d
where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType
GROUP BY d.fingerprint"
where PageDeltaSummary is my entity. But I'm getting the following exception:
org.apache.openjpa.persistence.ArgumentException: Your query on type "class com.su3analytics.sitedelta.model.PageDeltaSummary" with filter "select d, count(d) from PageDeltaSummary d where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType GROUP BY d.fingerprint" is invalid. Your select and having clauses must only include aggregates or values that also appear in your grouping clause.
The query works fine if I remove either count(d) as finger_count or the GROUP BY.
Any suggestions?
Thanks
Your original SQL query doesn't make sense, therefore you can't convert in into JPQL.
I guess you want to get count of page_delta_summary rows satisfying where conditions for each fingerprint. If so, the SQL query looks like this:
select fingerprint, count(*) as finger_count from page_delta_summary
where delta_history_id = ? and change_type = ? group by fingerprint;
and JPQL - like this:
select d.fingerprint, count(d) from PageDeltaSummary d
where d.deltaHistoryId = :deltaHistoryId and d.type = :pageDeltaType
GROUP BY d.fingerprint
These queries return pairs <fingerprint, finger_count> instead of full page_delta_summary rows (or entities).