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...
Related
I have an entity with around 20 columns. At any point in time I don't want to fetch all the columns but only the selected columns and I don't know what could those columns be since it's coming from the request as a parameter. What's the right way to achieve this ?
I have tried implementing this using the native queries but having a hard time parsing the result set to the desired response format.
Query query = entityManager.createNativeQuery("SELECT name, model, desc FROM Product");
query.getResultList();
Now this result set is so generic that I am not able to convert it to the required product model by passing Product.class as a parameter to the query as it fetches only the 3 fields and the rest are missing. And I can't have a predefined projection as it's coming from the API request as a parameter.
Here's how I've done it. Apparently you can get the ResultSet as a list of Tuple. I just had to iterate over this list and make a Map for each entry in result set so I can map it using ModelMapper. Here tags are a list of columns to be read.
Query query = entityManager.createNativeQuery(preparedQuery,Tuple.class);
List<Tuple> resultList = query.getResultList();
List<Product> resultDto = new ArrayList<>();
for (Tuple tuple : resultList) {
HashMap<String, Object> data = new HashMap<>();
for (String tag : tags) {
data.put(tag, tuple.get(tag));
}
resultDto.add(mapper.map(data, Product.class));
}
return resultDto;
Thanks for all who responded to this.
The response you get will be in this format
Map< String,Object >.
Create a ProductDTO class and convert this map into ProductDTO with the
help of ModelMapper.
ProductDTO productDto = new ModelMapper().map(resultMap, ProductDTO.class);
Suppose you want other fields means just add the #JsonInclude annotation at top of the entity class.
#JsonInclude(Include.NON_NULL)
public class Product{
Edit:
Create the Constructor in Product Class
public Product(String name,String model,String desc){
this.name=name;
this.model=model;
this.desc=desc;
}
String query = String.format("select new pakageName.Product(e.name, e.model,e.desc) from Product as e);
Query queryObject = (Query) em.createQuery(query);
List<Product> result = queryObject.getResultList();
Native query only returns the Object. So try this like the below mentioned one.
I have two different csv files having data on two different entities and I have to merge two different csv files to create one on the basis of sql join type equijoin and left join.
so I have created first entity as class name Customer having attributes:
int CustomerId ;
String CustomerName;
int OrderId;
And List of object of this class like:
Customer c1 = new Customer(CustomerId, CustomerName, OrderId);
1 million objects..
List<Customer> cust = new ArrayList<>();
cust.add(c1);
cust.add(c2);
so on to make list of 1 million object.
Similarly, I have created class of second entity Order having attributes:
int orderId;
String orderName;
Date orderdate;
Order o1 = new Order(orderId, orderName, orderdate);
so on 1 million object
List<Oder> order = new ArrayList<>();
Now I need to merge both the object on the basis of orderId and generate third object having result class having all the attributes from both the classes described above.
Please suggest me solution using java stream 8 to map both the streams of list to create inner join and left join type example in the third new result class.
Aside from the getters, your Customer class should have the following method:
public boolean orderMatch(Order order) {
//fixed the attribute name so it would be in camelCase
return orderId == order.getId();
}
Of course, this implies that Order has a getId() getter method to get its id attribute.
Finally, you'll need a CustomerExtended class.
class CustomerExtended {
int customerId ;
String customerName;
Order customerOrder;
public CustomerExtended(Customer customer, Order order) {
customerId = customer.getId();
customerName = customer.getName();
customerOrder = order;
}
}
Now, you can create a Function which would search for the corresponding Order and append it to a Customer:
Function<Customer,CustomerExtended> extendCustomer = (c) ->{
//I used the more descriptive name orderList instead of o1.
Optional<Order> order = orderList.stream()
.filter(c::orderMatch)
.findFirst();
if(order.isPresent()) {
return new CustomerExtended(c,order.get());
}
return null;
};
And then you can apply it to the Customer list through a map.
List<CustomerExtended> newCustomerList = customerList.stream()
.map(c -> extendCustomer.apply(c))
.collect(Collectors.toList());
EDIT: A few last notes
There should be some check that the ID numbers are not duplicate either when adding the objects to the lists, or when the lists are populated.
For semantic purposes, The Customer object as it is should be renamed CustomerOrder or be separated into an object only for customer info and an object which would store the relation between customer and order.
The case where an order is not found should be better handled and throw an exception.
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.
I have an object product in my application. This object has a list of items inside it (ArrayList). In the database I have 2 tables: Product and Item. Items have ids of products connected with foreign keys to product id.
It is quite simple to get the Product from database and then get a list of items for it.
How to get a list of products from the database, which will contain all the relevant items inside?
As far as I know, it is not efficient to call dao for every product in the list. How can I manage the query, or object creation to make it more efficient?
I am using Spring JdbcTemplate to get data from Database. Currently I am getting list of products in DAO class and straight from there I call ItemDAO for every item. The database is MySQL.
Update
Okay, probably I was unclear in my first approach. The goal is to get nested lists from Database and how to make it performance-wise.
public class Product {
private Integer id;
private String name;
private String description;
private ArrayList<Item> items;
private Timestamp timeAdded;
}
public class Item {
private String name;
private Float price;
private String type;
private Integer productId;
}
public ArrayList getProducts(String searchName) throws SQLException {
ArrayList<Product> products = new ArrayList<Product>();
String sql = "SELECT * FROM product WHERE name LIKE '%" + searchName + "%'";
List<Map> rows = getJdbcTemplate().queryForList(sql);
for (Map row : rows) {
Product product = setNewProduct(row);
products.add(product);
}
return products;
}
private Product setNewProduct(Map row) {
Product product = new Product();
product.setId((Integer) row.get("id"));
product.setName((String) row.get("name"));
product.setDescription((String) row.get("description"));
ItemsDao itemsDao = (ItemsDao) appContext.getBean("itemsDao");
ArrayList<Item> items = itemsDao.getItems(product.getId());
if (items.size() > 0) {
product.setItems(items);
}
product.setTimeAdded(new java.util.Date(((Timestamp) row.get("time_added")).getTime()));
}
In ItemsDao class I just get a list of items for the product, based on the ID sent.
So the question is how to call list of products and get list of items inside every product? (maybe in one complex SQL statement?). Now the performance is horrible, because when there are 30 products with 10 items each, to get the whole list of products it will have to call database so many times.
The answer is that you can't when using an ORM, they all perform like crap when you have any amount of data. OK if you lazy-load the related collections and only use a few, but if you need to display field from the child objects then you can get 100,000+ queries for a page load. Believe me I just ahd to optimise a page using Doctrine that ran 200,000 queries.
What you have to do is either to paginate the results, or to write a custom query that joins the data and then process the results with a loop control: eg
$sql = "SELECT * FROM table1 t1 JOIN table2 t2 ON t1.id = t2.fk_id";
// run query and get resdults
$cur_prod = '';
While( // fetch a row ){
if($row['prod'] != $cur_prod){
$cur_prod = $row['prod'];
// start the new record in your layout
}
echo $row['t1.f1'];
echo // blah blah blah the related data
}
What I am getting at here is that you have to track the parent product and manage the output when it changes.
So I want to create a kind of table using ArrayLists both for columns and rows. It's like an ArrayList inside another ArrayList. ArrayList of the columns(each with a different data type) where each column is also an ArrayList holding data of the type mentioned for that column.
I could really use your help about how can I make a table of this kind and how can I handle updating the info. Thanks!
You can create such a table like this:
List<List<?>> table = new ArrayList<>();
This is what you were asking for, but I don't think that this structure is useful. You would have to create a new ArrayList for each column of your table. And the data in one column should always have the same type. Otherwise you could never get the data in a cell, because you would not know to what type it has to be casted. As an example look at this table:
name | age
------+-----
alice | 25
------+-----
bob | 30
Using the lists-sturcture, the table can be created like this:
table.add(new ArrayList<String>());
table.add(new ArrayList<Integer>());
Adding and accessing persons is not comfortable and error-prone:
table.get(0).add("alice");
table.get(1).add(25);
table.get(0).add("bob");
table.get(1).add(30);
String nameOfAlice = (String) table.get(0).get(0);
Integer ageOfAlice = (Integer) table.get(1).get(0);
String nameOfBob = (String) table.get(0).get(1);
Integer ageOfBob = (Integer) table.get(1).get(1);
This can be done much more effective if you create a class for the rows:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getters & setters
}
The declare tha table:
List<Person> persons = new ArrayList<>();
Now adding and accessing a person is much easier:
persons.add(new Person("alice", 25));
persons.add(new Person("bob", 30));
Person alice = persons.get(0);
Person bob = persons.get(1);