How to write JPQL query with `COUNT` and `GROUP BY` - jpa

How write JPQL query with COUNT and GROUP BY and get result as Map<Integer,Integer>?
public class CommentEntity {
private int id;
private int parentId;
private EntityParentType parentType;
private Long replyCounts;
private String author;
private String comment;
}
.
public enum EntityParentType {
PERSON,
EVENT,
COMMENT;
}
I wrote MySQL query and this work fine:
SELECT parent_id, COUNT(*) FROM comment AS c WHERE c.parent_type = 2 AND c.parent_id IN (64,65) GROUP BY parent_id
but JPQL query is fail:
#Repository
#Transactional(readOnly = true)
public interface CommentRepository extends JpaRepository<CommentEntity, Integer> {
#Query(value = "SELECT c.parentId, COUNT(c.id) FROM CommentEntity AS c WHERE c.parentType = ?1 AND c.parentId IN (?2) GROUP BY c.parentId")
Map<Integer, Integer> findReplyCountByParentIds(EntityParentType entityParentType, List<Integer> ids);
}
.
Method threw 'org.springframework.dao.IncorrectResultSizeDataAccessException' exception.
result returns more than one elements
below is fail too:
#Query(value = "SELECT c.parentId, COUNT (c.id) FROM CommentEntity AS c WHERE c.parentType = ?1 AND c.parentId IN (?2) GROUP BY c.parentId")
List<Map<Integer, Integer>> findReplyCountByParentIds(EntityParentType entityParentType, List<Integer> ids);
.
Method threw 'org.springframework.dao.InvalidDataAccessApiUsageException' exception.
No aliases found in result tuple! Make sure your query defines aliases!
I tried add pacakge to CommentEntity and also is fail

One workaround is to use the constructor syntax as follows:
SELECT NEW org.apache.commons.lang3.tuple.ImmutablePair(c.parentId, COUNT(c.id)) FROM ...
Of course, you could use any other class in place of ImmutablePair (e.g. a concrete implementation of Map.MapEntry). You then declare the result as a List<ImmutablePair> and collect the results into a map in your service method.

Related

How to return a count column not exists in table by JPA

I want find a way to get extra column that count my records and return it in 1 mapping entity with extra filed.
I tried #transient on field but it will not return value when query.
Then I remove #transient but get an exception when save.
Also I tried #Formula but received null pointer exception.
Here's my repository code:
#Query(value = "select id,account,session_id,create_time,count from query_history a join " +
"(select session_id sessionId,max(create_time) createTime,count(*) count from query_history group by session_id) b " +
"on a.session_id = b.sessionId and a.create_time = b.createTime where account = ?1 order by create_time desc",
countQuery = "select count(distinct(session_id)) from query_history where account = ?1",
nativeQuery = true)
Page<QueryHistory> findByNtAndGroupBySessionAndAction(String account, Pageable pageable);
entity code:
#Entity
#Table(name = "query_history")
#Data
public class QueryHistory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String account;
#Column
private Long sessionId;
#Column
private long createTime;
#Transient
private Integer count;
}
Sorry about my English and thanks a lot for any advice.
I solved the problem by projections spring-data-projections, in fact I tried this before but in my sql:
select id,account,session_id,create_time,count
which should be:
select id,account,session_id sessionId,create_time createTime,count
PS:
projection interface:
public interface QueryHistoryWithCountProjection {
Long getId();
String getAccount();
Long getSessionId();
long getCreateTime();
Integer getCount();
}

Convert specific query into JPQL or Criteria Builder query

Here is the code for 2 entities (it generates three tables in the database). A Book entity:
#Entity
public class Book {
#Id
private long id;
private String name;
#ManyToMany
private List<Author> authors;
}
An Author entity:
#Entity
public class Author {
#Id
private long id;
#Column(unique=true)
private String name;
}
I'm trying to find books by the list of authors. Here is a sql query:
select book.id, ARRAY_AGG(author.name)
from book
join book_authors ba on book.id=ba.book_id
join author on ba.authors_id=author.id
group by book.id
having ARRAY_AGG(distinct author.name order by author.name)=ARRAY['a1', 'a2']::varchar[]
['a1', 'a2'] is a list of book authors, it must be passed as a parameter. The idea is to aggregate authors and then compare them with the list of passed parameters.
How to rewrite this SQL-query into either a JPQL or CriteriaBuilder query?
#Query("select distinct b from Book b join b.authors a where a.name in(:names)")
List<Book> findByAuthorsNames(#Param("names") List<String> names)
If you want to fetch b.authors use join fetch instead of join
If the exact match is necessary you can use Specification like this
public class BookSpecifications {
public static Specification<Book> byAuthorsNames(List<String> names) {
return (root, query, builder) -> {
Join<Book, Author> author = root.join("authors", JoinType.LEFT);
Predicate predicate = builder.conjunction;
for(String name : names) {
Predicate namePredicate = builder.and(author.get("name"), name);
predicate = builder.and(predicate, namePredicate);
}
return predicate;
}
}
}
BookRepository have to extend JpaSpecificationExecutor.
Usage:
BookRepository repository;
public List<Book> findByAuthorsNames(List<String> names) {
return repository.findAll(BookSpecifications.byAuthorsNames(names));
}

JPA inheritance issue, why the generated query is different between wildfly10 and jboss7

I am migrating a project from jboss7 to wildfly10. The strange thing is the generated query in jboss is different in wildfly10, that causes the tables structure have to be changed, but it is not expected.
public class BaseAnnotation implements Serializable {
private static final long serialVersionUID = 6636704943305921427L;
}
#Entity
#Table(name="one")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class oneBaseAnnotation extends BaseAnnotation {
#Id
#GeneratedValue(generator = "baseAnnotationSequencer")
#SequenceGenerator(name = "baseAnnotationSequencer", sequenceName = "BASEANNOTATION_SEQ")
private Long id;
private String annotationType;
.....
}
#Entity
public class TwoStructureAnnotation extends oneBaseAnnotation {
private static final long serialVersionUID = -5838272604038154615L;
#OneToMany
#JoinTable(name= "CSA_CS")
private List<TwoStructure> twoStructures = new ArrayList<TwoStructure>();
public TwoStructureAnnotation() {
setAnnotationType("Two Structure");
}
.....
}
public class..... {
protected List<T> createQuery(int first, int pageSize,
List<SortMeta> multiSortMeta, Map<String, String> filters,
String joinField) {
// Setup
CriteriaBuilder cb = getObjectEntityManager().getCriteriaBuilder();
CriteriaQuery<T> criteria = (CriteriaQuery<T>) cb.createQuery();
Root<A> annotationRoot = criteria.from(TwoStructureAnnotation.class);
ListJoin<A, T> joinRoot = annotationRoot.joinList("twosStructures");
Predicate restrictions = cb.conjunction();
// Filter
filters.putAll(this.getBaseFilter());
restrictions = cb.and(restrictions,
createGlobalFilter(filters, joinRoot, cb));
restrictions = cb.and(restrictions,
cb.equal(annotationRoot, annotation));
...
// Query creation
criteria.where(restrictions);
criteria.select(joinRoot);
// Restrict Returns
TypedQuery<T> returnQuery = getObjectEntityManager().createQuery(
criteria);
returnQuery.setFirstResult(first);
returnQuery.setMaxResults(pageSize);
List<T> results = returnQuery.getResultList();
....}
The query below, the different that the key in the inner join on table CSA_CS. I have no idea why, please suggest me, thank you.
--in Jboss7
select * from
( select
crystalstr2_.id as id1_43_,
crystalstr2_.pdbEntry_id as pdbEntry_id3_43_,
crystalstr2_.title as title2_43_
from
ONE crystalstr0_
inner join
CSA_CS crystalstr1_
on crystalstr0_.id=crystalstr1_.ONE_id
inner join
TwoStructure crystalstr2_
on crystalstr1_.crystalStructures_id=crystalstr2_.id
where
crystalstr0_.DTYPE='TwoStructureAnnotation'
and 1=1
and 1=1
and crystalstr0_.id=? )
where
rownum <= ?
---In wildfly10
select
*
from
( select
crystalstr2_.id as id1_36_,
crystalstr2_.pdbEntry_id as pdbEntry_id3_36_,
crystalstr2_.title as title2_36_
from
ONE crystalstr0_
inner join
CSA_CS crystalstr1_
on crystalstr0_.id=crystalstr1_.TWOStructureAnnotation_id
inner join
TwoStructure crystalstr2_
on crystalstr1_.crystalStructures_id=crystalstr2_.id
where
crystalstr0_.DTYPE='TwoStructureAnnotation'
and 1=1
and 1=1
and crystalstr0_.id=? )
where
rownum <= ?
Tables:
table-TWOSTRUCTURE
ID
TITLE
table-CSA_CS
ONE_ID
CRYSTALSTRUCTURES_ID
table-ONE
DTYPE
ID
ANNOTATIONTYPE
JBoss7 ships hibernate 4.x and wildfly 10 ships hibernate 5. In hibernate 5, Oracle is implemented 'inner join'. If you use Oracle10gDialect, then
Oracle10gDialect added support for ANSI joins. Subclasses (e.g. Oracle12cDialect) inherit this functionality.

How to query for #ElementCollection attributes using Spring Data JPA query language?

My code looks like:
#Entity
public class A extends AbstractEntity<Long> {
#ElementCollection(fetch = FetchType.EAGER)
#MapKeyColumn(name = "propertyKey")
#Column(name = "propertyValue")
private Map<String, String> properties;
}
I use the following query,
#Query("Select a from A a JOIN a.properties vp where vp.propertyValue = ?2 ")
A findByRequestedSimNumber(String key, String simNumber);
When I run the application I got error with such exception:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException:cannot dereference scalar collection element: propertyValue [Select a from A a JOIN a.properties vp where vp.propertyValue = ?2 ]
I am really confused, can't we access the attributes of the mapped table.Please help me to sort the problem.
Thankyou!
In the repositories deep dive code examples we do it like this:
The Product entity:
#Entity
public class Product extends AbstractEntity {
...
#ElementCollection
private Map<String, String> attributes = new HashMap<String, String>();
...
}
The repository query method that allows querying for arbitrary product attributes:
#Query("select p from Product p where p.attributes[?1] = ?2")
List<Product> findByAttributeAndValue(String attribute, String value);
Your query will have to look like
Select a from A a, IN(a.properties) as vp where vp.propertyValue = ?2
You cannot just ask for equality in a collection.
If your problem is to access the properties in a given A, they are automatically loaded with the A object, so you only have to
A a = <some finder method>
System.out.println(a.properties);

JPA or Play Framework list from query joining 2 tables

Mainly I work with JSF so am totally new to this annotation subject
If anyone can help
I wanna a list from this query
SELECT f.CODE ,f.NAME || '-' || e.NAME
FROM FS.ELIGIBLE e RIGHT
OUTER JOIN FS.FINANCIAL_SUPPORT f ON e.CODE = f.CODE ;
The query above retrieves a list from 2 tables and concatenating the name field from both tables!!
How can i do this in JPA or in play with another query supported by Play Framework ???
Have a read of the Play Framework documentation, specifically the part about JPA and your Domain Model.
You can access the entity manager at any time by calling
EntityManager entityManager = JPA.em();
Using this you can create any query that you want, even a "Native" Query. For example:
List<Object> results = JPA.em().createNativeQuery(
"SELECT f.CODE ,f.NAME || '-' || e.NAME "+
"FROM FS.ELIGIBLE e RIGHT "+
"OUTER JOIN FS.FINANCIAL_SUPPORT f ON e.CODE = f.CODE").getResultList()
JPA is not like relational database system which you can do your queries like join, left join or outer joins it is a mapping technology of objects. You can also do the same fetch just like those RDBMS counterparts but with a different approach.
What you have to do is make an Object then relate your second Object to your first Object, that is the proper way to relate 2 or more Objects. The Objects I'm talking about is your Table. See my example below:
Table1: Items.java
...
// do your imports here ...
// do your annotations here like
#Entity
#Table(name="Items")
public class Items implements Serializable {
private String id;
private String itemno;
private String description;
private Set<Vendors> vendors; //this is the 2nd table (1:n relationship)
...
// don't forget your constructor
// in your setter and getter for Vendors do the ff:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name="id")
public Set<Vendors> getVendors() {
return vendors;
}
public void setVendors(Set<Vendors> vendors) {
this.vendors = vendors;
}
...
}
Table2: Vendors.java
#Entity
#Table(name="Vendors")
public class Vendors implements Serializable {
private long id;
private String company;
private String contact;
private String sequence;
...
public Vendors() { }
...
// in your setter & getter
#Id
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
...
}
Now on your query, just do a regular select as in the ff:
public void makeQuery(String seq) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(...);
EntityManager em = emf.createEntityManager();
TypedQuery<Items> query = em.createQuery("
SELECT i, j.contact, j.company, j.sequence FROM Items i LEFT OUTER JOIN i.vendors j WHERE i.vendors.sequence = :seq
ORDER BY i.id", Items.class);
List<Items> items = query.setParameter("sequence", seq).getResultList();
...
}
Now you can refer to your 2nd table Vendors by using items.vendors.company ... and so on.
One disadvantage with this one is that, JPA make its related objects in the form of Set, see the declaration in Table1 (Items) - private Set vendors. Since it was a Set, the sequence is not in order it was received, unlike using List.
Hope this will help ...
Regards, Nelson Deogracias