I'm using Spring Boot 1.5.2 and the following #Query:
#Query(value = "SELECT m FROM Message m WHERE m.from.userId = :myId OR m.to.userId = :myId GROUP BY m.from.userId, m.to.userId ORDER BY m.date DESC")
List<Message> chatOverview(#Param("myId") final Long myUserId);
The intention of the query is to create a chat messenger overview, where you see the last message of each conversation you had. It works fine for me in dev, but in production (newer MySQL database version) I get this error:
java.sql.SQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'message0_.message_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
I read in this thread what the reason for this change was, however I couldn't find a way to fix this with JPA / Spring. I cannot change any settings in the production MySQL database and I would like to avoid any upgrading in Spring either. How can I fix my query?
Here is the definition and purpose of the GROUP BY (see section 4.7) clause:
The GROUP BY construct enables the aggregation of result values according to a set of properties.
That means it is used only if you're aggregating (sum, avg, max, min, ...) the value(s) of a field(s). But in your case I don't see any aggregation function. So just remove the GROUP BY clause and everything should be fine:
SELECT m FROM Message m WHERE m.from.userId = :myId OR m.to.userId = :myId ORDER BY m.date DESC
Grouping on userId doesn't make sense too because all the entities returned by this query will have the same value for this field.
Related
I am trying to use Spring Data Pageable with JPA Query (Not native) which cases the following exception:
Caused by: java.sql.SQLSyntaxErrorException: Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'alerting.alerthisto0_.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Here is my query:
#Query(value =
"SELECT history.alertId AS alertId, history.userId AS userId, alert.organization.id AS organizationId,"
+ " alert.status AS status, alert.createdAt AS createdAt, COUNT(history.alertId) AS alertsCount"
+ " FROM AlertHistory AS history LEFT JOIN Alert AS alert ON history.alertId=alert.id"
+ " GROUP BY history.alertId, history.userId")
Page<AlertAggregationDetails> groupByAlertIdAndUserId(Pageable pageable);
Apparently, because I am using Pageable history.id is injected which is unnecessary and causing syntax issue. If I add history.id to the group by section the syntax will be correct but obviously, the result is far from what I'd like to have. I was wondering if there is a way to fix the syntax issue and continue using Pageable.
Also, if I remove Pageable this query works and there is no issue.
EDIT:
After I debug the query I noticed that Spring JPA added ORDER BY history.id to my query automatically when I use Pageable. As a workaround, I can replace sql_mode, but I don't understand why I cannot overwrite ORDER BY with Pageable.
AFAIK Spring Data by default adds the entity id as order by expression if you don't add any sorts which might be what you see. It makes sense, because you want a consistent ordering, otherwise pagination wouldn't work. You will have to add the sorts alertId and userId for MySQL to be happy and also get consistent results.
I wish to implement pagingation with FIQL support.
I am using apache cxf with JPA(Hibernate).
Here is sample example given for it http://cxf.apache.org/docs/jax-rs-search.html#JAX-RSSearch-JPA2.0
SearchConditionVisitor<Order, TypedQuery<Order>> visitor
= new JPATypedQueryVisitor<>(em, Order.class);
// connect FIQL cxf SearchCondition with our JPA visitor
searchCondition.accept(visitor);
// creeate JPA specific TypedQuery by our visitor
TypedQuery<Order> typedQuery = visitor.getQuery();
typedQuery.setFirstResult((page * perPage) - perPage);
typedQuery.setMaxResults(perPage);
// Run the query and return matching a complex FIQL criteria
return typedQuery.getResultList();
Every thing looks working including searching and pagination.
It looks have no order by clause being use from generate sql log and seems follow database insertion order.
Now I wish to change the default sorting order. For example I wish to sort by Order id field in descending order. How can I achieve that?
I can get it working with use of CriteriaQuery.
JPACriteriaQueryVisitor<Order, Order> jpa
= new JPACriteriaQueryVisitor<>(em, Order.class, Order.class);
searchCondition.accept(jpa);
CriteriaQuery<Order> cq = jpa.getCriteriaQuery();
CriteriaBuilder cb = em.getCriteriaBuilder();
Root<Order> root = (Root<Order>) cq.getRoots().iterator().next();
cq.orderBy(cb.desc(root.get("id")));
TypedQuery<Order> query = jpa.getTypedQuery();
query.setFirstResult((page * perPage) - perPage);
query.setMaxResults(perPage);
return query.getResultList();
You cannot change the order after creating the query.
If you use MySQL, then maybe you can use an integer parameter in ORDER BY :colnum DESC to specify the column number by which to sort (starting at 1 from the selected columns only), but you cannot change direction.
PostgreSQL does not allow you to do this. I do not know how it is on other databases, though. With MySQL, the parameters are replaced by the SQL Driver, so it always receives the full query with escaped sequences. With PG, the query is first parsed by the server, the execution plan is created (including if and how to order the results) and then the parameters are sent.
I recently encountered the following problem with buiding queries in jooq (version 3.1.0):
I want to build delete statement with order and limit constraints. So, my aim is to build something like this:
DELETE FROM table ORDER BY field DESC LIMIT 1 (this is MySql syntax)
But i haven't found nesessary methods in result delete query object:
DSLContext context = createContext();
DeleteWhereStep delete = context.delete(createTable(table));
DeleteConditionStep whereStep = delete.where(condition);
whereStep.orderBy(...)//and no such method here
There are all nesessary methods in select statements and none for delete.
Is it possible to set order and limit for delete request in jooq?
As of jOOQ 3.2, these sorts of extensions are currently not implemented yet. Chances are, that #203 could be implemented in jOOQ 3.3, though.
In the mean time, you have two options:
Resort to plain SQL
i.e. write something like:
context.execute("DELETE FROM {0} ORDER BY {1} DESC LIMIT 1",
createTable(table),
field);
Manually transform your SQL statement into something equivalent
I suspect that the ORDER BY .. LIMIT extension to the MySQL DELETE statement is just sugar for:
DELETE FROM table t
WHERE t.id IN (
SELECT id FROM table
ORDER BY field LIMIT 1
)
Or with jOOQ:
context.delete(TABLE)
.where(TABLE.ID.in(
select(TABLE.ID)
.from(TABLE)
.orderBy(TABLE.FIELD)
.limit(1)
))
I have a query like the following:
Select new mypackage.MyClass( u, max(sc.serviceDate))
from Unit u left join u.serviceCalls sc
where u.organization.key = :organizationKey
So, my mapping is that I have a Unit, which has a collection of ServiceCalls (FetchType.LAZY), and also has an organization. Each ServiceCall has a serviceDate.
In my query, I would like to select the entire Unit, but not all of the serviceCalls. I would like to fetch the most recent serviceDate if one exists.
Attempting to execute the query through eclipselink on postgres gets me the following (I removed some selected fields from the query output)
Internal Exception: org.postgresql.util.PSQLException: ERROR: column "t0.key" must appear in the GROUP BY clause or be used in an aggregate function
Position: 8
Error Code: 0
Call: SELECT t0.KEY, MAX(t1.service_date) FROM unit t0 LEFT OUTER JOIN service_call t1 ON (t1.unit_key = t0.KEY), organization t2 WHERE ((t2.KEY = ?) AND (t2.KEY = t0.organization_key))
It looks like max is being applied across all service calls instead of getting me the max for each unit. Is there a way to do this or am I going to have to just getch all the service calls and get the max that way?
Do you need a group by u at the end?
I have a rather complex query which works in SQL, but I would like to express this in HQL for portability. I'm going to fetch a user configured preference value if they exist, if not I must use a default value. This value must be subtracted from current date and the matched against a column in the table which I'm interested in:
select d.id, d.guid, d.deletetimestamp, u.id
from descriptor d, ownerkey ow, user u
where
d.parentid in
(select td.id
from descriptor td, store s
where s.type = 'Trash'
and s.descriptorid = td.id
)
and d.ownerkeyid = ow.id
and ow.ownerid = u.id
and
(
(d.deletetimestamp < CURRENT_TIMESTAMP() - INTERVAL
(select pv.value
from preferencevalue pv, userpreference up
where u.id = up.userid
and up.preferenceid = 26
and up.value = pv.id)
DAY)
or
(d.deletetimestamp < CURRENT_TIMESTAMP() - INTERVAL
(select cast(pv.value as SIGNED)
from preferencevalue pv, defaultpreference dp
where dp.preferenceid = 26
and not exists(select up.userid from userpreference up where u.id = up.userid and up.preferenceid = 26)
and dp.value = pv.id)
DAY)
)
I'm trying to construct this by using the Criteria API which seems to include most of the logical operators that I need (equals, larger than, or, isEmpty/isNull), but not sure how I would express all these parts.
Using a view is not an option at this point since we're using MySQL as the production database while the integration tests are running with H2 inmemory database. I'm not able to get find the sata substract function in H2 while MySQL do support this.
The select fields isn't important since they have only been used for testing purposes.
you can use Restrictions.disjunction() for or -and Restrictions.conjuction() for and clauses.
To reference a certain property of an entity (like pv.value) you can use Projections.property("value")
for the casting I'm not sure, perhaps using the #Formula annotation on your entity? But this is a hibernate and not a JPA annotation.
as far as I know there is no equivalent for INTERVAL in hibernate but in such cases (maybe also for the above cast) you could use Restrictions.sqlRestriction("some sql...")
It will be a challenge putting all of this together to transform your query to hibernate criteria.
greetz,
Stijn