spring jpa query SQLGrammarException on join #ManyToMany - spring

I have Way POJO in my Spring Boot application, here is what it looks like:
public class Way {
#Id
private Long wayID;
#ToString.Exclude
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
private List<Relation> relations=new ArrayList<>();
Now I want to select all the ways that has relationID of a given value.
relationID is a member of Relation POJO.
So here is my query
#Query("select w from Way w join Relation relations where relations.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);
But the error seems to be in my JOIN portion, it crashes and says:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; SQL [select way0_.wayid as wayid1_5_ from way way0_ inner join relation relation1_ on where relation1_.relationid=?]; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement
How can I resolve this ?
I also tried this:
#Query("select way from Way.relations full join Way.relations rel where rel.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);
This too:
#Query("select way from Way way join Way.relations rel where rel.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);

When using JOIN you need to use alias of Way with relations field like this, w.relations r instead of Way.relations.
The complete query is below:
#Query("select w from Way w join w.relations r where r.relationID=?1")
List<Way> selectAllWaysByRelationID(Long relationID);

Related

CriteriaAPI Query with Join by a string value

I have currently a query with a bunch of filters, which makes sense to use the Criteria API, unfortunately I have this query that uses a Join which uses a string value instead of a relationship. This is an example of the query:
SELECT ua.id,
COALESCE(uf.status, f.status) AS status,
r.name,
ua.companyname,
ua.firstname,
ua.lastname,
ua.usergroup,
ua.email,
ua.country,
ua.continent
FROM useraccount ua
JOIN userrole ur on ua.id = ur.userid
JOIN role r on ur.roleid = r.id and r.eventgroupid = 1
JOIN feature f on f.name = 'Locked'
LEFT JOIN userfeature uf on uf.featureid = f.id AND uf.userid = ua.id;
As you can see the problem of the query is that I want to use COALESCE operation to get a UserFeature status if present, if not use the default status from the Feature table.
The feature table is just a simple one with id, name and the status, it is only related to UserFeature and UserFeature at the same time is related to the UserAccount.
As you might guess the CriteriaAPi doesn't allows a Join<> by a regular string value. I have tried to get my mind around to get how can I change the select statement to be more aligned with what CriteriaAPI offers, but I haven't found anything on this.
I'm using PostgreSQL and Hibernate 5.4.32 (by using the spring starter jpa)

Join 2 Tables using Spring Boot JPA Criteria Specification and Predicate

I'm trying to create a criteria to retrieve Rows from 2 tables (UserDetail, Vehicle). A UserDetail has reference to Vehicle. My objective is to retrieve a list of specified field from both Tables, given an UserDetail id. In #Query was easy to do but the client said that this must be coded with criteria with specification and predicate to Join Tables.
My #Query was:
#Repository
public interface UserDao extends CrudRepository<UserDetail, Integer>{
#Query("SELECT ud.userId, ud.userName ,vh.vehicleId, vh.vehicleName "
+ "FROM UserDetail ud LEFT JOIN ud.vehicle vh where ud.vehicleId = vh.vehicleId")
public List<UserVehicleDTO> findAllUserVehicle();
}
Can anyone help me? I Fetch Data Using Specification with predicate as given Query is needed
Criteria query(I did not tested/debugged it, may need modify):
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<UserDetail> root = query.from(UserDetail.class);
Join<UserDetail, Vehicle> join = root.join("vehicle", JoinType.LEFT);
query.multiselect(
root.get("userId"),
root.get("userName"),
join.get("vehicleId"),
join.get("vehicleName")
))
List<Tuple> x = entityManager.createQuery(query).getResultList();
BTW. JPQL query is wrong. Don't need where condition, it will be created by JPA automatically.
SELECT ud.userId, ud.userName ,vh.vehicleId, vh.vehicleName
FROM UserDetail ud
LEFT JOIN ud.vehicle

DTO Projection of Query with #ElementCollection causes 'Unable to locate appropriate constructor on class' error

I'm writing custom query using projection to reduce amount of queries in one session, when couple of field from antity are needed and using Fetch join.
Unfortunately got stuck into a problem when one types in returned dto is a collection.
I have following class with #ElementCollection (siplified version for this purpose):
#Entity
class MyClass(
val someString: String,
#ElementCollection
#Enumerated(EnumType.STRING)
var enums: MutableSet<Enum>,
#ManyToOne
var condition: Condition
//And so on...
)
And DTO used for projection:
data class ProjectionDTO(
val aString: String,
val enumList: List<Enum>
)
But when using query:
fun query(condition: Condition): List<ProjectionDTO> =
entityManager.createQuery(
"""SELECT NEW com.xxx.ProjectionDTO( m.someString, e ) FROM MyClass m
INNER JOIN FETCH m.enums e
WHERE m.condition = :condition""", ProjectionDTO::class.java)
.setParameter("condition", condition)
.resultList
}
I get following exception:
Exception:[org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [com.xxx.ProjectionDTO]. Expected arguments are: com.xxx.String, com.xxx.Enum [SELECT NEW com.xxx.ProjectionDTO( m.someString, e ) FROM MyClass m
INNER JOIN FETCH m.enums e
WHERE m.condition = :condition]]
Already tried different types of collection, additional constructors and calling field of given #ElementCollection like e.enum in query params.
Is it possible to return a list (or other collection) from this kind of query? If so, how do I tackle it?
It is not allowed to use collection path expressions in constructor query. link
Put just root entity into constructor:
SELECT NEW com.xxx.ProjectionDTO(m) WHERE m.condition = :condition
In constructor assign m.enums, m.someString to fields.

How to calculate average on a column with JPA?

I would like to calculate average on a column.
I tried the following:
#Query("SELECT AVG(e.rating) FROM user_rating e WHERE e.route_uid = ?1")
fun averageOfRateings(routeId: UUID): Long
The query works in Sql, however I get the following error when I run the code in Spring Boot.
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException:
user_rating is not mapped [SELECT AVG(e.rating) FROM user_rating e WHERE e.route_uid = ?1]
What would be the right syntax? What is the problem with the mapping of my table?
you must use the entity name, not the table.The same applies for the column name, you must use the field name instead.
I assume your entity is UserRating, so the correct query will be:
#Query("SELECT AVG(e.rating) FROM UserRating e WHERE e.routeUid = ?1")
Or second option specify that is native query:
#Query(value = "SELECT AVG(e.rating) FROM user_rating e WHERE e.route_uid = ?1" , nativeQuery = true)

QueryDSL Paging Sorting duplicates

I have got an Entity A say Car with a OneToMany relationship to an Entity B say CarProperty.
My Car Repository extends QueryDslPredicateExecutor to support paging and sorting:
Page<T> findAll(Predicate predicate, Pageable pageable);
I'm trying to execute a query where I sort the results, by a column of CarProperty, defining the sort on pageable variable.
However since it is a One To Many relationship between Car and CarProperty, I have returned duplicates of Car.
Is it possible to obtain distinct results on Car using this structure?
If it is not possible to use distinct on the Predicate, how could I represent the following query in the predicate (using exists to eliminate duplicates):
SELECT Car.*
FROM Car C LEFT JOIN CarProperty CP ON (C.ID = CP.CAR_ID)
WHERE EXISTS (SELECT 1
FROM CarProperty CP2
WHERE CP2.CAR_ID = C.ID AND CP2.ID = CP.ID)
ORDER BY CP.PROPERTY_NAME ASC;
Thanks in advance
You can use named entity graph to avoid duplicate records, in my case it worked.
#NamedEntityGraph(name = "Car.carProperty" ,attributeNodes = #NamedAttributeNode("carProperties"))
#Entity
public class Car {
#OneToMany
#JoinColumn(name = "carProperties")
private List<CarProperty> carProperties;
}
and then override findall method
#EntityGraph(value = "Car.carProperty" , type = EntityGraphType.LOAD)
Page<Car> findAll(Predicate predicate, Pageable pageable);

Resources