#Id
#Column(name="Item", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int itemId;
#Column(name="ItemName")
private String itemName;
#Column(name="ItemPrice")
private double itemPrice;
#Column(name="status")
private String status;
#Column(name="image")
private String image;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RestaurantId", nullable = false)
private Restaurant restaurant;
this is my Entity class ,
public List<FoodItem> getFoodItems(Restaurant restaurant) {
Session session=getSession();
List<FoodItem> list=null;
NativeQuery<?> query = session.createNativeQuery("SELECT " +
" \"Item\"," +
" \"ItemName\"," +
" \"ItemPrice\"," +
" \"RestaurantId\"," +
" \"status\"," +
" \"image\" " +
"FROM \"SYSTEM\".\"FoodItem\" where \"RestaurantId\"="+restaurant.getRestaurantId());
list = (List<FoodItem>) query.getResultList();
return list;
}
when i run this method, it doesn't return me a List<FoodItem> instead it returns a List<Array> like this,
[
[
1,
"Pasta",
55,
14,
"Veg",
null
],
[
2,
"Burger",
35,
14,
"Veg",
null
]
]
and if i try to set the restaurant object to null in each object in the list,
for(int index=0 ;index< list.size();index++)
list.get(index).setRestaurant(null);
i got ClassCastException.
i need the response in key : value pair as per my entity class
can anyone solve this for me.
thanks.
[update] Solved!
You suppose to get list of object array from the native query. It will not construct type objects out of the box for you. If you want typed list, you need to do JPQL instead of native query.
List<Object[]> listResults = query.getResultList();
Iterate over the list and construct the typed object list -
List<FoodItem> foodItems = new ArrayList();
for (Object[] record : listResults) {
FoodItem item = new FoodItem();
// set values from record, do necessary casts as well.
foodItems.add(item);
}
NativeQuery has a bounded type parameter. Use it and pass expected resultClass as a second parameter for a proper generic resolution and you'll get an expected result.
NativeQuery<FoodItem> query = session.createNativeQuery("SELECT " +
" \"Item\"," +
" \"ItemName\"," +
" \"ItemPrice\"," +
" \"RestaurantId\"," +
" \"status\"," +
" \"image\" " +
"FROM \"SYSTEM\".\"FoodItem\" where \"RestaurantId\"="+restaurant.getRestaurantId(),
FoodItem.class);
List<FoodItem> list = query.getResultList();
return list;
Related
I have two entities, Recipes and Ingredient:
Recipes looks like:
public class Recipes {
private Long id;
private String name;
private Integer serveCount;
private RecipesType type;
#OneToMany(mappedBy = "recipes",
cascade = CascadeType.ALL)
private List<Ingredient> ingredients = new ArrayList<>();
}
And Ingredient looks like:
public class Ingredient {
private Long id;
private String name;
private Integer count;
private String unit;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "RECIPES_ID", nullable = false)
private Recipes recipes;
}
I was asked to implement filtering service with these parameters:
Recipes type(Vegetarian, Dairy, Fast food)
Number of serving
Specific ingredients (either include or exclude)
So I created a FilterRequestDTO based on them and implemented my service as below:
#Override
public ListResponseDTO<RecipesDTO> getFilteredRecipes(FilterRecipesDTO filterRecipesDTO) {
ListResponseDTO<RecipesDTO> response = new ListResponseDTO<>();
var temp = repository.findFilteredRecipes(
Objects.nonNull(filterRecipesDTO.getType()) ? RecipesType.parseRecipesType(filterRecipesDTO.getType()) : null,
Objects.nonNull(filterRecipesDTO.getServeCount()) ? filterRecipesDTO.getServeCount() : null,
filterRecipesDTO.getIncludeIngredients().size() > 0 ? filterRecipesDTO.getIncludeIngredients() : null,
filterRecipesDTO.getExcludeIngredients().size() > 0 ? filterRecipesDTO.getExcludeIngredients() : null,
PageRequest.of(filterRecipesDTO.getPage(), filterRecipesDTO.getSize()));
if (temp.isPresent()) {
response = new ListResponseDTO<>(filterRecipesDTO.getPage(), temp.get().getNumberOfElements());
for (Recipes recipes : temp.get().getContent())
response.getData().add(new RecipesDTO().from(recipes));
}
return response;
}
And it calls my Repository method layer which looks like:
#Query("select r from Recipes r join Ingredient i on r.id = i.recipes.id " +
"where (:typ is null or r.type = :typ) " +
"and (:cnt is null or r.serveCount = :cnt) " +
"and (:inc is null or i.name in :inc) " +
"and (:exc is null or i.name not in :exc)")
Optional<Page<Recipes>> findFilteredRecipes(#Param("typ") RecipesType type,
#Param("cnt") Integer serveCount,
#Param("inc") List<String> includeIngredients,
#Param("exc") List<String> excludeIngredients,
Pageable page);
But when I want to filter results with given includeIngredients and excludeIngredients lists, it does not seem to work.
Do you have any idea how to solve this issue?
Any help would be appreciated!!
----------> UPDATE <----------
With help of #Andrey, finally I could change my repository method to :
#Query(value = "select r from Recipes r " +
"where ((:typ) is null or r.type in :typ) " +
"and ((:cnt) is null or r.serveCount in :cnt) " +
"and ((:inc) is null or exists (select 1 from Ingredient i where i.recipes = r and i.name in :inc)) " +
"and ((:exc) is null or not exists (select 1 from Ingredient i where i.recipes = r and i.name in :exc)) ")
Optional<Page<Recipes>> findFilteredRecipes(#Param("typ") List<RecipesType> types,
#Param("cnt") List<Integer> serveCounts,
#Param("inc") List<String> includeIngredients,
#Param("exc") List<String> excludeIngredients,
Pageable page);
As you can see I have also changed dataType of Type and ServeCount search parameters to list<> to extend the ability to filter my data.
If you need to return receipt if it contains at least one ingredient requested, the query would be:
select r from Recipes r
where (:typ is null or r.type = :typ)
and (:cnt is null or r.serveCount = :cnt)
and ((:inc) is null or exists (select 1 from Ingredient i where i.recipes=r and i.name in :inc))
and ((:exc) is null or not exists (select 1 from Ingredient i where i.recipes=r and i.name in :exc))
if the requirement is to return receipt with all ingredients requested, the query would be:
select r from Recipes r
where (:typ is null or r.type = :typ)
and (:cnt is null or r.serveCount = :cnt)
and ((:inc) is null or :inccnt = (select count(1) from Ingredient i where i.recipes=r and i.name in :inc))
and ((:exc) is null or not exists (select 1 from Ingredient i i.recipes=r and where i.name in :exc))
I am trying to execute a Native Query from a Spring Boot application, but i am getting this error " org.postgresql.util.PSQLException: ERROR: operator does not exist: integer = bytea "
Here are the codes i have written to implement this
#SqlResultSetMapping(
name = "StudentAssessmentValue",
classes = #ConstructorResult(
targetClass = StudentAssessmentDTO.class,
columns = {
#ColumnResult(name = "subject_title", type = String.class),
#ColumnResult(name = "assessment", type = String.class),
}
)
)
#NamedNativeQuery(
name = "getStudentSubjectsAssessment",
query = "SELECT\n" +
" subject.subject_title,\n" +
" j as assessment\n" +
"FROM assessment s\n" +
"JOIN LATERAL jsonb_array_elements(s.assessment) j(elem) ON (j.elem->>'student_id') = :student_id\n" +
"JOIN subject ON subject.id = s.subject_id\n" +
"WHERE s.subject_id IN (:subjects)\n" +
"AND s.academy_year_id = :academy_year_id\n" +
"AND s.term_id = :term_id\n" +
"AND s.section_id = :section_id"
,
resultSetMapping = "StudentAssessmentValue"
)
This is the code in my Respository
#Query(nativeQuery = true, name = "getStudentSubjectsAssessment")
List<StudentAssessmentDTO> getStudentAssessments2(
#Param("student_id") String student_id,
#Param("academy_year_id") Integer academy_year_id,
#Param("section_id") Integer section_id,
#Param("term_id") Integer term_id,
#Param("subjects") Integer[] subjects
);
And i have this in my controller
#GetMapping("/{student_id}/{academy_year_id}/{section_id}/
term_id}")
public List<StudentAssessmentDTO> getStudentAssessment2(
#PathVariable("student_id") String student_id,
#PathVariable("academy_year_id") Integer academy_year_id,
#PathVariable("section_id") Integer section_id,
#PathVariable("term_id") Integer term_id,
#RequestParam(value = "subjects") Integer[] subjects
){
return assessmentService.getStudentAssessments2(student_id, academy_year_id, section_id, term_id, subjects);
}
I have also notice if i remove this part from the query
WHERE s.subject_id IN (:subjects) or say i hard code the subjects value like so s.subject_id IN (2,3,4) the code runs successfully. But if the value is coming from the request i then get the error. Here is how the request looks like
localhost:8080/assessment/f3df0bc2-7b4c-49b9-86c9-6e6b01628623/3/4/1?subjects=2,3,4
I recently had a similar problem to yours, while also working with a native JPA query on Postgres. Here is what worked for me:
// sqlString contains your native query
Query query = entityManager.createNativeQuery(sqlString, StudentAssessmentDTO.class);
query.setParameter("subjects", subjects);
query.setParameter("academy_year_id", new TypedParameterValue(IntegerType.INSTANCE, academy_year_id));
query.setParameter("term_id", new TypedParameterValue(IntegerType.INSTANCE, term_id));
query.setParameter("section_id", new TypedParameterValue(IntegerType.INSTANCE, section_id));
List< StudentAssessmentDTO > = query.getResultList();
The error you are seeing can be explained by the Postgres JDBC driver's inability to correctly pass needed type information to the database. For instance, the following error:
ERROR: operator does not exist: integer = bytea
would occur because the driver is passing the parameter to Postgres as a byte array, but the target column is integer type. By using the above type "hints," we can force the driver to pass the correct type information.
This might not be the best solution to this, but it is working for me, i will be glad if anyone can still provide a better solution.
Here is what i did:
I wrote a method that will take the array gotten from the request and generate a string of this nature "(a,b,c)" and then added this string to my query string and it work
CODE
This is the method that build the string
NOTE: I can afford to use this function because i know for sure them element of this array does not grow rapidly, max for my case will be 15
public String generateInSearchParameter(Integer[] inputArr){
StringBuilder search = new StringBuilder("(");
IntStream.range(0, inputArr.length).forEach(i -> {
if (i != inputArr.length - 1) {
search.append(inputArr[i]).append(',');
} else {
search.append(inputArr[i]);
}
});
search.append(")");
return search.toString();
}
Here is the controller code
#GetMapping("/{student_id}/{academy_year_id}/{section_id}/{term_id}")
public List<StudentAssessmentDTO> getStudentAssessment2(
#PathVariable("student_id") String student_id,
#PathVariable("academy_year_id") Integer academy_year_id,
#PathVariable("section_id") Integer section_id,
#PathVariable("term_id") Integer term_id,
#RequestParam(value = "subjects") Integer[] subjects
){
return assessmentService.getStudentAssessments2(student_id, academy_year_id, section_id, term_id,generateInSearchParameter(subjects));
}
Here is the code in my Service
public List<StudentAssessmentDTO> getStudentAssessments2(
String student_id, Integer academy_year_id,
Integer section_id, Integer term_id, String subjects
){
String sqlString = "SELECT" +
" subject.subject_title," +
" j.*" +
" FROM assessment s" +
" JOIN LATERAL jsonb_array_elements(s.assessment) j(elem) ON (j.elem->>'student_id') = :student_id" +
" JOIN subject ON subject.id = s.subject_id" +
" WHERE s.academy_year_id = :academy_year_id" +
" AND s.section_id = :section_id" +
" AND s.subject_id IN " + subjects +
" AND s.term_id = :term_id";
Query query = entityManager.createNativeQuery(sqlString, "StudentAssessmentValue");
query.setParameter("academy_year_id", new TypedParameterValue(IntegerType.INSTANCE, academy_year_id));
query.setParameter("term_id", new TypedParameterValue(IntegerType.INSTANCE, term_id));
query.setParameter("section_id", new TypedParameterValue(IntegerType.INSTANCE, section_id));
query.setParameter("student_id", new TypedParameterValue(StringType.INSTANCE, student_id));
return query.getResultList();
}
I will be guard to welcome a better solution if provided
How can I read list of #Embeddable objects from MongoDB with Hibernate OGM after aggregation.
I have entity like this
#javax.persistence.Entity
public class MySession implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Type(type = "objectid")
private String id;
private Date start;
private Date end;
#ElementCollection
private List<MySessionEvent> events;
}
and #Embeddable object
#javax.persistence.Embeddable
public class MySessionEvent implements Serializable {
private Long time;
private String name;
}
I stuck with mapping Embeddable objects from native query
String queryString = "db.MySession.aggregate([" +
" { '$match': { 'events.name': { '$regex': 'Abrakadabra'} }}, " +
" { '$unwind': '$events' }, " +
" { '$replaceRoot': { 'newRoot': '$events'}} " +
"])";
List<MySessionEvent> objects = em.createNativeQuery(queryString, MySessionEvent.class).getResultList();
I get an error Caused by: org.hibernate.MappingException: Unknown entity
Copying your comment here because it adds some details:
I have data like this [ {id:'s1', events: [{name: 'one'},{name:
'two'}]}, {id:'s2', events: [{name: 'three'},{name: 'four'}]} ] and I
want result like this [{name: 'one'},{name: 'two'},{name:
'three'},{name: 'four'}]
The query you are running returns the following type of results if I run it on MongoDB natively (I populated it with some random data):
{ "name" : "Event 3", "time" : NumberLong(3) }
{ "name" : "Abrakadabra", "time" : NumberLong(5) }
This is not enough to rebuild an entity and that's the reason you are seeing the exception.
Considering that you only want the list of events, this should work:
List<Object[]> poems = em.createNativeQuery( queryString ).getResultList();
Hibernate OGM will convert the previous result in a list of arrays.
Each element of the List is an array where the firs value of the array is the name of the event and the second one is the time.
For supported cases like this I think HQL queries are better. You could rewrite the same example with the following:
String queryString =
"SELECT e.name " +
"FROM MySession s JOIN s.events e " +
"WHERE e.name LIKE 'Abrakadabra'";
List<Object[]> events = em.createQuery( queryString ).getResultList();
Note that I decided not to return the time because in your comment you didn't request it, but this will work as well:
String queryString =
"SELECT e.time, e.name " +
"FROM MySession s JOIN s.events e " +
"WHERE e.name LIKE 'Abrakadabra'";
List<Object[]> events = em.createQuery( queryString ).getResultList();
It is not recognizing the entity, make sure all your entities are in persistence.xml Embeddable objects too
<class>org.example.package.MySessionEvent</class>
I'm trying to run a JPA query to return only specific fields from my entity, rather than the entire entity (for performance reasons).
Within this entity is this:
#OneToMany(cascade = { CascadeType.ALL }, mappedBy = "helper", fetch = FetchType.EAGER)
#MapKeyColumn(name = "year")
public Map<Integer, DutyHistory> getDutyHistoryList() {
return dutyHistoryList;
}
I'd like, within my query, to return multiple values from this map e.g. fields from the DutyHistory object for the last 3 years.
My question is, what's the query syntax for this? I'm mapping the returned values to a POJO as below:
#Query(value = "SELECT new com.castlemon.helpers.dto.ReportHelper(h.helperId, h.firstName, h.secondName"
+ ", h.sex, h.service, h.dateOfBirth, h.schoolGroup, h.orientationRequired, h.notes as adminNotes "
+ ", h.primaryDuty.dutyName as primaryDuty, h.secondDuty, h.secondaryDuty.dutyName as secondaryDuty "
+ " WHERE h.travelling = 1")
public List<ReportHelper> getTravellingHelperDetails();
You should create another query with your "year" parameter
#Query(value = "SELECT new com.castlemon.helpers.dto.ReportHelper(h.helperId, h.firstName, h.secondName"
+ ", h.sex, h.service, h.dateOfBirth, h.schoolGroup, h.orientationRequired, h.notes as adminNotes "
+ ", h.primaryDuty.dutyName as primaryDuty, h.secondDuty, h.secondaryDuty.dutyName as secondaryDuty "
+ " WHERE h.travelling = 1 AND h.year >= :yearLimit")
public List<ReportHelper> getTravellingHelperDetailsUntilYear(String yearLimit);
I get a ClassCastException when trying to query my JPA entity class. I only want json to show two columns. That is name and address. How do I only show selected columns in JPA? From debugging it has to be the for loop. So List is the object and I need to make the right side an object instead of a list correct?
Entity
#Entity
#Table(name = "Personnel")
public class User implements Serializable {
private String id;
private String name;
private String address;
public User(String id, String name, String address)
{
this.id = id;
this.name = name;
this.address = address;
}
#Id
#Column(name = "name", unique = true, nullable = false)
public String getName() {
return this.name;
}....
//setters getters
Query/Impl
public List<User> getRecords(User ent){
String sql = "select "
+ " usr.name, usr.address "
+ " from User usr"
+ " where usr.id = '1' ";
List<User> records = this.getSession().createQuery(sql).list();
for ( User event : records ) {
System.out.println( "Records (" + event.getName + ") );
}
return records;
}
Update
This is my attempt to declare the result object as List. Does the method have to be an object instead of ?
public List<User> getRecords(User ent){
String sql = "select "
+ " usr.name, usr.address "
+ " from User usr"
+ " where usr.id = '1' ";
Map<String, String> results = new HashMap<String, String>();
List<Object[]> resultList = this.getSession().createQuery(sql).list();
// Place results in map
for (Object[] items: resultList) {
results.put((String)items[0], (String)items[1]);
results.toString();
}
return (List<User>) results;
You can pass a DTO class to the SELECT clause like this:
List<UserDTO> resultList = this.getSession().createQuery("""
select
new my.package.UserDTO(usr.name, usr.address)
from User usr
where usr.id = :userId
""")
.setParameter("userId", userId)
.getResultList();
You should read the complete object:
String sql = " from User usr"
+ " where usr.id = '1' ";
Or declare the result object as List<Object[]>
You can use generic(I assure you are using java 1.5 and above) and and one you get result, you do type check and downcast to desired type.
If You don't want to read the complete object you have to use Criteria and Projection on specific properties.
See this answer it will help