How to work with maps in JPQL? - java

Here is the overview of a problem..
public class Student
{
private Map<Test,MarkWrapper> marks;
...
}
public class MarkWrapper
{
private List<Mark> marks;
...
}
public class Test
{
private String name;
private Date date;
..
}
public class Mark
{
private int mark;
private int total;
private float average;
...
}
Now, I would like to retrieve students who got 90 marks. Rather than changing the architecture by removing the map, how to solve this problem?
In the Map class there is no getter method to get the values in it. If this can't be achieved then how would i do it with Criteria API?
Thank you.

I would like to retrieve students who got 90 marks
If you want to retrieve student whose marks is exact 90 then make your map key as 90 and add value as student.
Example:
Pun on map to those students who got exact 90 marks by iterating studentList
Map<Integer,Student> studentMap=new HashMap<Integer, Student>();
List<Student> studentList= <yourStudentList>;
for(Student student : studentList){
if(student.getMarks() == 90){
studentMap.put(90,student);
}
}
Edit:
I assume you have entity class name as Student and have property marks and so on.
Using JPQL:
You can do to get students list from database either
String jpql="Select s from Student s where s.marks =:marks ";
Query query=em.createQuery(jpql);
query.setParameter("marks", 90);
List<Student> studentList=query.getResultList();//returns all students who got marks 90
or
String jpql="Select s from Student s ";
Query query=em.createQuery(jpql);
List<Student> studentList1=query.getResultList();//returns all students
and you can put in map to all students who got 90 marks by iterating studentList1 like I said earlier

An example JPQL query string:
SELECT s FROM Student s JOIN s.marks sm
WHERE VALUE(sm) IN (
SELECT mw FROM MarkWrapper mw JOIN mw.marks mwm
WHERE mwm.mark = :value
)
where :value is query parameter (i.e. 90)

Related

Java streams - getting a map from list of maps

I have a class University that contains (among other things) a list of semesters and a method:
public Map<String,Integer> gradesMap(Student s,Semester s)
that should return a map of grades for a given student in a given semester.
Classes Semester and Course look something like this:
public class Semester{
...
private List<Course> courses;
public List<Courses> getCourses(){return courses;}
...
}
public class Course{
...
String courseName;
private Map<Student,Integer> courseGrades;
public Map<Student,Integer> getCourseGrades(){return courseGrades;}
public String getCourseName(){return courseName;}
...
}
I tried writing something like:
Map<String,Integer> grades=semester.getCourses().stream().
forEach(c->c.getCourseGrades()).collect(Collectors.toMap(key,value));
but I'm not sure how to fetch the key and value for my map. Any suggestions would be appreciated.
Edit: Output map should contain a Course name and a grade.
You can get the course grade from the Course Grade map by the student and collect grade as the map's value and course name as the key.
Map<String,Integer> gradesMap =
semester.getCourses()
.stream()
.collect(Collectors.toMap(c -> c.getName(),
c -> c.getCourseGrades().get(studentObj)));
Note: Make sure you defined equals() and hashCode() for Student

Get element from list that contains another list

I have this configuration with java:
Student {
private List<Note> notes;
}
Note {
private int value;
// Constructor - getters - setters
}
School {
Private List<Student> students;
// Constructor - getters - setters
}
I want the following behavior:
Students :
S1 : note1(value=10), note2(value=16)
S2 : note1(value=7), note2(value=18), note3(value=2)
S3 : note1(value=19)
I want to manage an object with a list of schools as:
Manage {
private List<School> schools;
}
And I want to get the school who has the student with the higher note.
In this example: the result would be S3 because we have one student with the higher note 19.
How can I achieve this behavior using Java Stream?
You can create a Stream<Map.Entry<School,Student>> of all the pairs of Schools and Students, and then find the entry having the Student with the max value.
For that purpose I suggest adding to Student class a getMaxValue() method that would return the max value of all the Students Notes.
Optional<School> school =
schools.stream()
.flatMap(sc -> sc.getStudents()
.stream()
.map(st -> new SimpleEntry<>(sc,st)))
.collect(Collectors.maxBy(Comparator.comparing(e -> e.getValue().getMaxValue())))
.map(Map.Entry::getKey);

Querying entity by parameter inside list property

I am trying to write a query in HQL but it doesn't work. The query filters Students by Grades. Grade is a ManyToMany list inside Student's class.
Exapmle (Student.java):
#NamedQuery(name = "getStudentsByGrade" , query = "from Student where :grade in grades")
The Student class has list property:
Private List<Grade> grades;
This :grade member of grades was what i looked for.

Best way to cast java.lang.object to customized object on using HQL select statement on multiple fields on an Entity Class

In this scenario rather than doing complete select on object,have decided to go for select statement on fields required only.Hence the queries generated will be less.
Once the result is in,i want to cast back to original values and return them to calling method.
Please suggest any alternative efficient approach.
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
#Column(name="enroll_id")
private String enrollmentId;
public Student(Integer id, String enrollmentId) {
super();
this.id = id;
this.enrollmentId = enrollmentId;
}
// source code continues
}
public List<Student> getStudentList(){
Query multipleSelect=em.createQuery("select student.id,student.enrollmentId from Student as student");
List<Object[]> studentList=multipleSelect.getResultList();
List<Student> studentArrayList=new ArrayList<Student>();
for(Object[] list:studentList){
Integer id=((Integer)list[0]);
String eId=((String)list[1]);
studentArrayList.add(new Student(id, eId));
}
return studentArrayList;
}
If you're asking for a way to avoid casting each row from the resultList and having to manually create Student object then try using "JPQL Constructor Expressions"
You're select query can be modified as:
"select NEW com.FullyQualifiedName.Student(student.id,student.enrollmentId) from Student as student"
and accept the query result directly as
List<Student> studentList=multipleSelect.getResultList();
or Simply:
public List<Student> getStudentList(){
return em.createQuery("select NEW com.FullyQualifiedName.Student(student.id,student.enrollmentId) from Student as student").getResultList();
}
Note:
Make sure Student constructor is called using fully qualified name.
Do not use JPQL with createNativeQuery.
If you want the output of the query to be of Student type then you'll have to create the query in a different way, i.e.,
TypedQuery<Student> multipleSelect=em.createQuery("select NEW your_package.Student(student.id,student.enrollmentId) from Student as student"
, Student.class);
List<Student> students = multipleSelect.getResultList();
However, this is not a good way to do this as the return type of the method would suggest that it is returning a completely filled Student object. Also, you'll have to make constructors for every combination. I would rather suggest you fetch a map, i.e.,
TypedQuery<Map> multipleSelect=em.createQuery("select NEW map(student.id as id,student.enrollmentId as eid) from Student as student"
, Map.class);
List<Map> students = multipleSelect.getResultList();
This will return you a map with key as "id" and value as the actual id of the student.

ClassCastException while using GROUP BY clause in JPA

I am working with JPA. while doing GROUP BY clause example it's throwing ClassCastException.
Below Is My code:
public class StudentGrouping
{
public static void main(String[] args)
{
EntityManager entityManager = EntityManagerUtil.getEmf()
.createEntityManager();
try {
EntityTransaction entr = entityManager.getTransaction();
entr.begin();
Query query = entityManager
.createQuery("SELECT student.studentName, SUM(student.studentAge) FROM Student student GROUP BY student.studentName");
List<?> list = query.getResultList();
Iterator<?> iterator = list.iterator();
while (iterator.hasNext())
{
System.out.println("entered into loop");
Student student = (Student) iterator.next();
System.out.print("Student Name:"+student.getStudentName());
System.out.print(" Age:"+ student.getStudentAge());
System.out.println();
}
entr.commit();
System.out.println("success");
}
catch (Exception e)
{
e.printStackTrace();
} finally {
entityManager.close();
}
}
}
Below is The Expection:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.demo.entities.Student
at com.demo.action.StudentGrouping.main(StudentGrouping.java:28)
My POJO class Fields are:
#Column(name = "studentName")
private String studentName;
#Column(name = "studentAge")
private int studentAge;
Is my GROUP BY clause query is wrong.
Student student = (Student) iterator.next(); is the problem as you aren't actually pulling back an entire student.
SELECT student.studentName, SUM(student.studentAge) FROM Student
student GROUP BY student.studentName
Your query is pulling back these two fields. If you want to map to a student object, you'd have to use the following.
FROM Student student
Then do the calculation by hand on the data. If you wanted to use your original query, you'd have to parse each individual value with, rather than using iterator lets say they were in a resultList.
for (Object[] result : resultList) {
String studentName = (String) result[0]
Integer age = (Integer) result[1];
}
This is because I am sure that you Student class does not look like
String studentName;
Integer yyyy;
which is what you are getting when you ask for
student.studentName, SUM(student.studentAge)
I suggest that you either create a class that looks like your result, or just treat the result as an Object[]
As in
Object student[] = (Object[])iterator.next();
System.out.print("Student Name:"+student[0]);
System.out.print("Max Age:"+student[1]);
Unless you have defined the Student class to be a String and a number, you're not getting back a Student class, so you can't cast it to that in this line:
Student student = (Student) iterator.next();
That, and you probably should also define what kind of classes your List and Iterators are handling.
The problem is you are actually not selecting the student instead you are trying to select custom values from student.
try this:
First create a constructor inside the entity with two param ie. the name and age.
then use this query:
SELECT NEW com.sample.Student(student.studentName, SUM(student.studentAge)) FROM Student student GROUP BY student.studentName;
You need to specify the full class name (sample:com.sample.Student) in the query

Categories