Map result of a sql query to pojo using spring Data JPA - java

I am a student learning Spring data jpa. I am trying to solve some of practice coding questions. I have a question which i could not find answer to.
I have a database table by name MYDB which has fields:
(id, firstname, lastname, rollno, major, country)
And i have an sql query like this:
select Count(*) as counts, lastname as last_name, major as major_field from MYDB group by country
The above query returns three fields: counts(which is not a db column), last_name and major_field.
I have a POJO like this:
public class MyPojo {
private int counts;
private String lastName;
private String majorField;
// Getters and Setters of all data members here
...................
}
My question is how do i map the result that i got from sql query to my POJO? I need to assign:
counts = counts(from sql query), lastName = last_name(from sql query), majorField = major_field(from sql query).
I am stuck at this point and do not know how to implement further to map result of sql query to POJO:
public interface MyRepo extends JpaRepository<MyPojo, String> {
#Query(value=MY_SQL_QUERY, nativeQuery = true)
List<MyPojo> findAll();
}
Ultimately i need to convert MyPojo to a Json object, but i know how to do that part. I am only stuck without ideas about assigning result of sql query to pojo.

Problem solved using interface-based projections:
https://www.baeldung.com/jpa-queries-custom-result-with-aggregation-functions#solution_interface_jpa

Use the javax.persistence #Column annotation to specify which values from the query are used to populate the fields of the Java object:
public class MyPojo {
#Column(name = "counts")
private int counts;
#Column(name = "last_name")
private String lastName;
... and so on
}
Here is a great tutorial on the annotations:
https://www.javaworld.com/article/3373652/java-persistence-with-jpa-and-hibernate-part-1-entities-and-relationships.html

You have to use TypedQuery and give your class name for executing and mapping your query result into pojos

Related

Is it possible to query with list of objects using JPA like foreach tag?

Let's say I have a query like this:
Query object
public class myObject {
private String code;
private String name;
}
query method
#Query(nativeQuery = true,value = "...")
findByAcodeAndBname(List<MyObject> queryObject);
Pseudocode of the query
select a.code, b.name from tableA a
left join table b on a.column = b.column
where a.code = ${} b.name = ${}
Since it could cause performance issues just simply using foreach code in Java to do the query, how do I map every pair of a.code and b.name using native JPA query like mybatis foreach tag? Or is it posssible?
I think you don't need native query here, because the costs of querying data from the database and transfering it over network are higher than any related Java operations.
Instead of native query you can use JPQL query, e.g.
#Query("select new com.tsypanov.domain.HasIdAndNameDto(e.id, e.name)" +
" from ManyFieldsEntity e " +
"where e.name = :name")
List<HasIdAndName> findAllByNameUsingDto(#Param("name") String name);
Here we call constructor of DTO from JPQL query passing the fields from our entity. The DTO class must have the specified constructor:
#RequiredArgsConstructor
public class HasIdAndNameDto {
private final Long id;
private final String name;
}

Select only specific columns from joined tables (Many-to-Many) in Spring Data JPA

The purpose is to select columns from joined tables (Many-to-Many).
The problem i have is to select two columns from a joined Many-to-Many table.
I'am using Springboot 2.3 and Spring data Jpa.
I have this data model, and what i want to fetch are the blue boxed fields
So the native query could look like this (if i am right ...)
SELECT bg.id, bg.name, p.name, c.name, c.short_desc FROM boardgame as bg
JOIN boardgame_category bgc on bg.id = bgc.fk_game
JOIN publisher p on bg.fk_publisher = p.id
JOIN category c on bgc.fk_category = c.id
WHERE bg.id = :id
I first tried to work with dto in JPQL statment
public class BoardgameDto {
private long id;
private String name;
private String publisherName;
private Set<CatregoryDto> categoryDto;
// setter, getter etc...
}
public class CategoryDto {
private String name;
private String shortDesc;
// setter, getter etc...
}
The JQPL query could look like this , but it doesn't work (IDE shows errors on CategoryDto)
/* THIS DOESN'T WORK */
SELECT new org.moto.tryingstuff.dto.BoardgameDto(bg.id, bg.name, p.name,
new org.moto.tryingstuff.dto.CategoryDto(c.name, c.short_desc)) FROM Boardgame as bg, Publisher as p, Category as c
Well, I think the problem I have with this way of doing is that the dto's contructor can't receive a collection as written here, and i think neither another contructor in parameter.
Then i started looking at Criteria Queries, especialy multiselect, Tuple, Dto, but it look like i had same kind of problems so i didn't dive deeper into it.
Finally i used a JpaRepository and it's findById() method like this
public interface BoardgameRepository extends JpaRepository<Boardgame, Long> {
}
// In a test or service method
Boardgame game = repository.findById(long id);
Then i filter the fields i need to keep through mappings in Service or Controller layer. So the front only received need datas.
But it feel a bit overkill,
Am I missing something, any part of the framework that would allow me to select only specific columns?
As you wrote, you can't use a collection as the parameter of a constructor expression. That's because the expression gets applied to each record in the result set. These records are a flat data structure. They don't contain any collections. Your database returns a new record for each element in that collection instead.
But your constructor expression fails for a different reason. You're trying to combine 2 constructor expressions, and that's not supported. You need to remove the 2nd expression and perform that operation within the constructor of your DTO.
So, your query should look like this:
SELECT new org.moto.tryingstuff.dto.BoardgameDto(bg.id, bg.name, p.name, c.name, c.short_desc) FROM Boardgame as bg <Your JOIN CLAUSES HERE>
And the constructor of your BoardgameDto like this:
public class BoardgameDto {
public BoardgameDto(Long id, String gameName, String publisherName, String categoryName, String description) {
this.id = id;
this.name = gameName;
this.publisherName = publisherName;
this.category = new Category(categoryName, description);
}
...
}

partially mapping the result of a NamedNativeQuery to a class

I have an #Entity class Person, that has multiple fields and I would like to map the result of several #NamedNativeQuerys to the Person class however, the queries I am running do not return values for every field in the Person class. When I try to run a query I get the following errors:
[error] o.h.u.JDBCExceptionReporter - Invalid column name bar.
[error] play - Cannot invoke the action, eventually got an error: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute query
My class is set up similar to this:
#NamedNativeQueries({
#NamedNativeQuery(
name = "getBirthdate",
//Returns values for {idnumber, name, birthdate}
query = "EXEC dbo.proc_get_birthdate :name",
resultClass = Person.class
),
#NamedNativeQuery(
name = "getBar",
//Returns values for {idnumber, name, bar}
query = "EXEC dbo.proc_get_bar :name",
resultClass = Person.class
)
})
#Entity
public class Person {
#Id
#Column(name = "idnumber")
private int idNumber;
#Column(name = "name")
private String name;
#Column(name = "birthdate")
private String birthdate;
#Column(name = "foo")
private String foo;
#Column(name = "bar")
private String bar;
//All appropriate getter and setter methods are implemented
}
I double checked and all the columns in the Person class do, in fact, exist in the table being queried.
My actual class is much larger and due to that and some security concerns, I do not want my queries to have to return EVERY field and am hoping that there is a simple way to just give them a value of null if a value isn't returned by the query. I tried to set each field to null in the declaration (example below) but that didn't work either.
#Column(name = "bar")
private String bar = null;
I would really rather not have to create a tailored class for every single query I need to run so if what I'm trying to do is possible, any help would be greatly appreciated. Thanks in advance.
I believe that you misunderstood the #NamedNativeQuery use. This annotation should be used with a SQL Query and not with a stored procedure. Looking at the exception SQLGrammarException: could not execute query it is possible to see that is a grammar exception, the query could not be parsed.
With JPA 2.1 you could use the annotation #NamedStoredProcedureQuery that is the one with support to stored procedures.

Hibernate - Use native query and alias to Bean with enum properties?

I am having trouble using a native query in hibernate to alias a bean that contains enum properties. I am getting an InvocationTargetException when query.list() is called. My example is below:
#Entity(name = "table1")
public class Class1 {
#Column(name = "col1")
#NotNull
private Integer prop1;
#Column(name = "col2")
#NotNull
private String prop2;
#Column(name = "col3", length = 6)
#Enumerated(value = EnumType.STRING)
private MyEnumType prop3;
// ... Getters/Setters...
}
public List getClass1List(){
String sql = "select col1 as prop1, col2 as prop2, col3 as prop3 from table1";
Session session = getSession(Boolean.FALSE);
SQLQuery query = session.createSQLQuery(sql);
query.addScalar("col1", Hibernate.INTEGER);
query.addScalar("col2", Hibernate.STRING);
query.addScalar("col3", Hibernate.STRING);
query.setResultTransformer(Transformers.aliasToBean(Class1.class));
return query.list();
}
During the query.addScalar("col3", Hibernate.STRING) call, I don't know what type to use for col3 (the enum type). Hibernate.String is not working! I have also tried to leave the type off entirely ( query.addScalar("col3") ) but I get the same InvocationTargetException. Can anyone help me out with this? My model cannot be changed and I am stuck with a native sql query. Any ideas are appreciated.
// In public List getClass1List() method:
// 1. DEFINE:
Properties params = new Properties();
params.put("enumClass", "enumerators.MyEnumType");
params.put("type", "12");
// 2. REPLACE:
// query.addScalar("col3", Hibernate.STRING);
// X
query.addScalar("col3", Hibernate.custom(org.hibernate.type.EnumType.class, params));
Firstly, you shouldn't use
private EnumType prop3;
but
private ActualEnum prop3;
Where ActualEnum is your own enum type (for example, Fruits to distinguish apples and oranges).
Second, you hibernate mapping is irrelevant when you use native sql.
Now, there are couple of options I can propose. You can try to use addEntity() instead of bunch of scalars. It's possible that Hibernate will recognize enum property and map correctly.
Other option is to have non public setter that would take string from database, convert it to enum and set actual property.
Finally, you can customize transformer. But it's probably most complex option.

Best SQL Option for Performance

I currently need to execute a database query that involves data from three separate tables.
(bases,member,sell_transaction)
The bases table looks like the following
base_id(PK) name state
The member table looks like the following
id(PK) last_name username email first_name
The sell_transaction table has the following schema
transaction_id(PK) city
state
base_id
date id
agentId
NOTE- these tables contain more columns, but they are irrelevant, so I am going to save the time and not write them out.
I am working on a transaction inquiry that involves data from all of these tables. I need to return this data in a TransactionItem that I am populating from these three tables
The TransactionItem as a Pojo looks like the following
public TransactionItem(){
private String firstName;
private String lastName;
private String email;
private String baseName;
private String city;
private String state;
private Date date;
public String getFirstName(){
return firstName;
}
public String setFirstName(String firstName){
this.firstName = firstName;
}
...
...///The rest of the getters and setters
...
...
}
Currently I am doing three separate selects from each table, which is taking longer then I would like. I would image that there is a way I could use a join or nested select statements,etc.
The data and conditions and their corresponding tables are as follows
I need...
first_name, last_name,id from member
I need
name and state from bases where base_id from sell_transaction is equal to base_id from bases
Again there is more that I need, but it is redundant to name them off, The idea should still be clear, I need to query data that is dependent on each other. I am not sure performance wise what is the best performance option. The amount of data that is being traversed could end up being very large or very small(not sure if that matters).
select
b.name,b.state,
s.city,s.state,s.duty,s.branch,s.date,
m.first_name,m.last_name,m.email,m.phone,m.id,s.transaction_id
from
bases as b, sell_transaction as s, member as m
where
s.agentId is null and
s.username = m.username and
b.base_id = s.duty
select first_name, last_name, member.id member_id, bases.name, bases.state
from bases, sell_transaction, member
where sell_transaction.base_id = bases.base_id
and member.id = sell_transaction.agentId;
I am going out on a limb here and guessing that agentID is the connection to member from sell_transaction, but it's not clear or obvious, and possibly incorrect (unless you clarify your data model).

Categories

Resources