I have a entity, something like this:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Entity(name = "warranties")
public class Warranty implements Comparable<Warranty> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "warranty_pk")
private Long id;
#Column(nullable = false)
private Date startDate;
#Column(nullable = false)
private Date expireDate;
#Column
private String note;
#Transient
private Date displayDate;
}
}
Notice that displayDate is annotated with #Transient.
I have two functionalities related to this entity, one is to fetch some of them, to do that I need to check expiration dates. So in my repository I have the following query:
#Query(value =
"SELECT "
+ "IF(expire_date < ((CURDATE() + INTERVAL 1 DAY) - INTERVAL 1 SECOND), expire_date, (expire_date - INTERVAL 90 day)) AS display_date, * "
+ "FROM warranties "
+ "WHERE expire_date < ((CURDATE() + INTERVAL 1 DAY) - INTERVAL 1 SECOND) + INTERVAL 90 DAY "
+ "AND expire_date <= :expire_date "
+ "AND start_date >= :startDate "
+ "AND expire_date <= :endDate + INTERVAL 90 DAY "
+ "ORDER BY display_date DESC",
nativeQuery = true)
List<Warranty> findWarranties(#Param("expire_date") Date expireDate,
#Param("startDate") Date startDate, #Param("endDate") Date endDate);
Its that way so I can retrieve the "displayDate" calculated by the database.
It works fine when I use the following import:
import org.springframework.data.annotation.Transient;
And the other functionality is a save, where I save a given note.
For that case I get the following error:
Caused by: java.sql.SQLSyntaxErrorException: Unknown column 'warranty0_.display_date' in 'field list'
If I use the following import, the second functionality works fine:
import javax.persistence.Transient;
But then the findWarranties doesn't return displayDate.
Any thoughts?
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 have a named query where I want to fetch all records where year and month of a parameter match the database entry:
#NamedQuery(
name = entries.queryX,
query = "SELECT DISTINCT t from entries t " +
"where MONTH(t.myMonth) = :month " +
"and YEAR(t.myMonth) = :year "
)
The value "myMonth" in table entries is a YearMonth:
#Column(name = "MY_MONTH", precision = 7)
private YearMonth myMonth;
I set the query parameters as follows:
query.setParameter("month", date.get(Calendar.MONTH) + 1);
query.setParameter("year", date.get(Calendar.YEAR));
However, this approach does not work... Is there any other way to implement what I want to search for?
My JPA query is very complex because of that I am using nativequery, But when I run it returns different results from JAVA query and Oracle query. However Oracle query returns correct result.What could be the reason for this ?
I changed SELECT statement, try to fetch all of them by a.* and also try to fetch only necessary column. I also remove DATE comparison statements. However interestingly JPA query returns 4 row and all of them were same exactly. I also try to fetch data without TRUNC keyword in DATE comparison part, and it is not worked. In addition I add schema names in front of tables in native query which is not worked again.
public Optional<LocalDateTime> getPaymentPeriod2(LocalDate reportDate, String loanId)
{
final String reportDateParemeter = reportDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
final String query = "SELECT st.t_no, st.islem_tarihi, st.s_kodu, st.odeme_suresi " +
"FROM k_tarihce st, k_tanim sta, kt_tanim tta " +
"WHERE st.s_kodu = sta.s_kodu " +
"AND tta.s_tipi = sta.s_tipi AND tta.t_kodu = st.t_kodu " +
"AND st.t_no = :loanId AND st.t_kodu in (37,55) " +
"AND st.s_kodu = 'K1' AND (TRUNC(st.odeme_suresi) >= TRUNC(TO_DATE(:reportDateParemeter,'yyyy-mm-dd')))";
final Query loanGetPaymentPeriodQuery = entityManager.createNativeQuery(query, LoanStatusHistory.class);
loanGetPaymentPeriodQuery.setParameter("reportDateParemeter", reportDateParemeter);
loanGetPaymentPeriodQuery.setParameter("loanId", loanId);
List<LoanStatusHistory> loanStatusHistoryList = loanGetPaymentPeriodQuery.getResultList();
return loanStatusHistoryList.isEmpty() ? Optional.empty() : Optional.of(loanStatusHistoryList.get(0).getPaymentPeriod());
}
And my entity is:
#Entity
#Table(name = "K_TARIHCE", schema = "LOS")
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Cacheable(false)
#IdClass(LoanStatusHistoryId.class)
public class LoanStatusHistory {
#Id
#Column(name = "T_NO")
private String loanId;
#Id
#Column(name = "ISLEM_TARIHI")
private LocalDate processDate;
#Id
#Column(name = "S_KODU")
private String statusCode;
#Column(name = "ODEME_SURESI")
private LocalDateTime paymentPeriod;
#Column(name = "T_KODU")
private String historyCode;
}
class LoanStatusHistoryId implements Serializable {
private String loanId;
private transient LocalDate processDate;
private String statusCode;
}
I want to same result with Oracle SQL developer. My main objective is fetch ODEME_SURESI field.
I found solution, again !interestingly! Here my new code below, I just not mapped my native query with entity, and return some LocalDate.
final Query loanGetPaymentPeriodQuery = entityManager.createNativeQuery(query);
loanGetPaymentPeriodQuery.setParameter("reportDateParemeter", reportDateParemeter);
loanGetPaymentPeriodQuery.setParameter("loanId", loanId);
final List<LocalDate> loanStatusHistoryList = loanGetPaymentPeriodQuery.getResultList();
return loanStatusHistoryList.isEmpty() ? Optional.empty() : Optional.of(loanStatusHistoryList.get(0));
This is the only change I made. I still not know why it worked?.
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());
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.