I am running into an issue.
I search for several hours but did not find any anwers.
What I want to do is a sql select, in which the ORDER clause depends on the value of a column (so it changes for every tuples).
I managed to do it via HQL with something like that :
SELECT NEW myDTO(m.id, m.name, " + calculDistance + " AS distance) FROM Table m GROUP BY m.mercId ORDER BY distance ASC
With calculDistance depending of m.latitude and m.longitude
This works fine.
However, my request is much more complicated than that and for reading, update and such reasons, I'd like to do it directly with JPA.
Do you know if this is possible?
Thanks for your help.
EDIT
Here is the part of my table structure (I put only the needed columns):
#Entity
#Table(name = "td_merchant")
#XmlRootElement
#SequenceGenerator(name = "td_merchant_id_seq", sequenceName = "td_merchant_id_seq")
public class Merchant implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "td_merchant_id_seq", strategy = GenerationType.SEQUENCE)
#Column(name = "merc_id")
private Integer mercId;
#Column(name = "merc_name")
private String mercName;
#Column(name = "merc_latitude")
private Double mercLatitude;
#Column(name = "merc_longitude")
private Double mercLongitude;
...
}
I also faced same issue, i wrote one function which was taking input for order by as well as asc and desc seq.
public List<CompanyName> loadAllCompanies(CompanySortField sortField, boolean ascending) {
String queryString = "select new com.Company(u.name, u.surname, " +
" country.name, country.population, " +
" city.name) from Company u left join u.city as city left join u.country as country " +
" order by " + sortField.getField() + (ascending ? " asc " : " desc ");
return entityManager.createQuery(queryString).getResultList();
}
You can try in this way, only thing is you need to fire another query to find out company sort field.
Related
In my project, in the repository, I have a select that displays all the data I need, including those months for which I don’t have data. To do this, in my select, I create a temporary column, which I named tneDate .
#Query(value = "SELECT \n" +
" theDate, asl.id, asl.interest_payment, asl.interest_rate, asl.principal_payment, asl.total_payment, asl.actual_delta, \n" +
" asl.date, asl.modified, asl.interest_payment_modified, asl.amortization_schedule_initial_id, asl.tranche_id, asl.outstanding_usd,\n" +
" asl.disbursement, asl.floating_index_rate, asl.upfront_fee, asl.commitment_fee, asl.other_fee, asl.withholding_tax, \n" +
" asl.default_fee, asl.prepayment_fee, asl.total_out_flows, asl.net_flows, asl.user_id, asl.outstanding_principal, asl.new_row\n" +
"FROM\n" +
" GENERATE_SERIES\n" +
" (\n" +
" (SELECT MIN(ams.date) FROM amortization_schedules ams),\n" +
" (SELECT MAX(ams.date) + INTERVAL '1' MONTH FROM amortization_schedules ams),\n" +
" '1 MONTH'\n" +
" ) AS tab (theDate)\n" +
"FULL JOIN amortization_schedules asl on to_char(theDate, 'yyyy-mm') = to_char(asl.date, 'yyyy-mm')", nativeQuery = true)
List<AmortizationSchedule> findAllByDate();
Now, through the getter in Entity , I want to get only this tneDate , that is, the date that I form. I don't want it to be written to the database. Therefore, I put the #Transient annotation, but as I understand it, this annotation ignores the getter of this entity and I cannot get this data, since I get NULL. In this part :
else {
BigDecimal swaprate = getRate(previousAmortiz.getDate(), previousAmortiz.getTranche().getLocalCurrency().getId());
childReports.add(createChild(previousAmortiz.getOutstandingPrincipal(), swaprate, previousAmortiz.getTheDate()));
My Entity
#Getter
#Setter
#ToString
#Entity
#Table(name = "amortization_schedules")
public class AmortizationSchedule {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Transient
private Date theDate;
#Column(name = //)
private BigDecimal //;
how do i get the tneDate data?
I believe the code below is working correctly but my logic is flawed and so I'm looking for help getting the results I actually want. In addition I think my attempt on the JPA method is overly convoluted and there's likely a cleaner solution. I know I can likely use #Query but I want to try and get the method name solution working as well so I can learn where I went wrong.
For simplicity I've stripped back the DB tables in this example, however I have Table1 which is the top level table and Table2 which sits below it and has a foreign key back to Table1.
Table 1 -> Table 2 is a one to many relationship, I am looking to write a JPA method that will pull back a record from Table 1 when given the Table1 ID and only the current effective record from Table2 (this will be where EffectiveDateTime column is most recent but NOT future dated). Using the example below I want it to only pull back ID 8.
Unfortunately, I believe with the code I have below it recognises one of those records for Table2 is within the date range required and is therefore pulling all the records back that have a relation with the ID from Table 1. I could be wrong on this though and the logic could be flawed in a different way.
Any help would be appreciated.
Table1 Entity Class
#Entity
#Table(name = "TABLE1")
public class Table1 {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, unique = true)
private Integer id;
#OneToMany(mappedBy = "table1", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Fetch(value = FetchMode.SUBSELECT)
#JsonManagedReference
private List<Table2> table2;
//Plus getters & setters
Table2 Entity Class
#Entity
#Table(name = "TABLE2")
public class Table2{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID", nullable = false, unique = true)
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Table1Id")
#JsonBackReference
private Table1 table1;
#Column(name = "EffectiveDateTime", nullable = false)
private LocalDateTime effectiveDateTime;
//Plus getters & setters
Repo Class
#Repository
public interface Repository extends JpaRepository<Table1, Integer>, JpaSpecificationExecutor<Table1> {
public Optional<Table1> findTopByIdAndTable2EffectiveDateTimeLessThanEqualOrderByTable2EffectiveDateTimeDesc(int id, LocalDateTime now);
}
Current JSON return
{
"id": 5
"Table2": [
{
"id": 6,
"effectiveDateTime": "2021-01-01T00:00:01"
},
{
"id": 8,
"effectiveDateTime": "2022-01-01T00:00:01"
},
{
"id": 9,
"effectiveDateTime": "2023-01-01T00:00:01"
}
]
}
Once you declared a oneToMany relationship, your entity will load all joined entity when calling the getTable method (wich is called by jackson when serializing). I believe if you activate debug mode, you will see 2 request, the first will be the one you describe in your repopsitory, the second the one JPA use to load all related table2 entity.
Their is some possible ways to accomplish what you want:
the first (and most obvious ?) is to manually make the 2 request equivalent to your HQL request (the table 1 find by ID and then the table 2 find by....) and then aggregate the 2 results
the second one would be to use jackson to serialize your data how you want to (by filtering useless table 2 data)
the third one would be to use jackson to serialize a table2 (wich give you the corresponding table1 data) into the appropriate JSON. It's a bit more complicated but probably more efficient if your table1 reference a lot of table2 data.
The last one would be to try using projections it allow you to declare only the data you want to retrieve when requesting data.
hope this help !
In the end we completed this using a JPQL query. An example of the query is below in case it helps any others in the future.
#Query(value = "SELECT table1 FROM Table1 table1 "
+ " JOIN FETCH table1.table2 table2 "
+ " WHERE table1.id = :table1Id"
+ " AND table2.id = "
+ " ( SELECT table2.id FROM table2 "
+ " WHERE table2.table1.id = :table1Id "
+ " AND table2.effectiveDateTime = "
+ " ( SELECT MAX(effectiveDateTime) FROM table2"
+ " WHERE table2.table1.id = :table1Id "
+ " AND table2.effectiveDateTime <= :currentTimestamp "
+ " ) "
+ " ) ")
Optional<Table1> getEffectiveDateTimeTable2(#Param("table1Id") int table1Id, #Param("currentTimestamp") LocalDateTime currentTimestamp);
In my Spring boot application I have a query which should return a distinct List of Focus' (works perfectly in MySQL)
#Query(value = "SELECT DISTINCT * FROM Focus F " +
"JOIN FrameworkFoci FF on FF.focusID = F.focusID " +
"JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
"JOIN GroupMembers GM on FG.groupID = GM.groupID " +
"JOIN Users U on GM.userID = U.userID " +
"WHERE U.userID = :userID", nativeQuery = true)
List<Focus> findByUserID(#Param("userID") Long userID);
However this does not return distinct values, duplicates are contained in the resulting list. Another issue is that I can't return a whole entity using #Query annotation - changing my query to SELECT DISTINCT(F) FROM Focus F gives the error java.sql.SQLSyntaxErrorException: Unknown column 'F' in 'field list'.
Furthermore, I tried changing adjusting the query to the following
#Query(value = "SELECT DISTINCT * FROM FrameworkFoci FF " +
"JOIN FocusGroups FG on FF.frameworkID = FG.frameworkID " +
"JOIN GroupMembers GM on FG.groupID = GM.groupID " +
"JOIN Users U on GM.userID = U.userID " +
"WHERE U.userID = :userID", nativeQuery = true)
however this produced the error java.sql.SQLException: Column 'focusCategory' not found.
Why is the query not returning distinct values? And why can't I return a whole entity nor use the second query?
Focus Entity:
#Entity
public class Focus {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long focusID;
#Column(name = "focusCategory")
private String focusCategory;
private String focusName;
private String focusExplanation;
#OneToMany(mappedBy = "focus")
private Set<Rating> ratings;
#ManyToMany
#JoinTable(name = "FrameworkFoci",
joinColumns = #JoinColumn(
name = "focusID"),
inverseJoinColumns = #JoinColumn(
name = "frameworkID"))
private Set<Framework> frameworks;
//image
protected Focus(){}
public Focus(String focusName, String focusCategory, String focusExplanation) {
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
}
public Focus(String focusCategory, String focusName, String focusExplanation, Set<Rating> ratings){
this.focusCategory = focusCategory;
this.focusName = focusName;
this.focusExplanation = focusExplanation;
this.ratings = ratings;
}
public Long getFocusId() {
return focusID;
}
public void setFocusId(Long focusID) {
this.focusID = focusID;
}
public String getFocusCategory() {
return focusCategory;
}
public void setFocusCategory(String focusCategory) {
this.focusCategory = focusCategory;
}
EDIT:
I've switched from SQL to JPQL with the following query:
#Query(value = "SELECT DISTINCT focus FROM Focus focus " +
"WHERE focus.frameworks.groups.groupMembers.user.userID =:userID ")
I now get an error org.hibernate.QueryException: illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups]
Framework entity:
#Entity
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "frameworkID")
public class Framework {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long frameworkID;
private String frameworkName;
#ManyToMany
#JoinTable(name = "FrameworkFoci",
joinColumns = #JoinColumn(
name = "frameworkID"),
inverseJoinColumns = #JoinColumn(
name = "focusID"))
private Set<Focus> frameworkFoci = new HashSet<>();
#OneToMany(mappedBy = "framework", fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
private Set<Group> groups;
public Framework(){}
The following query solves the issue
#Query(value = "SELECT DISTINCT focus FROM Focus focus " +
"JOIN focus.frameworks frameworks " +
"JOIN frameworks.groups groups " +
"JOIN groups.groupMembers groupMembers "+
"WHERE groupMembers.userID =:userID ")
List<Focus> findByUserID(#Param("userID") Long userID);
Frameworks and GroupMembers are collections and hence needed to be joined, otherwise illegal attempt to dereference collection [focus0_.focusID.frameworks] with element property reference [groups] was produced
you should write your query like this:
'
SELECT DISTINCT f FROM Focus F '
The problem stems from you using SQL by specifying nativeQuery = true. SQL doesn't know about entities, just tables.
Since you presumably have many FrameworkFoci rows (and rows in all the other tables) for each Focus row, each Focus row gets repeated for each matching row in FrameworkFoci. This kind of duplicates the Focus row but the resulting rows are still distinct, because they differ in the columns from the other tables.
And then each row gets turned into a Focus entity, probably with a single element in the framework set.
So therefore query doesn't so much return duplicate results as results split into multiple entities.
Fortunately the solution should be fairly simple: Use JPQL which should be perfectly possible, since you're using only simple joins.
The following should give you a start:
#Query(value = "SELECT DISTINCT * FROM Focus F " +
"WHERE F.framework.groupMembers.user.id=:userID")
List<Focus> findByUserID(#Param("userID") Long userID);
I have the following entity Project:
#Data
#NoArgsConstructor
#Entity
#ToString(exclude = "roles")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(unique = true)
private String name;
private String description;
private Boolean isArchived;
private LocalDate archivedDate;
private LocalDate creationDate;
#Column(nullable = false, columnDefinition = "BOOLEAN DEFAULT FALSE")
private Boolean invoicingActivated;
#ManyToOne
#NotNull
private Order order;
#OneToOne(cascade = CascadeType.ALL)
private DefaultDailyEntrySettings defaultDailyEntrySettings;
#OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true)
private List<ProjectEmployee> projectEmployees;
}
I want to get all projects. Each project should also have their list of projectEmployees.
Thats the entity ProjectEmployee:
#Data
#Table(uniqueConstraints = {#UniqueConstraint(columnNames = {"employee_id", "project_id"})})
#NoArgsConstructor
#ToString(exclude = "project")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class ProjectEmployee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
#JsonIgnore
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
#NotNull
private Project project;
#ManyToOne
#NotNull
private Employee employee;
#ManyToOne
private ProjectEmployeeRole projectEmployeeRole;
}
To avoid n+1 queries, i wrote the following query:
#Query("SELECT project FROM Project project JOIN FETCH project.order ord JOIN FETCH ord.customer " +
"LEFT JOIN FETCH project.projectEmployees projectEmployee LEFT JOIN FETCH project.defaultDailyEntrySettings " +
"LEFT JOIN FETCH projectEmployee.employee LEFT JOIN FETCH projectEmployee.projectEmployeeRole")
List<Project> findAllProjectsInOneQuery();
This works, but it returns all properties of every single object. For example i´m only interested in the id and the name of ord.customer, i dont need all the other fields of ord.customer in this case. The problem with getting all the fields this way is that theres a lot of data being transmitted which i do not need in this case. To only select the ones i need and reduce the amount of data im sending over the internet i could do something like this:
#Query("SELECT new de.project.Project(project.id, project.name, " +
"project.description, project.isArchived, project.archivedDate, " +
"project.creationDate, project.invoicingActivated, project.order.id, " +
"project.order.name, project.order.customer.id, project.order.customer.name) " +
"FROM Project project")
List<Project> findAllMinimal();
But as you see, i can´t get project.projectEmployees that way, because it´s a list and i do not think im able to pass a list via the constructor this way.
I tried:
#Query("SELECT new de.project.Project(project.id, project.name, " +
"project.description, project.isArchived, project.archivedDate, " +
"project.creationDate, project.invoicingActivated, project.order.id, " +
"project.order.name, project.order.customer.id, project.order.customer.name, " +
"projectEmployee.id) " +
"FROM Project project JOIN project.projectEmployees projectEmployee")
List<Project> findAllMinimal();
But projectEmployee.id is just the id of the first projectEmployee, i do not think im able to pass all projectEmployees this way.
Is there any way to get all projects with their projectEmployees(and the other properties i listed in the query above) and specify which fields i would like to get? It does not have to be one query, a constant number of queries is fine. It should obviosuly just avoid n+1 queries.
Edit:
I came up with a workaround. Im using the following two queries:
#Query("SELECT new de.project.Project(project.id, project.name, " +
"project.description, project.isArchived, project.archivedDate, " +
"project.creationDate, project.invoicingActivated, project.order.id, " +
"project.order.name, project.order.customer.id, project.order.customer.name) " +
"FROM Project project")
List<Project> findAllMinimal();
#Query("SELECT DISTINCT new de.projectemployee.ProjectEmployee(projectEmployee.id, " +
"projectEmployee.employee.id, projectEmployee.employee.email, " +
"projectEmployee.employee.firstName, projectEmployee.employee.lastName, " +
"projectEmployee.employee.address, projectEmployee.employee.weeklyHoursEnabled, " +
"projectEmployee.employee.weeklyHours, projectEmployee.employee.isArchived, " +
"projectEmployee.employee.archivedDate, projectEmployee.project.id, projectEmployeeRole.id, " +
"projectEmployeeRole.name, projectEmployeeRole.hourlyWage) FROM ProjectEmployee projectEmployee " +
"LEFT JOIN projectEmployee.projectEmployeeRole projectEmployeeRole " +
"WHERE projectEmployee.project IN :projects")
List<ProjectEmployee> findByProjects(#Param("projects") List<Project> projects);
To give every project his projectEmployees i need some additional java code:
List<Project> projects = projectRepository.findAllMinimal();
List<ProjectEmployee> projectEmployees = projectEmployeeRepository.findByProjects(projects);
Map<Long, List<ProjectEmployee>> projectIdToProjectEmployeesMap = new HashMap<>();
for (ProjectEmployee projectEmployee : projectEmployees) {
List<ProjectEmployee> projectEmployeesToBeSaved = projectIdToProjectEmployeesMap.getOrDefault(projectEmployee.getProject().getId(), new ArrayList<>());
projectEmployeesToBeSaved.add(projectEmployee);
projectIdToProjectEmployeesMap.put(projectEmployee.getProject().getId(), projectEmployeesToBeSaved);
}
projects.forEach(project -> project.setProjectEmployees(projectIdToProjectEmployeesMap.get(project.getId())));
return projects;
So, ye as you see im able to achieve my goal of getting all projects with their projectEmployees in a constant number of queries(2) and only select the fields i need. The downside is that i have a javacode running with O(n) complexity. But i reduced the size of the data im transmitting by over 90%, so i guess its worth.
It´s hard to believe that a java code algorithm like the one i used is required to find a solution for my problem, so if anyone finds a better solution(with just sql queries) which is capable of doing the above stated, please share it.
Didn't verify it for your case, but at a first glance you may take advantage of the first level cache. After you perform
List<Project> projects = projectRepository.findAllMinimal();
List<ProjectEmployee> projectEmployees = projectEmployeeRepository.findByProjects(projects);
Any access to projectEmployee.getProject() should return its project from the cache avoiding n+1 problem. So if you can leave project.projectEmployees List unpopulated, you can drop your "custom javacode".
This is not classical N+1 problem. My issue is conserning using projections and DTO objects in Jpa.
I have next method with JPA Query:
public List<MeterDTO> getAllBrokenMeterByHouseServ(House house, Serv serv, Date dt) {
Query query =em.createQuery("select new MeterDTO(m, g.kart.lsk, nvl(e.tp,0)) from Meter m "
+ "join m.exs e with m.id=e.meter.id "
+ "join m.meterLog g with m.meterLog.id=g.id "
+ "join g.kart k with g.kart.id=k.id and :dt between k.dt1 and k.dt2 "
+ "join g.serv s with g.serv.id=s.id "
+ "join k.kw kw with k.kw.id=kw.id "
+ "join kw.house h with kw.house.id=h.id "
+ "where s.id = :servId "
+ "and kw.house.id = :houseId "
+ "and :dt between e.dt1 and e.dt2 and nvl(e.tp,0) in (2,3,4) "
+ "");
query.setParameter("servId", serv.getId());
query.setParameter("houseId", house.getId());
query.setParameter("dt", dt);
return query.getResultList();
}
I fetch records from the query above into
data transfer object:
meterDao.getAllBrokenMeterByHouseServ(house, serv, dt2).stream().forEach(t-> {
log.info("meter.id={}, lsk={}, tp={} ", t.getMeter().getId(), t.getLsk(), t.getTp());
});
MeterDTO:
#Getter #Setter
public class MeterDTO {
private Meter meter;
private Integer lsk;
private Double tp;
public MeterDTO(Meter meter, Integer lsk, Double tp) {
super();
this.meter = meter;
this.lsk = lsk;
this.tp = tp;
}
}
Why does hibernate produce one main query:
select
meter0_.ID as col_0_0_,
kart3_.lsk as col_1_0_,
nvl(exs1_.TP,
0) as col_2_0_
from
MT.METER meter0_
inner join
MT.METER_EXS exs1_
on meter0_.ID=exs1_.FK_METER
and (
meter0_.ID=exs1_.FK_METER
)
inner join
MT.METER_LOG meterlog2_
on meter0_.FK_METER_LOG=meterlog2_.ID
and (
meter0_.FK_METER_LOG=meterlog2_.ID
)
inner join
AR.KART kart3_
on meterlog2_.FK_KLSK_OBJ=kart3_.FK_KLSK_OBJ
and (
kart3_.lsk=kart3_.lsk
and (
? between kart3_.DT1 and kart3_.DT2
)
)
inner join
AR.KW kw6_
on kart3_.FK_KW=kw6_.ID
and (
kart3_.FK_KW=kw6_.ID
)
inner join
AR.HOUSE house7_
on kw6_.FK_HOUSE=house7_.ID
and (
kw6_.FK_HOUSE=house7_.ID
)
inner join
TR.SERV serv5_
on meterlog2_.FK_SERV=serv5_.ID
and (
meterlog2_.FK_SERV=serv5_.ID
)
where
serv5_.ID=?
and kw6_.FK_HOUSE=?
and (
? between exs1_.DT1 and exs1_.DT2
)
and (
nvl(exs1_.TP, 0) in (
2 , 3 , 4
)
)
and multiple queries with different bind argument "?" to load every entity:
select
meter0_.ID as ID1_44_0_,
meter0_.FK_K_LSK as FK_K_LSK2_44_0_,
meter0_.FK_METER_LOG as FK_METER_LOG4_44_0_,
meter0_.TRANS_RATIO as TRANS_RATIO3_44_0_
from
MT.METER meter0_
where
meter0_.ID=?
How to avoid this issue? I want to load all entities Meter in one main query.
Is it possible?
I use:
<spring-framework.version>5.0.5.RELEASE</spring-framework.version>
<hibernate.version>5.1.0.Final</hibernate.version>
Any help would be greatly appreciated.
upd1
I simplified my JPA query code to this:
public List<MeterDTO> getAllBrokenMeterByHouseServ(House house, Serv serv, Date dt) {
Query query =em.createQuery("select new com.ric.bill.dto.MeterDTO(m) from Meter m ");
}
But it still produces mutiple queries:
select
meter0_.ID as ID1_44_0_,
meter0_.FK_K_LSK as FK_K_LSK2_44_0_,
meter0_.FK_METER_LOG as FK_METER_LOG4_44_0_,
meter0_.TRANS_RATIO as TRANS_RATIO3_44_0_
from
MT.METER meter0_
where
meter0_.ID=?
20-04-2018 12:52:49.482 [main] DEBUG o.h.l.p.e.p.i.ResultSetProcessorImpl - Starting ResultSet row #0
20-04-2018 12:52:49.482 [main] DEBUG org.hibernate.SQL -
select
meter0_.ID as ID1_44_0_,
meter0_.FK_K_LSK as FK_K_LSK2_44_0_,
meter0_.FK_METER_LOG as FK_METER_LOG4_44_0_,
meter0_.TRANS_RATIO as TRANS_RATIO3_44_0_
from
MT.METER meter0_
where
meter0_.ID=?
<Skipped>
very strange!
upd2 Meter entity:
#SuppressWarnings("serial")
#Entity
#Table(name = "METER", schema="MT")
#Getter #Setter
public class Meter extends Base implements java.io.Serializable, Storable {
public Meter (){
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", updatable = false, nullable = false)
protected Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="FK_METER_LOG", referencedColumnName="ID")
private MeterLog meterLog ;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval=true)
#JoinColumn(name="FK_METER", referencedColumnName="ID")
#BatchSize(size = 50)
private List<Vol> vol = new ArrayList<Vol>(0);
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="FK_METER", referencedColumnName="ID")
#BatchSize(size = 50)
private List<MeterExs> exs = new ArrayList<MeterExs>(0);
#Column(name = "TRANS_RATIO", updatable = true, nullable = true)
private Double trRatio;
}
In DTO you have 'Meter meter' field, in meter field you have 'MeterLog meterlog' etc. In this case Hibernate is additionally loading for field for full object. This DTO is to much complex. Try to create more flat object:
public class MeterDTO {
private Integer meterId
private Double meterTrRatio
private Integer lsk;
private Double tp;
(...)
And query will be:
(...) new MeterDTO(m.id, m.trans_ratio, g.kart.lsk (...)
And after that you can extending your DTO for the next fields you want.
The accepted answer suggests changing the DTO, which would not always an acceptable solution.
Here is a solution with no need to change your DTO.
Write your HQL like this:
from Meter m
join m.exs e with m.id=e.meter.id
join m.meterLog g with m.meterLog.id=g.id
join g.kart k with g.kart.id=k.id and :dt between k.dt1 and k.dt2 "
join g.serv s with g.serv.id=s.id "
join k.kw kw with k.kw.id=kw.id "
join kw.house h with kw.house.id=h.id "
(more joins and wheres)
Note that there should not be any select.
getResultList will give you List<Object[]>. Each entry is an array of {Meter, m.exs, m.meterLog, g.kart, ....}. Pick the ones you need and make your MeterDTO.
In my case:
jpa repo
#Query("from Bind bind "
+ "left join Employee employee "
+ "with bind.empCode = employee.empCode "
+ "where bind.accountName = :hiveAccount and bind.disabled = 1 ")
List<Object[]> listMembers(#Param("hiveAccount") String hiveAccount);
DTO
public class BindDTO {
Bind bind;
Employee emp;
public BindDTO(Object[] objs) {
this((Bind) objs[0], (Employee) objs[1]);
}
service
myRepo.listMembers(hiveAccount).stream().map(BindDTO::new).collect(Collectors.toList());