I have hibernate query, where I need to transform one column with date_trunc, however I don't know how to bind this transformed value to my joined entity object
Is there any possibility to pass this transformed column to mine hibernate entity?
Of course it's conceptual code in case it wouldn't make sens for you:
#Entity
class SomeClass {
private ZonedDateTime orderTime;
#ManyToOne(targetEntity = SomeStats.class)
#JoinColumn(name = "SOME_STATS_ID", referencedColumnName="ID")
private SomeStats someStats; // Class that I need aggregate
private long id;
// ... other fields that aren't grouped
}
class SomeStats {
private long id;
private double price; // I need to take average of this
private ZonedDateTime paymentTime; // And truncate this
}
I need some idea to make something similar to:
SELECT someclass.*, AVG(somestats.price), date_trunc('hour', somestats.payment_time) as timebucket
FROM SOME_CLASS someclass
JOIN SOME_STATS somestats ON somestats.id = someclass.some_stats_id
GROUP BY someclass.id, timebucket
but with average as SomeStats.price and truncated date as SomeStats.paymentTime, is it even possible?
Related
I'm trying to add a Map entity in two ways:
Map<Enum,Enum>:
#Entity
#Table(name = "reservations")
public class Reservation {
...
private Map<Request, RequestImportance> guestsRequests;
...
}
Map<Enum,Boolean>:
public class Room {
...
private Map<Request, Boolean> requestsMap;
}
These are the enums Request and RequestImportance:
public enum Request {
ELEVATORPROXIMITY,
SEAVIEW,
BATHTUB,
BALCONY,
HANDICAPPED,
HIGHFLOOR
}
public enum RequestImportance {
NOT_IMPORTANT,
NICE_TO_HAVE,
MUST;
}
I'm not sure which attributes I'm supposed to use to map to the DB.
I'm using mySQL for this project.
EDIT:
Probably not possible to do that. I figured that I would change the Map to a List and create a new Class which contains an Enum field. That way I can represent the data in the DB.
You could use #ElementCollection for that
#ElementCollection
private Map<Request, RequestImportance> guestsRequests = new HashMap<>();
By default it is mapped to the table reservation_guests_requests that has three columns
reservation_id (refers to Reservation obviously)
guests_requests_key (contains Request ordinal number)
guests_requests (contains RequestImportance ordinal number)
If you are not fine with these names or need to fit the existing table they could be adjusted
#ElementCollection
#CollectionTable(
name = "guests_requests",
joinColumns = #JoinColumn(name = "rsrv_id")
)
#MapKeyColumn(name = "request_n")
#Column(name = "request_importance_n")
private Map<Request, RequestImportance> guestsRequests = new HashMap<>();
This time it will be guests_requests with
rsrv_id (I guess reservation_id is a better name, just showed the possibility to adjust it)
request_n
request_importance_n
On the one hand saving enum ordinal is fine, on the other hand it is sensitive to its order and could make issues if the values are reordered.
Luckily it could be switched to String or any custom index or an abbreviation with AttributeConverter. If you put #Enumerated(EnumType.STRING) above the map it will affect only the value i.e. RequestImportance
I have a parent Table, called "PN", which in it's entity contains a list of another Entity "PnDett", which is related to the first table.
I want to execute a query that will give me the list of PN with my where condition, but that will filter the list of "PnDett" also based on a where condition.
How can i achieve this?
This is the PN mapping:
#Entity
#Table(name = "PN")
public class Pn implements java.io.Serializable {
private static final long serialVersionUID = 2556879508428749494L;
#Id
#Column(name="ID_PN", unique = true, nullable = false)
private BigDecimal idPN;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_DOC")
private Date dataDoc;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_COMP_BANC")
private Date dataCompBanc;
#Column(name="STATO_PN")
private String statoPN;
#Column(name="TESTO_TESTATA")
private String testoTestata;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_INVIO_SAP")
private Date dataInvioSap;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_INS")
private Date dataIns;
#Column(name="ID_UTENTE_AGG")
private BigDecimal idUtenteAgg;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_CONTABILE")
private Date dataContabile;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_REND_INTEGR")
private Date dataRendIntegr;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DATA_AGG")
private Date dataAgg;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pn")
private Set<PnDett> pnDetts = new HashSet<>(0);
This is the PnDett mapping:
#Entity
#Table(name = "PN_DETT")
public class PnDett implements java.io.Serializable {
private static final long serialVersionUID = 2556879508428749494L;
#Id
#Column(name="ID_PN_DETT", unique = true, nullable = false)
private BigDecimal idPNDett;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="ID_PN", nullable=false)
private Pn pn;
#OneToOne
#JoinColumn(name="ID_DOM_TIPO_SCRITTURA")
private DomTipoScrittura domTipoScrittura;
#Column(name="TIPO_DOCUMENTO")
private String tipoDocumento;
#Column(name="ID_CALCOLO")
private String idCalcolo;
#Column(name="CONTO_COGE")
private String contoCoge;
#Column(name = "IMPORTO_AVERE")
private BigDecimal importoAvere;
#Column(name = "IMPORTO_DARE")
private BigDecimal importoDare;
#OneToOne
#JoinColumn(name="ID_UTENTE")
private Utente utente;
#Column(name = "DATA_INS")
private Date dataIns;
#Column(name = "TIPO_DETTAGLIO")
private String tipoDettaglio;
#Column(name = "SE_CANC")
private Integer seCancellato;
In HQL, i wrote this query "select * from Pn firstNote where firstNote.dataContabile = (a param i put myself, in this case 20 april 2020) and firstNote.pnDetts.seCancellato = (a param i put myself, in this case 0) and firstNote.pnDetts.importoDare <> 0 or firstNote.pnDetts.importoAvere <> 0"
The results are, quite frankly, a mess. I have only two lines in my PN table that have the parameter dataContabile set at 20 april 2020, and yet i get 18 results, and the results are an Object[] which somehow containes both entities. Inside the PN object i do have the pnDetts list filtered to match the date i'm searching them for, but the other filters don't even work. How can i fix this mess? Ideally, my result should be a list of two PN objects that have the pnDetts list filtered, but i don't know how to achieve this.
Edit:
Ok, i've made an SQL query and found out that the PnDett lines related to that date that have either importoDare <> 0 or importoAvere<>0 are exactly 18, that's why i get 18 sets of objects[]. But how can i have just two PN objects, with the list inside filtered instead?
In order to make the filter work, we can make use of fetch, this will make sure that the initial query fetches all the required data.
select * from Pn firstNote join fetch firstNote.pnDetts pnd where firstNote.dataContabile = :param1 and pnd.seCancellato = :param2 and pnd.importoDare <> 0 or pnd.importoAvere <> 0
Like mentioned by Vishnu you can use join fetch, but there are many issues with such an approach. Since the result of the query are managed entities, filtering the collection is problematic. When your transaction finishes, the filtered out elements might be removed so it's important that you immediately detach all entities after such a query e.g. view entityManager.clear().
If you also want Pn objects that have empty pnDetts because nothing matches, you are out of luck with join fetch and have to use a normal join like this:
SELECT firstNote, pnd
FROM Pn firstNote
LEFT JOIN firstNote.pnDetts pnd
ON pnd.seCancellato = :param2 AND pnd.importoDare <> 0
OR pnd.importoAvere <> 0
WHERE firstNote.dataContabile = :param1
This is a scalar query which will return a Object[] so you have to collect the lists manually.
today I met an interesting problem with stream API filter.
Namely, the problem is as follows: We have a list of parameters to filter as a function argument. Using the streams we want to filter all the records and return the ones that match the parameters we gave as the function argument. For example [Car, 2012, 50000, Audi]. I realize that in this situation I could use Criteria API or Full Search Text but I would like to do it without additional API only using Java streams.
Customer.class
public class Customer {
#Id
#GeneratedValue
private Long id;
private String name;
private Integer age;
#OneToMany(mappedBy = "customer")
List<CustomerOrder> customerOrders;
}
Product.class
public class Product {
#Id
#GeneratedValue
private Long id;
private String name;
private BigDecimal price;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(mappedBy = "product")
List<CustomerOrder> customerOrders;
}
CustomerOrder.class
public class CustomerOrder {
#Id
#GeneratedValue
private Long id;
private String description;
private Integer quantity;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "customer_id")
private Customer customer;
#ManyToOne()
private Product product;
}
With full Lombok utility.
The problem is that the list of arguments may vary. We can blend an argument order to something like [50000, Audi, Car, 2012] and we suppose to receive the same result. Otherwise, assuming that we would have a list of parameters that would be previously validated by us for correctness, we could program it on a filtering basis after the next parameters, but in this case, we are not so sure.
Problem clarification:
We give as an argument a list of parameters we want to search for: [50,000, Audi, Car, 2012]. In normal case, assuming that the order will always be correct, so we first specify the price, then the brand, type of goods and year, we could filter the records one by one using a standard filter with the stream API, i.e. first search at the price, then by the brand and so on. The problem occurs when the order is changed and we give first the yearbook then the brand then the price and type of goods. In this situation, our filter will not work because it will not find anything because we changed the order of items that we gave as an argument for our filtering function. In this situation, the part of our filter responsible for the search by parameter: the price will receive the year of the goods, which for obvious reasons will not return decent results assuming. I hope that this time I summarized the problem as best as possible.
Thank you so much for pieces of advice.
I have an entity that looks like
#Entity
#Table(name = "log_entry")
public class LogItem {
#Id
#Column(name = "id")
private long id;
#Column(name = "request_id", nullable = false)
private String requestId;
#Column(name = "request_type")
#Enumerated(EnumType.STRING)
private RequestType requestType;
#Column(name = "entry_type")
#Enumerated(EnumType.STRING)
private LogType logType;
#Column(name = "operation_name")
private String operationName;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_stamp", nullable = false)
private Date timeStamp;
#Column(name = "attr_1")
private String attr1;
#Column(name = "attr_2")
private String attr2;
// ...
#Column(name = "attr_10")
private String attr10;
}
This maps to a table that holds audit records, typically there would be one root row and a bunch of rows inserted with the same requestId with varying logType, requestType and operationName values. The attrX columns hold different values depending on the types/operations involved.
I need to implement a generic query tool that can find say find the root request and one or more specific child element depending on the values of attrs. However, I'm struggling to simply join the log table to itself.
The bit I'm stuck at is trying to emulate the following:
SELECT l1.*, l2.* FROM log_entry l1
JOIN log_entry l2
ON l2.request_id = l1.request_id
AND l2.entry_type = 'Something'
AND l2.operation_name = 'someOperation'
WHERE l2.operation_name = 'someOtherOperation'
This is my code
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<LogItem[]> criteria = cb.createQuery(LogItem[].class);
Root<LogItem> root = criteria.from(LogItem.class);
Join<LogItem, LogItem> responseJoin =
root.join(LogItem.requestId.getName(), JoinType.INNER);
ArrayList<Predicate> predicates = new ArrayList<Predicate>();
// Simply adds preciates for log type etc...
getCommonPredicates(filter, cb, root, predicates);
// Add predicates for attr values
addAttributePredicates(cb, predicates, filter.getAttributeFilters());
criteria.where(predicates.toArray(new Predicate[predicates.size()]));
TypedQuery<LogItem[]> query = entityManager.createQuery(criteria);
List<LogItem[]> resultsList = query.getResultList();
This however gives me the error:
org.hibernate.jpa.criteria.BasicPathUsageException: Cannot join to attribute of basic type
I understand I could get around this by adding LogItem references to the entity class but I can't modify the table structure. Is there a way to do this without reverting to JBDC? The SQL is trivial I just can't see how to transform it into a criteria. I'm using hibernate, while I would prefer a JPA solution I'm not adverse to using hibernate specific code.
You can only join via a 1-1/1-N/M-N/N-1 relation, since JPA tries to keep to the spirit of O-O. Your alternatives here are
Add an extra from clause and specify a where clause to accompany
it. This likely will create a CROSS JOIN.
Add a relation to your model so then you can use join() and define INNER or LEFT OUTER.
Clearly some of these may not be an option for your situation.
I have a POJO that maps to a row in a specific table. The row describes an image in some site and contains data like width, height, url, some sort of status and some more fields. In some legacy code I have a query (in hibernate) that returns the url and the status. This data is encapsulated in a class ImageStatusAndOrigFilename.
I think this is a bad idea because:
What if tomorrow I need to query for some other fields? The name is too coupled to the data.
In the past the only way to get the image width and height was to parse the url. Today we map the width and height in the db and thus I now need to get the image size and status (and I don't care anymore about the original file name). So I need to change this class but can't because it's being used in other places in the code. I wish to get to something more generic that is not coupled to a specific scenario and can be extended when needed.
I'm trying to figure out which data structure to use. Should I use the original POJO that has all the fields but leave some of them null (I don't want to query all of the fields as I don't need all of them in this scenario). Should I create another POJO for this specific query (with a better name of course)?
Any other suggestions are surely welcome as well.
EDIT:
The POJO:
#Entity
#Table(name = "web_image")
public class WebImage {
private long id;
private Document document;
private Integer mediaType;
private Integer width;
private Integer height;
private Date creationDate;
private Date modificationDate;
private String origUrl;
private ImageStatus status;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
public Long getId() {
return id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "document_id")
public Document getDocument() {
return document;
}
public void setDocument(final OBDocument document) {
this.document = document;
}
#Column(name = "width")
public Integer getWidth() {
return width;
}
public void setWidth(final Integer width) {
this.width = width;
}
// Other getters and setters for the rest of the private fields
}
The query:
SELECT b.document_id , b.status , b.orig_file_id, a.min_id as id FROM web_image b,
( SELECT x.document_id, MAX(x.id) max_id, MIN(x.id) min_id
FROM web_image x
WHERE x.document_id in ( :docs ) GROUP BY x.document_id) a
WHERE a.max_id = b.id
What about this:
#MappedSuperclass
public class ImageStatusAndOrigFilename {
...
}
#Entity
public class WebImage extends ImageStatusAndOrigFilename {
...
}
Now you have both classes, the old one is not an entity, but its clients don't know anything about it, and all fetching and persistings are on WebImage class, but you can query for ImageStatusAndOrigFilename.
If it is really necessary to avoid loading unused columns - and you would need to profile to determine if any saving is actually worth it - then a simple solution is simply to write a query using a JPA Contructor Expression.
JPQL:
http://docs.oracle.com/html/E24396_01/ejb3_langref.html#ejb3_langref_constructor
Criteria API:
http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/querycriteria.html#querycriteria-typedquery-multiselect
Problem with these is you need to add a constructor for each set of properties.
Hibernate specific option - no Constructors required:
See Transformers.aliasToBean:
Java - Hibernate criteria.setResultTransformer() initializes model fields with default values
For all of these options you can use some other DTO or use your existing Entity i.e. return an unmanaged instance.
Lazy Loading
You can also lazy load fields however that required byte code manipulation:
http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-fetching-lazyproperties
Also note the comments:
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note
that this is mostly a marketing feature; optimizing row reads is much
more important than optimization of column reads. However, only
loading some properties of a class could be useful in extreme cases.