Group By in Java Persistence/JPQL - jpa

I have an Entity class and it has #ManyToOne relationships. I need to use GROUP BY as in SQL query.
I have written a JPQL but its not working. My code is :
#NamedQuery(name = "AssetDepModel.findByAssedId",
query = "SELECT dep FROM AssetDepModel dep "
+ "JOIN dep.faDetails fad "
+ "WHERE fad.assetId.assId = :assetId_passed "
+ "GROUP BY dep.faDetails,dep.faDetails.id,dep.fiscalModel.fyId,dep.depAmt,dep.depId,dep.depMethodId,dep.depRate,dep.depTypeId,dep.quarterId,dep.createdDt,dep.createdBy,dep.updatedDt,dep.updatedby "
+ "ORDER BY fad.id")
public class AssetDepModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String FIND_BY_ASSET_ID = "AssetDepModel.findByAssedId";
public static final String FIND_BY_DETAIL_ID = "AssetDepModel.findByDetailId";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "dep_id")
private int depId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fiscal_id", referencedColumnName = "fy_id")
private FiscalYrModel fiscalModel;
#Column(name = "quarter_id")
private int quarterId;
#ManyToOne
#JoinColumn(referencedColumnName = "id", name = "fa_details_id")
private FADetailsModel faDetails;
#Column(name = "dep_type_id")
private int depTypeId;
#Column(name = "dep_method_id")
private int depMethodId;
#Column(name = "dep_rate")
private Double depRate;
#Column(name = "dep_amt")
private Double depAmt;
#Column(name = "created_dt")
#Temporal(TemporalType.TIMESTAMP)
private Date createdDt;
#Column(name = "created_by")
private int createdBy;
#Column(name = "updated_dt")
#Temporal(TemporalType.TIMESTAMP)
private Date updatedDt;
#Column(name = "updated_by")
private int updatedby;
I tried this code but while calling the JPQL it always gives error saying that objects in Select is not included in Group By clause.
I need to GROUP BY according to a foreign key field.
I get following error :
Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Column
'inv_asset_depreciation.fa_details_id' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY clause.
Error Code: 8120
Call: SELECT t0.dep_id, t0.created_by, t0.created_dt, t0.dep_amt, t0.dep_method_id,
t0.dep_rate, t0.dep_type_id, t0.quarter_id, t0.updated_dt, t0.updated_by,
t0.fa_details_id, t0.fiscal_id FROM inv_asset_depreciation t0, fiscal_yr t2,
inv_fixed_asset_detail_mcg t1 WHERE ((t1.asset_id = ?) AND ((t1.id = t0.fa_details_id)
AND (t2.fy_id = t0.fiscal_id))) GROUP BY t1.id, t1.asset_given_name,
t1.brand_name_description, t1.created_by, t1.created_date,
t1.dispose_dt_en,t1.dispose_dt_np, t1.dispose_value, t1.req_form_no,
t1.start_use_dt_en,t1.start_use_dt_np,t1.update_count, t1.updated_by,
t1.updated_date, t1.asset_id,t1.dept_id, t1.status, t1.id,t2.fy_id, t0.dep_amt,
t0.dep_id, t0.dep_method_id,t0.dep_rate, t0.dep_type_id,t0.quarter_id,
t0.created_dt, t0.created_by,t0.updated_dt, t0.updated_by
ORDER BY t1.id
bind => [1 parameter bound]
Query: ReportQuery(name="AssetDepModel.findByAssedId" referenceClass=AssetDepModel
sql="SELECT t0.dep_id, t0.created_by, t0.created_dt, t0.dep_amt,
t0.dep_method_id,t0.dep_rate,t0.dep_type_id, t0.quarter_id, t0.updated_dt,
t0.updated_by, t0.fa_details_id,t0.fiscal_id FROM inv_asset_depreciation t0,
fiscal_yr t2, inv_fixed_asset_detail_mcg t1 WHERE ((t1.asset_id = ?)
AND ((t1.id = t0.fa_details_id) AND (t2.fy_id = t0.fiscal_id)))
GROUP BY t1.id, t1.asset_given_name, t1.brand_name_description,
t1.created_by,t1.created_date, t1.dispose_dt_en, t1.dispose_dt_np,
t1.dispose_value, t1.req_form_no, t1.start_use_dt_en, t1.start_use_dt_np,
t1.update_count, t1.updated_by, t1.updated_date,t1.asset_id, t1.dept_id,
t1.status, t1.id, t2.fy_id, t0.dep_amt, t0.dep_id, t0.dep_method_id,
t0.dep_rate, t0.dep_type_id, t0.quarter_id, t0.created_dt,
t0.created_by, t0.updated_dt,t0.updated_by ORDER BY t1.id")
I modified a little bit like this :
#SuppressWarnings("unchecked")
public List<Object> findByAssetIdForSaleWriteOff(int assetId){
Query query = getEntityManager().createQuery("SELECT fad.id,dep.depAmt FROM AssetDepModel dep "
+ "JOIN dep.faDetails fad "
+ "WHERE fad.assetId.assId = "+assetId+" "
+ "GROUP BY fad.id,dep.depAmt "
+ "ORDER BY fad.id",AssetDepModel.class);
return (List<Object>)query.getResultList();
}
List<Object> objList = assetDepEJB.findByAssetIdForSaleWriteOff(faObj.getAssId());
Double amountDepTillNow = 0.0;
int fadId = 0;
int i=0;
for (Iterator<Object> iterator3 = objList.iterator(); iterator3
.hasNext();) {
Object[] obj = (Object[]) iterator3
.next();
if (i>0) {
if (fadId != (Integer) obj[0]) {
break;
}
}
fadId = (Integer) obj[0];
amountDepTillNow += (Double)obj[1];
i++;
}
It worked for me but If there is another efficient way, PLEASE DO SUGGEST ME.

Related

Netbeans wizard Entity Classes from Database, not all tables mapped

I used this wizard to create entity classes from my database. Some tables have not been transformed into classes, but there are attributes that identify the relationships.
this is my db ERD (mysql)
and this is the user entity class (attributes)
#Entity
#Table(name = "user")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
#NamedQuery(name = "User.findByOid", query = "SELECT u FROM User u WHERE u.oid = :oid"),
#NamedQuery(name = "User.findByUsername", query = "SELECT u FROM User u WHERE u.username = :username"),
#NamedQuery(name = "User.findByPassword", query = "SELECT u FROM User u WHERE u.password = :password"),
#NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = :email"),
#NamedQuery(name = "User.findByAddress", query = "SELECT u FROM User u WHERE u.address = :address"),
#NamedQuery(name = "User.findBySince", query = "SELECT u FROM User u WHERE u.since = :since")})
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "oid")
private Integer oid;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "username")
private String username;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 15)
#Column(name = "password")
private String password;
// #Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "email")
private String email;
#Size(max = 50)
#Column(name = "address")
private String address;
#Basic(optional = false)
#NotNull
#Column(name = "since")
#Temporal(TemporalType.DATE)
private Date since;
#JoinTable(name = "favorite", joinColumns = {
#JoinColumn(name = "user_oid", referencedColumnName = "oid")}, inverseJoinColumns = {
#JoinColumn(name = "wheelchair_oid", referencedColumnName = "oid")})
#ManyToMany
private List<Wheelchair> wheelchairList;
#ManyToMany(mappedBy = "userList1")
private List<Wheelchair> wheelchairList1;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "senderOid")
private List<Comment> commentList;
#JoinColumn(name = "role_oid", referencedColumnName = "oid")
#ManyToOne(optional = false)
private Role roleOid;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
public User() {
}
...
i can't understand something:
where is the OWN join table?
why i have userList1 and wheelchairList1? should it identifies OWN table? in this case i can rename it here or i have to rename it in some xml file?
why of
#OneToMany(cascade = CascadeType.ALL, mappedBy = "userOid")
private List<Orthopedy> orthopedyList;
?
it should be OneToOne...
moreover the "JSF from entities class" wizard creates CRUD operation to manage Users, how can i manage join tables? I need to write something in the controller like what?
can you please link me some resource where i can learn this?
thank you so much
While Creating Entities It Creates Classes For All Tables With Primary Key
But not for tables that have many to many relations . its managed by their parent classes it is maintained as a list.
This is my code for managing my many to many table of SubjectFaculty which has details of Faculty and Subjects
Assigning A Subject To Faculty
public void assignFacultyToSubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
College oCollege = em.find(College.class, oFaculty.getCollegeUname().getCollegeUname());
List<Faculty> lstFaculty = oSubject.getFacultyList();
List<Subject> lstSubject = oFaculty.getSubjectList();
if (!lstSubject.contains(oSubject)) {
lstFaculty.add(oFaculty);
lstSubject.add(oSubject);
oSubject.setFacultyList(lstFaculty);
oFaculty.setSubjectList(lstSubject);
em.merge(oSubject);
em.getEntityManagerFactory().getCache().evictAll();
} else {
System.out.println("Entry Already Found");
}
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}
Removing Subject And Faculty Details Form Many to Many Table
#Override
public void removeFacultySubject(String facultyUname, Integer subjectId) {
try {
Subject oSubject = em.find(Subject.class, subjectId);
Faculty oFaculty = em.find(Faculty.class, facultyUname);
List<Subject> lstSubject = oFaculty.getSubjectList();
List<Faculty> lsFaculty = oSubject.getFacultyList();
lstSubject.remove(oSubject);
lsFaculty.remove(oFaculty);
em.merge(oSubject);
} catch (Exception e) {
System.out.println("Error :- " + e.getMessage());
}
}

How to get the Max of Total in JPA?

Hi I have to write JPQL command that does something specific the question is exactly:
"Find those students that has the greatest total of studypoint scores?"
There are just two Entity classes that looks like :
#Entity
#Table(name = "student")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Student.findAll", query = "SELECT s FROM Student s"),
#NamedQuery(name = "Student.findById", query = "SELECT s FROM Student s WHERE s.id = :id"),
#NamedQuery(name = "Student.findByFirstname", query = "SELECT s FROM Student s WHERE s.firstname = :firstname"),
#NamedQuery(name = "Student.findByLastname", query = "SELECT s FROM Student s WHERE s.lastname = :lastname")})
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
#Column(name = "FIRSTNAME")
private String firstname;
#Column(name = "LASTNAME")
private String lastname;
#OneToMany(mappedBy = "studentId", cascade = CascadeType.PERSIST)
private List<Studypoint> studypointCollection;
public void addStudyPoint(Studypoint cc) {
if (studypointCollection == null) {
studypointCollection = new ArrayList<>();
}
studypointCollection.add(cc);
cc.setStudentId(this);
}
and the other class
#Entity
#Table(name = "studypoint")
#XmlRootElement
#NamedQueries({
#NamedQuery(name = "Studypoint.findAll", query = "SELECT s FROM Studypoint s"),
#NamedQuery(name = "Studypoint.findById", query = "SELECT s FROM Studypoint s WHERE s.id = :id"),
#NamedQuery(name = "Studypoint.findByDescription", query = "SELECT s FROM Studypoint s WHERE s.description = :description"),
#NamedQuery(name = "Studypoint.findByMaxval", query = "SELECT s FROM Studypoint s WHERE s.maxval = :maxval"),
#NamedQuery(name = "Studypoint.findByScore", query = "SELECT s FROM Studypoint s WHERE s.score = :score")})
public class Studypoint implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "MAXVAL")
private Integer maxval;
#Column(name = "SCORE")
private Integer score;
#JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID")
#ManyToOne
private Student studentId;
As you can see Student can have many Studypoints. Which I really struggle is this JPQl: Please help.
I tried for sometime to get this working with JQL and I can't see how you wrap the max in the sum, so I switched to SQL.
It's not ideal, but here is my repository:
package net.isban.fmis.repository;
import net.isban.fmis.entity.Studypoint;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface StudyPointRepository extends CrudRepository<Studypoint, Integer> {
#Query(value="select top 1 id from (select sum(score) scoresum, student.id from studypoint join student on student.id=studypoint.student_id group by student.id) order by scoresum desc", nativeQuery=true)
Integer findStudentIdWithMaxScore();
}

jpa merge Predicate array + search for foreign key value

I would like to have a search like this:
Select * From Users where ( ( username Like %criteria% OR firstName Like %criteria% ...) AND (CarId = carId) )
I did with 2 Predicate combined into an array, but need to do it with 1 Predicate only
#Entity
#Table(name = "users", uniqueConstraints = { #UniqueConstraint(columnNames = { "username" }) })
public class User{
#Id
#GeneratedValue(generator = "seq_id_user", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "seq_id_user", sequenceName = "seq_id_user")
private Long id;
#Column(unique = true)
#NotNull
private String username;
#Column(name = "first_name")
private String firstName;
....
#ManyToOne
#JoinColumn(name = "car_id", nullable = false, foreignKey = #ForeignKey(name = "CAR_ID_FK"))
private Car car;
...
The Car is a similar class, the User has 1 foreign key.
#Override
public List<User> searchUsers(String criteria, Car car) {
//TODO: check params and return values based on test cases
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> r = query.from(User.class);
Predicate predicate = builder.conjunction();
predicate = builder.or(predicate, builder.like(r.get("username"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("firstName"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("lastName"), "%" + criteria + "%"));
predicate = builder.or(predicate, builder.like(r.get("nickname"), "%" + criteria + "%"));
// how to implement the filter by foreign key value with AND? - can be a sub query to, which will be executed first time
// I need to use 1 Predicate, not a Predicate Array!
query.select(r).where(predicate);
List<User> result = entityManager.createQuery(query).getResultList();

JAX-RS return a collection of your custom object?

I have a webservice that uses JAX-RS, JPA and JAXB. I've writen a method that should return a collection of objects from my database. This doesn't happen though. It only return a single item.
My method:
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("findMeasurementsByRunID/{runID}")
public List<Measurement> getMeasurementByRunId(#PathParam("runID") int runID) {
List<Measurement> results = null;
Query query = emf.createEntityManager().createNamedQuery(
"findMeasurementsByRunID");
query.setParameter("runid", runID);
results = query.getResultList();
return results;
}
My Entity-class:
#Entity
#NamedQueries({
#NamedQuery(name = "findMeasurementsByRunID", query = "SELECT m "
+ "FROM Measurement m "
+ "WHERE m.runID = :runid"),
#NamedQuery(name = "findMeasurementsByRunIDPosition", query = "SELECT m "
+ "FROM Measurement m "
+ "WHERE m.runID = :runid AND "
+ "m.position = :position") })
#XmlRootElement
#XmlType(propOrder = { "runID", "rack", "position", "completionStatus",
"countingTime", "cr51Counts", "cr51CPM", "cr51Error",
"measurementDateTime", "protocolID", "protocolName" })
public class Measurement implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#AttributeOverrides({
#AttributeOverride(name = "runID", column = #Column(name = "RunID")),
#AttributeOverride(name = "position", column = #Column(name = "Position")) })
#Column(name = "RunID")
private int runID;
#Column(name = "Rack")
private int rack;
#Column(name = "Position")
private int position;
#Column(name = "Completionstatus")
private int completionStatus;
#Column(name = "CountingTime")
private double countingTime;
#Column(name = "Cr51Counts")
private double cr51Counts;
#Column(name = "Cr51CPM")
private double cr51CPM;
#Column(name = "Cr51Error")
private double cr51Error;
#Column(name = "MeasurementDateTime")
#Temporal(TemporalType.TIMESTAMP)
private Date measurementDateTime;
#Column(name = "ProtocolID")
private int protocolID;
#Column(name = "ProtocolName")
private String protocolName;
public Measurement() {
}
// Getters and Setters...
}
If I call the service it only returns the first item from the query as xml not the whole collection.
Output:
<measurements>
<measurement>
<runID>418</runID>
<rack>57</rack>
<position>1</position>
<completionStatus>0</completionStatus>
<countingTime>3599.97</countingTime>
<cr51Counts>2225.53</cr51Counts>
<cr51CPM>5.11</cr51CPM>
<cr51Error>44.26</cr51Error>
<measurementDateTime>2012-12-14T14:08:37.0</measurementDateTime>
<protocolID>3</protocolID>
<protocolName>Cr-51 GFR (almindelig blodproever)</protocolName>
</measurement>
</measurements>
How can I make the service return the whole collection of measurements and not just one item?
My data:
So you execute some query "findMeasurementsByRunID" which restricts it to a particular "runId" and you don't understand why you only get 1 Measurement object? Update your query to remove/change the filter so that it allows multiples

Complex order by in JPQL

Let me start by saying I'm not a DB guy. This stuff just confuses me to death, but unfortunately I got roped into doing this at work, so I'm kinda stuck :-)
I'm trying to get a complex order by to work in JPQL, and I'm not having any luck at all.
My current query looks like this:
select distinct msg from CSMessage msg, Error err where msg = err.msg order by err.task.src
What I'm trying to accomplish with this is to get all the msgs's with related errors, then sort the whole thing using the source document (err.task.src), to get all errored messages with the same source to appear together.
Needless to say this doesn't work at all. I get an exception that says; "ORDER BY item should be in the SELECT DISTINCT list"
I've looked over the docs and other sources and there doesn't seem to be anything in there that can help me.
Can anyone point me in the right direction?
Thanks
Edit 1:
The entities look like this:
CSMessage
public class CSMessage extends BaseModel implements Serializable
{
private static final long serialVersionUID = 1L;
.
.
.
Other fields not shown for brevity
.
.
.
#ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "TASK_ID")
private Task task;
}
Error:
public class Error
{
private static final long serialVersionUID = 1L;
#Column(name = "ERR_STRING", length = 255)
private String errString;
#Column(name = "ERR_TYPE")
private Integer errType;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "MSG_ID")
private CSMessage msg;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "SRC_ID")
private CommonSource src;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "TASK_ID")
private Task task;
}
Task
public class Task
{
private static final long serialVersionUID = 1L;
#Column(name = "CORRELATION_UUID", length = 36)
private String correlationId;
#Column(name = "CURRENT_NODE", length = 255)
private String currentNodeName = "empty";
#Column(name = "PROCESS_NAME", length = 255)
private String processName = "empty";
#Column(name = "SITE_ID", length = 10)
private String siteId = "1";
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SRC_ID")
private CommonSource src;
}
You have several options:
select distinct msg from CSMessage msg, Error err where msg = err.msg order by err.task.src
Add err.task.src to select clause (select msg, err.task.src) and change return type in your method
Order in memory instead of bd, using interface Comparable (implements Comparable and use Collections.sort)
Also you could use criteriaQuery or nativeQuery
If your only problem is to order the result list, then one way to do it is to add #OrderBy on the global fields (default order is ASC which can be left out):
#OrderBy("YOUR_COLUMN_NAME")
If you want descending order, the syntax will be:
#OrderBy("YOUR_COLUMN_NAME DESC")
For example:
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
#JoinColumn(name = "TASK_ID")
#OrderBy("TASK_ID") // <-------- ASC
private Task task;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SRC_ID")
#OrderBy("SRC_ID DESC") // <-------- DESC
private CommonSource src;