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
Related
I have two list of type String and an object (consider Employee). String type list have employee codes. Here I need to check if Employee list have any object of code(attribute) saved in String. Below is my employee class
public class Employee {
public String code;
public String id;
// getters, setters and constructor
}
Here I am able to find whether employees have code saved in the given String List (employeeUserGrpCodes).
public static void main(String[] args) {
final List<String> employeeUserGrpCodes= Arrays.asList("ABCWelcome","ABCPlatinum","SuperEmployee");
List<Employee> empList=new ArrayList<Employee>();
Employee k1= new Employee("KCAEmployee","1");
Employee k2 = new Employee("ABCWelcome","2");
empList.add(k1);
empList.add(k2);
List<Employee> empListN = empList.stream().filter(i->employeeUserGrpCodes.stream().anyMatch(j->j.equalsIgnoreCase(i.getCode()))).collect(Collectors.toList());
List<String>newEmpList = empList.stream().map(a->a.getCode()).collect(Collectors.toList()).stream().filter(employeeUserGrpCodes::contains).collect(Collectors.toList());
if(!empListN.isEmpty() || !newEmpList.isEmpty())
{
System.out.println("Employee have employeeUserGrpCodes");
}
}
In the above code, both approaches are working that is List 'empListN' and List 'newEmpList'. Is it possible to do the same with the help of Predicates which I can easily put in String 'anymatch' like
Predicate<Employee> isEmpUserGroup = e -> e.getCode().equalsIgnoreCase(employeeUserGrpCodes.stream())
boolean isRequiredEmployee = empList.stream().anyMatch(isEmpUserGroup);
First of all for the purpose of knowing if Employee have employeeUserGrpCodes you don't need the two lists because is empListN is not empty newEmpList won't be as well, so we can use only of the two lists, and then, related with the use of the predicates, you are using them already in the filter expressions, you can have something like this for the empListN list:
Predicate<Employee> employeePredicate = e -> employeeUserGrpCodes.stream().anyMatch(c -> c.equalsIgnoreCase(e.getCode()));
List<Employee> empListN = empList.stream().filter(employeePredicate).collect(Collectors.toList());
You can notice that the Predicate is using another predicate as well
c -> c.equalsIgnoreCase(e.getCode())
So you can also replace the if condition and avoid using a temporary list if you test your predicate against the employee list like this:
if (empList.stream().anyMatch(employeePredicate)) {
System.out.println("Employee have employeeUserGrpCodes");
}
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.
Is it possible to use a Object if i only got the String? I have an Object 'John' from the Class 'Student'. In the Class 'Student' is a ArrayList 'friends'. I want to access the Object 'John' by using a String (the name of the object). (Line 2 in the example)
public void addFriend(Student student, String friend) throws IOException{
student.friends.add(friend);
System.out.println("Friend: " + friend + " added to List of " + student);
}
I hope you understand what i mean (i am sorry for my terrible english :/ )
You can use map for this problem.
Map<String, Student> friends = new HashMap<String, Student>();
friends.put("John", objectOfJohn);
Student target = friends.get("John");
If I understand correctly, you want to print out name of a student using the variable student. If this is the case, you may want to override the toString() method inside Student class which returns name of that student. For example:
public class Student {
private String firstName;
private String lastName;
// ... Other methods
// here is the toString
#Override
public String toString() {
return firstName + " " + lastName;
}
}
Then you can do something like this to print out the student name:
System.out.println("Friend: " + friend + " added to List of " + student.toString());
You have a Student Class and you have created in some point some objects of this class.
Student john = new Student();
Student mike= new Student();
Student mary = new Student();
and you have all these objects stored in an Arraylist allStudents
ArrayList<Student > allStudents= new ArrayList<>();
allStudents.add(john);
allStudents.add(mike);
allStudents.add(mary);
So, if you want to find john from this list you may do:
Option A
If the name for your case is unique and exists also as an attribute in your object, you can iterate the Arraylist and find it:
Student getStudentByName = new Student();
for(Student student : allStudents){
if(student.getName().equals("john")){ //If name is unique
getStudentByName = student;
}
}
Option B
Add all objects in HashMap
Map<String, Student> allStudents= new HashMap<>();
allStudents.put("john", john);
allStudents.put("mike", mike);
allStudents.put("mary", mary);
And then get your desired object by:
Student target = friends.get("john");
Be mind that if you add again :
allStudents.put("john", newStudentObject);
the HashMap will keep the last entry.
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)
Here is the scenario.
I am trying to get a list of records in my resource layer. It has the following code;
PagedResponse<Person> pagedResponse= new PagedResponse<Person>();
There is a call to Business implementation as
pagedResponse = facadeLocator.getPersonFacade().findAllRecords(getUserId(), fromrow, maxrows);
Now in the Business implementation, I use named query as;
Query query = getNamedQuery("Person.findAll");
I have the response as
pagedResponse = executePagedQuery(query, firstResults, maxResults);
and inside executePagedQuery(), I have;
List resultList = query.getResultList();
The response returned pagedResponse is of custom type PagedResponse class with 2 members;
private Long totalRecords;
private List<T> records;
Now in my Person class, I have
#NamedQuery(name = "Person.findAll", query = "Select DISTINCT(p.personName), p.personAge, p.personGender from Person p where p.personAge = :personAge")
Now here is what happens at runtime.
I get the "records" as Vector with members as
[0] = Object[]
[1] = Object[]
Coming back to the resource layer, I want to iterate through the response and set it in a list
List<Person> personList = new ArrayList<Person>();
So what are the options I have.
I have tried doing
for(Object[] person: pagedResponse.getRecords()) {
Person p = new Person();
p.setPersonName((String)person[0]);
// Setting other values
personList.add(p);
}
But it says incompatible types for the line
for(Object[] person: pagedResponse.getRecords()) {
Just to add, I did not face any incompatible type issue when my query did not return select columns and instead returned all the columns like;
query = "SELECT p FROM Person p WHERE
So I have 2 questions;
1. Why was there no type casting issues when I was returning all the columns using the named query (It showed the type as "Person" and not generic type as showing after using the named query with specific columns)
2. Using the query with specific columns, what is the right approach to set the values returned from the query in the resource layer ?
The query with many individual SELECTed values is supposed to return a list of lists. Maybe you want to define a bean with an appropriate constructor:
package com.foo;
public class PersonData {
private String name;
private int age;
private Sex gender;
public PersonData(String name, int age, Sex gender) {
this.name = name;
this.age= age;
this.gender = gender;
}
// getters/setters
}
And run the query as:
SELECT NEW com.foo.PersonData(DISTINCT(p.personName), p.personAge, p.personGender)
FROM Person p WHERE p.personAge = :personAge
Now getResultList() should return a list of PersonData objects. Though I haven't used the nested new PersonData(DISTINCT(...)) syntax...