How do I form this complex query in Hibernate? - java

I'm building a REST service using Hibernate, Spring HATEOAS and Jackson. I am adding a method which returns a JSON representation of the results of a query like the one below;
SELECT ERRORS.DMN_NAM, CODES.MSG_TXT,
FROM SERV_ERR ERRORS, EVENT_CD CODES
WHERE ERRORS.SERV_RESP_CD_TXT = CODES.CD_TXT
GROUP BY ERRORS.DMN_NAM, ERRORS.SERV_NAM, CODES.MSG_TXT,
ERRORS.SERV_ERR_CNT, ERRORS.ERR_TS_NUM
ORDER BY ERRORS.DMN_NAM, CODES.MSG_TXT
I currently have two objects defined (ErrorsEntity and EventCodeEntity) which map to the tables SERV_ERR and EVENT_CD.
So the results of this query will be a list, but not of ErrorsEntity or EventCodeEntity but rather an amalgamation of the two entities.
Up to now, my queries have all returned objects that map directly to one table like so:
public List<ErrorsEntity> getErrors(double daysPrevious, double hoursToShow);
What's the best way to handle this in Hibernate where the results of a query aren't objects that are mapped to a single table and how can I write this query in HQL?

It's better to stick to an SQL query then, since HQL makes sense only when you plan on changing states from the resulted entities. In your case, the SQL is a better alternative, since it doesn't really follow the standard and you only want a projection anyway. You could remove the group by with distinct but it will require a derived table, which can be done in plain SQL anyway.
List dtos = s.createSQLQuery(
"SELECT " +
" ERRORS.DMN_NAM AS dmnNam, " +
" CODES.MSG_TXT AS msgTxt " +
"FROM SERV_ERR ERRORS, EVENT_CD CODES " +
"WHERE ERRORS.SERV_RESP_CD_TXT = CODES.CD_TXT " +
"GROUP BY " +
" ERRORS.DMN_NAM, " +
" ERRORS.SERV_NAM, " +
" CODES.MSG_TXT, " +
" ERRORS.SERV_ERR_CNT, " +
" ERRORS.ERR_TS_NUM " +
"ORDER BY " +
" ERRORS.DMN_NAM, " +
" CODES.MSG_TXT "
.addScalar("dmnNam")
.addScalar("msgTxt")
.setResultTransformer( Transformers.aliasToBean(MyDTO.class))
.list();
Make sure YourDTO has a matching constructor, and the types are the exactly like ee.dmn.nam and ece msgTxt.
Instead of group by I'd choose:
SELECT dmnNam, msgTxt
FROM (
SELECT DISTINCT
ERRORS.DMN_NAM AS dmnNam,
ERRORS.SERV_NAM,
CODES.MSG_TXT AS msgTxt,
ERRORS.SERV_ERR_CNT,
ERRORS.ERR_TS_NUM
FROM SERV_ERR ERRORS, EVENT_CD CODES
WHERE ERRORS.SERV_RESP_CD_TXT = CODES.CD_TXT
ORDER BY
dmnNam,
msgTxt
) as DATA

Related

Querying two tables in a database with Java

I have been defeated by the great SQL boss and am now requesting assistance.
Ive removed spaces in table names to avoid confusion
Anyways, I have two tables Orders and Order Details. I need to query a few columns from both. So far, I can query Orders just fine, but when it comes to querying Order Details, or the two together, I get errors.
My Question is this: How do I query two tables?
(note: semicolon is at the bottom, imagine it's there)
Here's what works so far on Orders:
String queryString = "select `Order Date`, Freight "
+ "from Orders "
+ "where Orders.`Order ID` = ? "
Here's my attempt to just grab one column from Order Details and the error to follow
String queryString = "select Product "
+ "from `Order Details` "
+ "where `Order Details`.`Order ID` = ? "
net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.1 user lacks privilege or object not found: ORDER DETAILS.ORDER ID
at net.ucanaccess.jdbc.UcanaccessConnection.prepareStatement(UcanaccessConnection.java:528)
Here's my attempt to grab both at once and the error to follow
String queryString = "select `Order Date`, Freight, Product "
+ "from Orders, `Order Details` "
+ "where Orders.`Order ID` = ? "
net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.1 user lacks privilege or object not found: PRODUCT
at net.ucanaccess.jdbc.UcanaccessConnection.prepareStatement(UcanaccessConnection.java:528)
Here's the above attempt with an extra line at the bottom combining them (I don't know what this does), but it alters the error.
String queryString = "select `Order Date`, Freight, Product "
+ "from Orders, `Order Details` "
+ "where Orders.`Order ID` = ? "
+ "and Orders.`Order ID` = `Order Details`.`Order ID`"
net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.1 user lacks privilege or object not found: ORDER DETAILS.ORDER ID
at net.ucanaccess.jdbc.UcanaccessConnection.prepareStatement(UcanaccessConnection.java:528)
Putting the table name in quotes doesn't work in any SQL-Server i know without changing some configuration.
The correct way is using []:
MSSQL-Example:
SELECT * FROM [Order Details]
Your query may look like this:
String queryString = "SELECT Product "
+ "FROM [Order Details] "
+ "WHERE `Order ID` = ? "
But i would suggest to work without whitspaces within any identifier.
Read about JOIN statements, this will allow you to work with two tables. Try use something like “SELECT your_columns FROM orders o JOIN orderDetails od ON o.id = od.order_id”.
Errors like “object not found” means you didn’t create table. Wish it’ll help.

JPQL Create "Dynamic" Query to execute in repository

Edit-
I'll add the use case to clear up the function of this.
The user will select two dates - a start date and an end date - these are then passed on and used to select the tables (each year has its own table). In one use case where the two given dates lie in the same year it's a simple query on that table alone.
However, if the two dates are different years I will need to join all tables (so 2011-2013 will be three tables connected, to search through) and thus, I want a dynamic fix to this. I know building up a query like below is against security - just thought something similar would work. As the system will get new tables each year I also dont want to have to manually add however many new queries for each case (2011-2016, 2014-2018, 2011-2019.. etc)
I have a question about whether it is possible to create a dynamic query as a String like below and then pass that through to service -> repository, and use that as a query?
for (int i = 0; i < yearCondition; i++) {
if (i == 0) {
query += "SELECT md.Device_ID, l.locationRef, " + reportRunForm.getStartDate() + " as 'From Date', "
+ reportRunForm.getEndDate() + " as 'To Date' "
+ "from mData.meterdata" + iDateStart.substring(0, 4)
+ " join MOL2.meters m on device_ID = m.meterUI "
+ "join MOL2.locations l on m.locationID = l.locationID "
+ "join MOL2.meterreg mr on m.meterID = mr.meterID "
+ "where mr.userID = ?1";
}
query += "UNION SELECT md.Device_ID, l.locationRef, " + reportRunForm.getStartDate() + " as 'From Date', "
+ reportRunForm.getEndDate() + " as 'To Date' "
+ "from mData.meterdata" + (Integer.parseInt(iDateStart.substring(0, 4))+i)
+ " join MOL2.meters m on device_ID = m.meterUI "
+ "join MOL2.locations l on m.locationID = l.locationID "
+ "join MOL2.meterreg mr on m.meterID = mr.meterID "
+ "where mr.userID = ?1";
}
I may have the wrong idea with how this works, and I know I could create and persist a query through entitymanager, but wanted to know whether doing it through the repository would be possible?
My thought was I'd build up the query like above, pass it through to service and then to repository, and bind it as value in #Query annotation but this doesn't seem possible. I'm likely approaching this wrong so any advice would be appreciated.
Thanks.
Edit -
Had a goof. Understand doing it at all like that is stupid, an approach to build up something similar is what I'm looking for that is still secure.
Maybe this annotations before your POJO can help
#org.hibernate.annotations.Entity(dynamicInsert = true)
for example two tables district and constituency ...
Dynamic query
query += "select *from constituency c where 1=1";
if(constituencyNumber!=null)
query +=" and c.constituency_number like '"+constituencyNumber+"%'";
query += " group by c.district_id";
OR
select *from constituency c where (c.constituency_number is null or c.constituency_number like '1%') group by c.district_id;

Multiple tables join via Spring JdbcTemplate returns an empty result

Trying to join 3 tables within a query returns an empty result. Strange enough, having one table removed (two tables join) returns some set. Here is what I do:
String sql = "SELECT\n" +
" tc.constraint_name, tc.table_name, kcu.column_name, \n" +
" ccu.table_name AS foreign_table_name,\n" +
" ccu.column_name AS foreign_column_name, constraint_type \n" +
"FROM \n" +
" information_schema.table_constraints AS tc \n" +
" JOIN information_schema.key_column_usage AS kcu\n" +
" ON tc.constraint_name = kcu.constraint_name\n" +
" JOIN information_schema.constraint_column_usage AS ccu\n" +
" ON ccu.constraint_name = tc.constraint_name\n" +
"WHERE constraint_type = 'FOREIGN KEY'";
List<Map<String, Object>> foreignTable1 = jdbcTemplate(getShardId(sku)).queryForList(sql);
Would always return an empty set.
Try using outer joins and check whether there are rows which don't have corresponding IDs so that the join removes the non-matching rows. Especially that you write, that two tables result in a non-empty result set seems to indicate, that the join with the third table does not result in matching rows of the result set of the first two.

Spring-Data-JPA paging

I am using the Datatables jQuery plugin to display a List of orderpositions.
While debugging my application I stumbled upon the following inconsistency.
To retrieve the data, I used the #Query-Notation for my custom query:
#Query("select op from OrderPosition " +
"op where op.order.created >= ?1" +
" and op.depot in ?2" +
" and op.order.deliveryAddress.deliveryMode in ?3" +
" and op.pickStatus in ?4 and op.order.id like ?5 ")
Page<OrderPosition> findOrderpositionsByFilterStatus(Date date, List<Integer>depots, List<DeliveryMode> deliveryModes, List<ArticleStatusType> pickStatus, String sSearch, Pageable p);
The error occured is the following:
I have a set of 81 Orderpositions test data. In the frontend I am able to filter by several criteria. One of the criteria is the deliveryMode (express|standard). I display 10 Entries at a time. The total number of expressdeliveries is 6. When paging through the Pages only 2 express are displayed. When filtering explicitely on express I get all 6.
When I add some sort of ordering e.g:
#Query("select op from OrderPosition " +
"op where op.order.created >= ?1" +
" and op.depot in ?2" +
" and op.order.deliveryAddress.deliveryMode in ?3" +
" and op.pickStatus in ?4 and op.order.id like ?5 " +
"order by op.order.id desc")
Page<OrderPosition> findOrderpositionsByFilterStatus(Date date, List<Integer>depots, List<DeliveryMode> deliveryModes, List<ArticleStatusType> pickStatus, String sSearch, Pageable p);
I get all 6 of 'em.
What brings me to the question:
When using non annotated queries - either the generic findAll() or queries from method names- does Spring-Data-JPA use an order by-clause internally, when detecting a pageable? Or inverse: Do I have to add an order by-clause in each of my custom queries, when using a pageable?
Yes inded. Spring Data uses an OrderBy (with a default desc) if you do not specify further.
Look at the Logs:
SELECT t0.oid, t0.jpaversion, t0.amount, [...]
FROM [...]
WHERE [..] AND (t13.jpatype IS NULL OR t13.jpatype IN (?))
ORDER BY t0.oid DESC LIMIT ?, ?

Joining tables using criteria and sqlProjection

I have the following function that builds a Hibernate Criteria to generate binned data:
private Criteria getCustomBinResultCriteriaSQL(double binSizeX, double binSizeY, String xVar, String yVar, String customBinQuery) {
return createCriteria(Residue.class)
.setProjection(Projections.projectionList()
.add(Projections.sqlGroupProjection(
"floor(" + xVar + " / " + binSizeX + ") * " + binSizeX + " as xBin, " +
"floor(" + yVar + " / " + binSizeY + ") * " + binSizeY + " as yBin, " +
"CAST (" + customBinQuery + " AS DOUBLE PRECISION) as customBinResult",
"xBin, yBin",
new String[] { "xBin", "yBin", "customBinResult" },
new Type[] { Hibernate.DOUBLE, Hibernate.DOUBLE, Hibernate.DOUBLE })))
.setResultTransformer(Transformers.aliasToBean(CustomBinResult.class));
}
This all works pretty well for data within the same table (residue), but let's say my datastructure is like this:
pdbentry:
id
pdbvar
expmethod:
id
expvar
residue:
id
resvar
pdbentry has a one-to-one relation with expmethod, and a one-to-many relation with residue.
How would I go about joining the residue table with expmethod, based on the criteria-builder above. So in other words: what do I need to add to the criteria to be able to have "expvar" as xVar?
I tried adding something like:
.setFetchMode("pdbentry", FetchMode.JOIN);
.setFetchMode("expmethod", FetchMode.JOIN);
at the end, but then I still couldn't put "expvar" nor "expmethod.expvar" as xVar.
Any ideas?
I'm aliasing the joined-to tables via
.createCriteria("rootEntity.foreignEntity", "someAlias")
This makes rootEntity.foreignEntity.someProperty available to the Criteria query as someAlias.someProperty (usually I choose "someAlias" to be the same as "foreignEntity").
Since I usually want an eager fetch, I tend to have Criteria.INNER_JOIN or Criteria.LEFT_JOIN on the createCriteria call, too.
Have you tried "pdbentry.expmethod.expvar"?
It is not clear (to me) what you are trying to do from just this, but the way you define your entity relationships, the way to access the relevant expvar from a given residue would be "residue.pdbentry.expmethod.expvar".

Categories