Aggregate HQL result to list - java

I want to group some sql data using criteria. Lets start with entity which looks mostly like this:
class CityEntity {
private String name;
private Date lastVisited;
}
What I want to do is to find all cities and return result in a tranformer:
class CityTransformer {
private String name;
private List<Date> lastVisited;
}
So as you can see sql result should group by name and put dates to a list.
I want to do it using criteria so it will look almost like this:
Criteria criteria = session.createCriteria(CityEntity.class, "ce");
criteria.setProjection(Projections.projectionList().add(Projections.groupProperty("name"), "name"));
criteria.setResultTransformer(Transformers.aliasToBean(CityTransformer.class));
List<CityTransformer> cities = criteria.list();
The problem is that I don't know how to aggregate dates (lastVisited) to list. Any help?
For example the input will look like this (name, lastVisited):
[Los Angeles, 10-11-2014],
[Los Angeles, 11-12-2011],
[LosAngeles, 10-01-2011],
[Berlin, 01-10-2011]
and output should look like this
[LosAngeles, list[10-11-2014, 11-12-2011, 10-01-2011]],
[Berlin, list[01-10-2011]]

You don't need SQL grouping for that. You can group it in Java:
Criteria criteria = session.createCriteria(CityEntity.class, "ce");
criteria.setProjection(
Projections.projectionList()
.add( Projections.property("ce.name"), "ceName" )
.add( Projections.property("ce.lastVisited"), "ceLastVisited" )
);
List<Object[]> citiesAndDates = (List<Object[]>) criteria.list();
Map<String, CityTransformer> cityTransformerMap = new HashMap<String, CityTransformer>();
for(Object[] citiesAndDate : citiesAndDates) {
String city = (String) citiesAndDate[0];
Date date = (Date) citiesAndDate[1];
CityTransformer cityTransformer = cityTransformerMap.get(city);
if(cityTransformer == null) {
cityTransformer = new CityTransformer();
cityTransformerMap.put(city, cityTransformer);
}
cityTransformer.getLastVisited().add(date);
}
return cityTransformerMap;

Related

Why am I getting multiple duplicate criteria results for an ID if it has a one-to-many mapped table with multiple data

I have a parent Entity called CustomerDetailsEntity. It has following one to many mapping to another Entity IBPSDiscrepancyFieldsEntity.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "Customer_discrepancy_fk", referencedColumnName = "customer_detail_id")
private Set<IBPSDiscrepancyFieldsEntity> ibpsDiscrepancyFieldsEntity;
Now, say for id=123, in IBPSDiscrepancyFieldsEntity Table, I have 4 rows of data.
Now when I hit criteria search on CustomerDetailsEntity I am getting 4 results with same data instead of one. This is how my search criteria is written:
public Map<String, Object> search(Long slrId, String param, String ibpsStatus, Pageable pageable) {
Criteria criteria = getSession().createCriteria(CustomerDetailsEntity.class);
criteria.add(Restrictions.eq("slrDtlId.sellerDtlId", slrId));
criteria.addOrder(Order.desc("modifiedOn"));
if (param != null && !param.isEmpty()) {
Criterion fName = Restrictions.like("customerFirstName", param+"%");
Criterion lName = Restrictions.like("customerLastName", param+"%");
Criterion appNo = Restrictions.like("loanAppNumber", "%"+param+"%");
Disjunction orExp = Restrictions.or(fName, lName, appNo);
criteria.add(orExp);
}
if (ibpsStatus != null && !ibpsStatus.isEmpty()) {
if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_DISCREPANT)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT));
} else if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_RESOLVED)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED));
}
} else {
Criterion discrepant = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT);
Criterion resolved = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED);
LogicalExpression orExp = Restrictions.or(discrepant, resolved);
criteria.add(orExp);
}
criteria.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
criteria.setMaxResults(pageable.getPageSize());
List<CustomerDetailsEntity> result = (List<CustomerDetailsEntity>) criteria.list();
Map<String,Object> countResultMap = new HashMap<>(2);
countResultMap.put(Constant.QUERY_RESULT, result);
logger.info("##### checking here");
logger.info("--->"+result.size());
criteria.setFirstResult(0);
Long count = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
logger.info("Total Count : "+count);
countResultMap.put(Constant.TOTAL_COUNT, count);
return countResultMap;
}
How do I get only 1 result of CustomerDetailsEntity id=123 when I search, instead of 4 duplicate values. In the logs, result.size() shows as 4. Is the Issue with my mapping or criteria method? Please help.

JDBC resultset into java POJO with list member

I have two query and resultsets, in the below code I want to showcase that for a particular userGroupCode I have certain userPreference and employee associated with it. I have written the code below code to display the userGroupCode object:
String query1= "SELECT ug.userGroupCode, ug.userGroupDesc, up.userPreference"
+ "FROM dbo.UserGroup_link ug INNER JOIN dbo.UserPreference up ON ug.userGroupCode = up.userGroupCode";
userGroupCode
userGroupDesc
userPreference
A100
Finance
Mumbai
A100
Finance
Bangalore
A200
Supply Chain
Chennai
A201
Marketing
Delhi
A201
Marketing
Kolkata
A300
Health
Indore
String query2= "SELECT ug.userGroupCode, ug.userGroupDesc, emp.employee_id,emp.name,emp.role"
+ "FROM dbo.UserGroup ug INNER JOIN dbo.employee emp ON ug.userGroupCode = emp.userGroupCode";
userGroupCode
userGroupDesc
employee_id
name
role
A100
Finance
101
Foo1
Developer
A100
Finance
101
Foo1
Team Lead
A200
Supply Chain
091
Test1
Manager
A201
Marketing
591
User1
Analyst
A201
Marketing
1001
Boo1
Scrum Master
A300
Health
1001
Boo1
Developer
I have class UserGroupMapping like:
public class UserGroupMapping {
private String userGroupCode;
private String userGroupCode;
private List<String> userPreference;
private List<Employee> emp;
//getter and setter
}
Another class for Employee is:
public class Employee {
private String employee_id;
private String name;
private List<String> role;
//getter and setter
}
In my stored procedure class I am calling these queries with the help of jdbcTemplate.query();
String userCode = null;
List<String> userPreferenceList = new ArrayList<>();
List<UserGroupMapping> userGroupMappingList = new ArrayList<>();
List<UserGroupMapping> userGroupMappingList1 = new ArrayList<>();
UserGroupMapping userGroupMapping = new UserGroupMapping();
List<Employee> employeeList = new ArrayList<>();
Employee emp = new Employee();
UserGroupMapping userGroupMapping1 = new UserGroupMapping();
jdbcTemplate.query(query1, (rs)->{
String user_group_code = rs.getString("userGroupCode");
String user_group_desc = rs.getString("userGroupDesc");
String user_preference = rs.getString("userPreference");
if(userCode == null){
userGroupMapping.setUserGroupCode(user_group_code);
userGroupMapping.setUserGroupDesc(user_group_desc);
userPreferenceList.add(userPreference);
userCode = user_group_code;
} else if (userCode.equals(user_group_code)) {
userPreferenceList.add(userPreference);
} else {
userGroupMapping.setUserPreference(userPreferenceList);
userGroupMappingList.add(userGroupMapping);
userPreferenceList = new ArrayList<>();
userGroupMapping = new userGroupMapping();
userGroupMapping.setUserGroupCode(user_group_code);
userGroupMapping.setUserGroupDesc(user_group_desc);
userPreferenceList.add(userPreference);
userCode = user_group_code;
}});
userCode = null;
userGroupMapping.setUserPreference(userPreferenceList);
userGroupMappingList.add(userGroupMapping);
jdbcTemplate.query(query2, (rs)->{
String user_group_code = rs.getString("userGroupCode");
String user_group_desc = rs.getString("userGroupDesc");
String emp_id = rs.getString("employee_id");
String name = rs.getString("name");
if(userCode == null){
userGroupMapping1.setUserGroupCode(user_group_code);
userGroupMapping1.setUserGroupDesc(user_group_desc);
emp.setId(employeeId);
emp.setName(name);
employeeList.add(emp);
userCode = user_group_code;
} else if (userCode.equals(user_group_code)) {
Employee emp = new Employee();
emp.setId(employeeId);
emp.setName(name);
employeeList.add(emp);
} else {
userGroupMapping1.setEmployee(employeeList);
userGroupMappingList1.add(userGroupMapping1);
employeeList = new ArrayList<>();
userGroupMapping1 = new userGroupMapping();
Employee emp = new Employee();
userGroupMapping1.setUserGroupCode(user_group_code);
userGroupMapping1.setUserGroupDesc(user_group_desc);
emp.setId(employeeId);
emp.setName(name);
employeeList.add(emp);
userCode = user_group_code;
}});
userGroupMapping1.setEmployee(employeeList);
userGroupMappingList1.add(userGroupMapping1);
List<UserGroupMapping> ugList = Stream.concat(userGroupMappingList.stream, userGroupMappingList1.stream).distinct().collect(Collectors.toList())
return ugList;
The problem is that I want my output to be like :
[
{
"userGroupCode" : "A100",
"userGroupDesc" : "Finance",
"userPreference": ["Mumbai","Bangalore"],
"Employee" : [
"employee_id" : "101",
"name" : "Foo1",
"role" : ["Developer","Team Lead"]
]
}
]
After merging the two list I am getting the below output:
[
{
"userGroupCode" : "A100",
"userGroupDesc" : "Finance",
"userPreference": ["Mumbai","Bangalore"],
"Employee" : []
},
{
"userGroupCode" : "A100",
"userGroupDesc" : "Finance",
"userPreference": [],
"Employee" : [
"employee_id" : "101",
"name" : "Foo1",
"role" : []
]
}
]
Could anyone please help me with few things:
How could I embed the role into the Employee object.
How can I merge the table based on userGroupCode and userGroupDesc.
I am feeling the code is not that performance optimised, How could I optimised this code.
Thank you in advance.
Ola,
You can group by using Map taking id as key and value as object ( to be aggregated into) . For example:
if(map.containes(key))
{
get object from map and do Ops.
}
else
{
1. Create new object
2. Do set Ops on Object
3. Add to map.
}
You basically have 2 solutions,
Write a query that returns all results with joins and do filtering in java, quite easy to achieve with 2 maps (one for the UserGroup the other for the Employee.
Write a query and aggregate the duplicates using list in the query itself.
SELECT ug.userGroupCode, ug.userGroupDesc, up.userPreference, emp.employee_id,emp.name,emp.role
FROM dbo.UserGroup_link ug
INNER JOIN dbo.UserPreference up ON ug.userGroupCode = up.userGroupCode
INNER JOIN dbo.employee emp ON ug.userGroupCode = emp.userGroupCode
Then use a RowCallbackHandler to achieve what you want (instead of a ResultSetExtractor.
Map<String, UserGroup> userGroups = new HashMap<>;
Map<Integer, Employee> employees = new HashMap<>;
jdbc.query(query, (rs) -> {
String userGroupCode = rs.getString("userGroupCode");
String emp_id = rs.getString("employee_id");
UserGroupMapping ugm userGroups.computeIfAbsent(userGroupCode, {
UserGroupMapping ugm1 = new UserGroupMapping();
ugm1.setUserGroupCode(userGroupCode);
ugm1.setUserGroupDesc(rs.getString("userGroupDesc");
ugm1.setUserPreference(new ArrayList<>());
ugm1.getEmployee(new ArrayList<>());
return ugm1;
});
ugm.getUserPreference().add(rs.getString("userPreference"));
Employee emp = employees.computeIfAbsent(emp_id, {
Employee emp1 = new Employee();
emp1.setName(rs.getString("name"));
emp1.setRole(new ArrayList<>());
ugm.getEmployee().add(emp);
return emp1;
});
emp.getRole().add(rs.getString("role"));
});
return userGroups.values();
The above code will get all UserGroupMapping objects from the result including all the Employee instances. The temporary maps are needed to determine if record has already been shown.
Another solution would be to use list in your query and some GROUP BY statement to let the query do part of the aggregation. That way you could make it a bit easier to create an Employee.

How to do this using collections/ stream api

I have this Requirement :
Construct each Doctor with their respective Appointments and return a doctorList. Input is Appointment List for whole Hospital.
What I have is the followings:
Two classes given with the following variables.
Class Appointment{
String doctorName;
DateTime startTime;
DateTime endTime;
String speciality;
//Getters-Setters
}
Another Class :
Class Doctor{
String name;
String speciality;
List <Appointment> appointments;
//Getters-Setters
}
Requirement is :
Construct each Doctor with their respective Appointments and return a doctorList.
Input is Appointment List for whole Hospital.
Can some body tell what should be my approach?
I hope this will help you. Use Map to maintain the appointments of each doctor. In the below code you can get the appointment details using the doctor's name.
`//appointments - input list
Map<String, List<Appointment>> map = new HashMap<>();
for(Appointment app : appointments) {
if(!map.containsKey(app.getDoctorName())) {
List<Appointment> li = new ArrayList<>();
li.add(app);
map.put(app.getDoctorName(),li);
} else {
map.get(app.getDoctorName()).add(app);
}
}`
You can use HashMap.
HashMap<Doctor, ArrayList> doctorList = new HashMap<>();
Might not be a good idea in your case mainly because I do not understand what you are trying to achieve.
If you need match doctors with Appointment by both Speciality and Name, then:
Map<String, List<Appointment>> map = appointmentList
.stream()
.collect(Collectors.groupingBy(e -> e.getDoctorName() + e.getSpeciality()));
List<Doctor> doctors = new ArrayList<>();
for (String doctorNameAndSpeciality : map.keySet()) {
List<Appointment> appointments = map.get(doctorNameAndSpeciality);
if (!appointments.isEmpty()) {
Doctor doctor = new Doctor();
Appointment appointment = appointments.get(0);
doctor.setName(appointment.getDoctorName());
doctor.setSpeciality(appointment.getSpeciality());
doctor.addAllAppointments(appointments);
}
}
return doctors;
if you need use only 'name', then just remove 'speciality'

How to do batch insert using neo4j cypher and java

I am performing single insert using for each loop for each value.
How can we do batch insert using cypher queries.
Here is my code...
Controller
#PostMapping("/geohash")
public Set<String> create(#RequestParam String name, #RequestBody LatLng[] latLngs) {
double[][] polygonPoints = convertTo2dArrayOfLatLng(latLngs);
Set<String> geoHashesForPolygon = GeoHashUtils.geoHashesForPolygon(6, polygonPoints);
for (String geohash : geoHashesForPolygon) {
min = Math.min(min, geohash.length());
geohashes = neoService.create(name, geohash);
}
return geoHashesForPolygon;
}
I want to insert each geoHashesForPolygon as single node..
Cypher query
#Query("MATCH (c:C) WHERE c.name = {name} CREATE (g: G{name : {geohash}} )<-[:cToG]-(c) RETURN c,g")
public GeohashOfCluster create(#Param("name") String name,#Param("geohash") String geohash);
You can have the params in a list and unwind it to create nodes. Your query would be like WITH [{name:'',geohash:''},{name:'',geohash:''},{name:'',geohash:''}] as data UNWIND data as d MATCH (c:C) WHERE c.name = d.name CREATE (g: G{name :d.geohash} )<-[:cToG]-(c) RETURN c,g
Hope this helps!

Predicates on Join not working

If I am using a Join to create predicates in a criteriaquery I won't get any results. The same predicates are returning entities when I am using the Game table as root.
Working Query:
CriteriaQuery<Game> query = cb.createQuery(Game.class);
Root<Game> root = query.from(Game.class);
List<Predicate> predicates = new LinkedList<Predicate>();
if(!selectedPlatforms.isEmpty()) {
predicates.add(root.get(Game_.type).in(TypeConverter.convert(selectedPlatforms)));
}
if(!selectedCategories.isEmpty()) {
Join<Game, String> collection = root.join(Game_.categories);
predicates.add(collection.in(cb.literal(selectedCategories)));
}
if(!selectedGames.isEmpty()) {
predicates.add(cb.isTrue(root.get(Game_.name).in(selectedGames)));
}
query.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
games = em.createQuery(query).getResultList();
Not working query:
CriteriaQuery<Hit> query = cb.createQuery(Hit.class);
List<Predicate> predicates = new LinkedList<>();
Date startDate = null;
Date endDate = null;
Root<Hit> hitRoot = query.from(Hit.class);
switch (time) {
case "Week":
startDate = new DateTime().withWeekOfWeekyear(timeValue).withDayOfWeek(DateTimeConstants.MONDAY).toDate();
endDate = new DateTime().withWeekOfWeekyear(timeValue+1).withDayOfWeek(DateTimeConstants.SUNDAY).toDate();
}
predicates.add(cb.and(cb.greaterThanOrEqualTo(hitRoot.<Date>get("hitDate"), startDate), cb.lessThanOrEqualTo(hitRoot.<Date>get("hitDate"), endDate)));
Join<Hit, Game> gameJoin = hitRoot.join("game", JoinType.LEFT);
if(!selectedPlatforms.isEmpty()) {
predicates.add(gameJoin.get(Game_.type).in(TypeConverter.convert(selectedPlatforms)));
}
if(!selectedCategories.isEmpty()) {
Join<Game, String> collection = gameJoin.join(Game_.categories);
predicates.add(collection.in(cb.literal(selectedCategories)));
}
if(!selectedGames.isEmpty()) {
predicates.add(cb.isTrue(gameJoin.get(Game_.name).in(selectedGames)));
}
query.groupBy(hitRoot.get("hitDate"), hitRoot.get("shop"));
query.orderBy(cb.asc(hitRoot.get("shop")));
query.where(predicates.toArray(new Predicate[predicates.size()]));
List<Hit> results = em.createQuery(query).getResultList();
The following part is responsible for not returning any machting entity. The same part just applied to a Root instead of Join like in the first query is returning machting entites. Without this part everything else is working.
if(!selectedGames.isEmpty()) {
predicates.add(cb.isTrue(gameJoin.get(Game_.name).in(selectedGames)));
}
The problem resolved itself by restarting the application server in 'Run' mode.

Categories

Resources