I am currently trying to execute this method in my repository:
#Query(value = "SELECT a.ID as 'ID', a.STATUS as 'STATUS', a.SOURCE as 'SOURCE', a.EXTERNAL_REFERENCE_ID as 'EXTERNAL_REFERENCE_ID', s.ID as 'STREET_ID', l.ID as 'LOCALITY_ID', c.ID as 'COUNTRY_ID', p.ID as 'POSTCODE_ID', a.INSERTED_TIMESTAMP as 'INSERTED_TIMESTAMP', a.UPDATED_TIMESTAMP as 'UPDATED_TIMESTAMP', a.DELETED_TIMESTAMP as 'DELETED_TIMESTAMP'\n" +
"FROM address_management_svc.subaddress sa\n" +
"INNER JOIN address_management_svc.address a\n" +
"ON (sa.ADDRESS_ID = a.ID)\n" +
"INNER JOIN street s\n" +
"ON (s.id = a.STREET_ID)\n" +
"INNER JOIN locality l\n" +
"ON (l.id = a.LOCALITY_ID)\n" +
"INNER JOIN country c\n" +
"ON (c.id = a.COUNTRY_ID)\n" +
"INNER JOIN post_code p\n" +
"ON (p.id = a.POSTCODE_ID)", nativeQuery = true)
List<AddressEntity> getAll();
I switched on the jpa logging and for some reason, I am getting all of this (below is a sample as in reality I am getting all records like this which is hundreds of thousands)
select
streetenti0_.ID as id1_4_0_,
streetenti0_.DELETED_TIMESTAMP as deleted_2_4_0_,
streetenti0_.EXTERNAL_REFERENCE_ID as external3_4_0_,
streetenti0_.INSERTED_TIMESTAMP as inserted4_4_0_,
streetenti0_.LOCALITY_ID as locality9_4_0_,
streetenti0_.NAME as name5_4_0_,
streetenti0_.SOURCE as source6_4_0_,
streetenti0_.STATUS as status7_4_0_,
streetenti0_.UPDATED_TIMESTAMP as updated_8_4_0_,
localityen1_.ID as id1_2_1_,
localityen1_.COUNTRY_ID as country_9_2_1_,
localityen1_.DELETED_TIMESTAMP as deleted_2_2_1_,
localityen1_.EXTERNAL_REFERENCE_ID as external3_2_1_,
localityen1_.INSERTED_TIMESTAMP as inserted4_2_1_,
localityen1_.NAME as name5_2_1_,
localityen1_.SOURCE as source6_2_1_,
localityen1_.STATUS as status7_2_1_,
localityen1_.UPDATED_TIMESTAMP as updated_8_2_1_,
countryent2_.ID as id1_1_2_,
countryent2_.DELETED_TIMESTAMP as deleted_2_1_2_,
countryent2_.INSERTED_TIMESTAMP as inserted3_1_2_,
countryent2_.NAME as name4_1_2_,
countryent2_.UPDATED_TIMESTAMP as updated_5_1_2_
from
STREET streetenti0_
left outer join
LOCALITY localityen1_
on streetenti0_.LOCALITY_ID=localityen1_.ID
left outer join
COUNTRY countryent2_
on localityen1_.COUNTRY_ID=countryent2_.ID
where
streetenti0_.ID=?
2021-09-06 17:06:19.012 address-management-service TRACE natty-lappy-work [http-nio-8080-exec-1] org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [INTEGER] - [179779]
2021-09-06 17:06:19.031 address-management-service DEBUG natty-lappy-work [http-nio-8080-exec-1] org.hibernate.engine.jdbc.spi.SqlStatementLogger -
select
localityen0_.ID as id1_2_0_,
localityen0_.COUNTRY_ID as country_9_2_0_,
localityen0_.DELETED_TIMESTAMP as deleted_2_2_0_,
localityen0_.EXTERNAL_REFERENCE_ID as external3_2_0_,
localityen0_.INSERTED_TIMESTAMP as inserted4_2_0_,
localityen0_.NAME as name5_2_0_,
localityen0_.SOURCE as source6_2_0_,
localityen0_.STATUS as status7_2_0_,
localityen0_.UPDATED_TIMESTAMP as updated_8_2_0_,
countryent1_.ID as id1_1_1_,
countryent1_.DELETED_TIMESTAMP as deleted_2_1_1_,
countryent1_.INSERTED_TIMESTAMP as inserted3_1_1_,
countryent1_.NAME as name4_1_1_,
countryent1_.UPDATED_TIMESTAMP as updated_5_1_1_
from
LOCALITY localityen0_
left outer join
COUNTRY countryent1_
on localityen0_.COUNTRY_ID=countryent1_.ID
where
localityen0_.ID=?
I want it to execute the exact statement I give it, without translating anything. I also had it set to nativeQuery = true but that doesn't seem to help. Any idea what I can do?
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(AddressEntity.class)
public interface AddressDto {
#IdMapping
Long getId();
String getStatus();
String getSource();
String getExternalReferenceId();
#Mapping("street.id")
Long getStreetId();
#Mapping("locality.id")
Long getLocalityId();
#Mapping("country.id")
Long getCountryId();
#Mapping("postCode.id")
Long getPostcodeId();
Instant getInsertedTimestamp();
Instant getUpdatedTimestamp();
Instant getDeletedTimestamp();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
AddressDto a = entityViewManager.find(entityManager, AddressDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<AddressDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
In your case, it will even avoid all the joins and just select the foreign keys as they are in the address table.
Related
#Query("select p.id,m.id,c.tipoDocumento,c.codigoFact from Corresponsable c"
" inner join Pais p"
" on p.id = c.pais.id"
" inner join Moneda m"
" on m.id = c.moneda.id"
" order by p.nombre,m.nombre,c.tipoDocumento,c.codigoFact")
List<Corresponsable> getFindPorCodPaisMonedaTipoDoc();
I did it this Query with JPA #Query in my Repository, but i would like to paginate my results with a number of page and numer of offset. What would be the best way to paginate keeping my values ordered?
The Spring Data has native support to paginating, you can simply declare:
List<Corresponsable> getFindPorCodPaisMonedaTipoDoc(Pageable pageable).
You can manage the pagination with:
Pageable pageable = PageRequest.of(page, pageSize, Sort.by(sortBy));
For more info: https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/domain/Pageable.html
Below is the JPQL query I wrote but it is not working. I get the following error: NoViableAltException: unexpected token: max
Can someone please point out the error in the below query?
SELECT new com.chp.cef.api.dto.EventSearchDTO(ec.eventName, e.eventKey, e.eventTime, CAST(e.messagePayload AS string), epl.status, epl.statusReason, CAST(epl.processLogPayload AS string), ec.source, " +
"epl.eventProcessLogKey, epl.eventReceivedTime, epl.eventProcessedTime) FROM Event e " +
"left join (SELECT inEpl.eventKey, max(inEpl.eventReceivedTime), inEpl.eventProcessLogKey, inEpl.status, inEpl.statusReason, inEpl.processLogPayload, inEpl.eventProcessedTime" +
"FROM EventProcessLog inEpl GROUP BY inEpl.eventKey) epl ON e.eventKey = epl.eventKey " +
"left join EventConfiguration ec ON e.eventConfigKey = ec.eventConfigurationKey WHERE ec.eventName = coalesce(:eventName, ec.eventName) " +
"AND epl.status = coalesce(:eventStatus, epl.status) AND e.eventTime >= :eventStartTime AND e.eventTime <= :eventEndTime
You can't join a subquery in JPQL/HQL. You will have to reformulate this to a correlated subquery in the ON clause.
Having said that, I think this is a perfect use case for Blaze-Persistence Entity Views along with the it's support for fetching the TOP-N per category through the #Limit annotation.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
But Blaze-Persistence not only helps you with the DTO mapping, it also enabled the use of some of the more advanced SQL concepts, in this case, lateral joins.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Event.class)
public interface EventSearchDTO {
#IdMapping
String getEventKey();
String getEventName();
Instant getEventTime();
#Mapping("CAST_STRING(messagePayload)")
String getMessagePayload();
// ...
#Limit(limit = "1", order = "eventReceivedTime DESC")
#Mapping("EventProcessLog[eventKey = VIEW(eventKey)]")
// If you have an association mapping, can be simplified to
// #Mapping("processLogs")
EventProcessLogDto getLatestLog();
#EntityView(EventProcessLog.class)
interface EventProcessLogDto {
#IdMapping
String getEventKey();
String getEventProcessLogKey();
// ...
}
#Mapping("EventConfiguration[eventConfigurationKey = VIEW(eventConfigKey)]")
EventConfigurationDto getConfiguration();
#EntityView(EventConfiguration.class)
interface EventConfigurationDto {
#IdMapping
String getEventConfigurationKey();
// ...
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
EventSearchDTO a = entityViewManager.find(entityManager, EventSearchDTO.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
This will produce a SQL query similar to the following
SELECT ec.eventName, ...
FROM Event e
left join lateral (SELECT * FROM EventProcessLog inEpl WHERE e.eventKey = inEpl.eventKey ORDER BY inEpl.eventReceivedTime DESC LIMIT 1) epl ON 1=1
left join EventConfiguration ec ON e.eventConfigKey = ec.eventConfigurationKey
I want to reduce the amount of querys run by spring. When getting an object with #ElementCollection via SQL I want to get the data for the ElementCollections directly via a JOIN within the quers.
The attribute with the ElementCollection
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "song_genre_list")
#org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<String> genre;
The Method that uses a custom string:
#Query(
value = "select distinct s.*, g.* from musicdb.songs s left join musicdb.song_genre_list g on s.id = g.song_id where s.name like ?1 or s.artist like ?1",
nativeQuery = true)
List<Song> searchSong(String title);
How I would imagine defining a query that also loads this element collection:
SELECT DISTINCT s.*, g.* FROM musicdb.songs s LEFT JOIN musicdb.song_genre_list g ON s.id = g.song_id WHERE s.name LIKE ?1 OR s.artist LIKE ?1
What Spring currently does (loading the genres for 3 songs with more querys):
Hibernate: select distinct s.*, g.* from musicdb.songs s left join musicdb.song_genre_list g on s.id = g.song_id where s.name like ?1 or s.artist like ?1
Hibernate: select genre0_.song_id as song_id1_4_0_, genre0_.genre as genre2_4_0_ from musicdb.song_genre_list genre0_ where genre0_.song_id=?
Hibernate: select genre0_.song_id as song_id1_4_0_, genre0_.genre as genre2_4_0_ from musicdb.song_genre_list genre0_ where genre0_.song_id=?
Hibernate: select genre0_.song_id as song_id1_4_0_, genre0_.genre as genre2_4_0_ from musicdb.song_genre_list genre0_ where genre0_.song_id=?
What I want Spring to do:
Hibernate: select distinct s.*, g.* from musicdb.songs s left join musicdb.song_genre_list g on s.id = g.song_id where s.name like ?1 or s.artist like ?1
The required Data for the ElementCollection is already included with the join. How can I tell spring to import that Data?
FetchType.EAGER will already load the genres for you, so you dont need to write a join in your original query. But for that hibernate hibernate uses separate queries by default. To change this add the annotation '#Fetch(FetchMode.JOIN)' on the genre field
this is my namedquery:
#NamedQuery(
name = "User.findOneWithLists",
query = "SELECT u FROM User u "
+ "LEFT JOIN FETCH u.aTemplates "
+ "LEFT JOIN FETCH u.bTemplates "
+ "LEFT JOIN FETCH u.bp "
+ "LEFT JOIN FETCH u.aCredentials "
+ "LEFT JOIN FETCH u.st WHERE (st.deleted = false) "
+ "LEFT JOIN FETCH u.bCredentials "
+ "LEFT JOIN FETCH u.cl "
+ "WHERE u.id= :id")
My problem is that I get an error when the application starting:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: LEFT ....
On the st side there is an annotation
#ManyToOne
#JoinColumn(name = "st_user")
private User user;
Any idea how can I handle this where clause?
Check a SQL syntax, you can't use left join after where clause. If you are looking at the SQL generated form that named query you will see that joined tables in the query the where clause comes after joins and should specify equal condition that links those tables by the keys. The primary key of the main table on the left and the foreign key of the joined table on the right. The joined table is specified by the property of your many-to-one association.
#NamedQuery(
name = "findOneWithLists",
query = "from Table t left join User u where u.id= :id"
)
For join conditions Hibernate provides the with keyword, even before JPA 2.1.
The relevant part of your query thus would look like this:
SELECT u FROM User u ... LEFT JOIN u.st WITH st.deleted = false
I'm not sure about LEFT JOIN FETCH u.cl with u.id= :id but if I remember correctly, that's not as easy and might have to be resolved with an adapted join and u.ui = :id in the where condition.
LEFT JOIN FETCH u.st WITH st.deleted = false`
This is not supported, since you can't do a partial fetch.
Following SQL query:
select distinct hotel.country 'Country', hotel.id 'Hotel ID', hotel.Name'Name', room.id 'Room ID'
from room, stay, reservation, hotel
where
(stay.roomid = room.id)
and (stay.reservationid = reservation.id)
and (reservation.status != 'Booked' AND reservation.status != 'CheckedIn')
and (reservation.arrivaldate >= '2012-08-08')
and (reservation.leavedate <= '2012-08-15')
and (room.hotelid = hotel.id)
order by hotel.country, hotel.id, hotel.name, room.id asc
This will give me a list of available rooms per hotel per country. Now I have to put this in HQL, but I can't do so because of this:
Query query = session.createQuery("from room, stay, reservation, hotel where stay.roomid = room.id");
This doesn't work becuase stay.roomid is a Room object whereas room.id is just an integer. I'm pretty new to Hibernate/HQL and the reference manual didn't really bring me anywhere... How can I "convert" (scary word, I know this doesn't involve actual converting) this SLQ query to a HQL statement? Thanks.
UPDATE
Using a join result in the same problem
Query query = session.createQuery("" +
"from Stay stay " +
"join stay.ReservationID reservation " +
"join stay.RoomID room " +
"join room.HotelID hotel " +
"where (stay.RoomID = room.ID)");
I'm really missing something (probably very logical) here...
You haven't shown your entities, but, just as you would use joins in SQL, you need to use joins in HQL:
select ...
from Stay stay
join stay.reservation reservation
join stay.room room
join rom.hotel hotel
where (reservation.status != 'Booked' AND reservation.status != 'CheckedIn')
and (reservation.arrivaldate >= '2012-08-08')
and (reservation.leavedate <= '2012-08-15')
The Hibernate documentation has a whole chapter on HQL, and a section of this chapter about associations in HQL.