Joining tables using criteria and sqlProjection - java

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".

Related

Oracle - How to use START WITH in loop (hibernate select)

I am using hibernate for this select:
NativeQuery sqlQuery = session.createSQLQuery("select :id, min(a.time) " +
"from table1 a, (" +
" select parentid" +
" from (" +
" select LEVEL, parentid" +
" from table2 " +
" START WITH id = :id" +
" CONNECT BY NOCYCLE PRIOR parentid = id" +
" order by level desc)" +
" where rownum = 1" +
" ) b " +
"where a.id = b.parentid");
sqlQuery.setParameter("id", id);
List<Object[]> list = sqlQuery.list();
I need to use this for a lot of ids. Ideally I would pass a list of ids, run that in oracle and return the result. I cannot do that because of the START WITH clause.
This way I send a query to database for each id, which is really slow.
Is there any way to put a list of ids to the query, do this in loop and return back to the application with a list of results?
NativeQuery sqlQuery = session.createSQLQuery("...?...");
sqlQuery.setListParameter("ids", ids);
List<Object[]> list = sqlQuery.list();
Edit: I cannot use recursive with, because we use Oracle 10.
I don't know about the Oracle specific syntax, but with the SQL standard WITH RECURSIVE syntax, you can of course list multiple parameters. Just use the IN predicate e.g. for batches of 5 id IN (:id1, :id2, :id3, :id4, :id5) and then bind the values with setParameter("id1", ...). If you are interested, you can also formulate this with Blaze-Persistence on top of Hibernate by using the JPA model: https://persistence.blazebit.com/documentation/1.6/core/manual/en_US/#recursive-ctes
In your case this would look like the following:
#CTE
#Entity
public class ResultCte {
#Id
Long parentId;
Long rootId;
int level;
}
CriteriaBuilder<Tuple> cb = criteriaBuilderFactory.create(entityManager, Tuple.class);
cb.withRecursive(ResultCte.class)
.from(Entity2.class, "e2")
.where("e2.id").in(idList)
.bind("parentId").select("e2.parent.id")
.bind("rootId").select("e2.id")
.bind("level").select("1")
.unionAll()
.from(Entity2.class, "e2")
.from(ResultCte.class, "cte")
.where("e2.id").eqExpression("cte.parentId")
.bind("parentId").select("e2.parent.id")
.bind("rootId").select("cte.rootId")
.bind("level").select("cte.level + 1")
.end()
.from(Entity1.class "a")
.from(ResultCte.class, "cte")
.where("a.id").eqExpression("cte.parentId")
.select("cte.rootId")
.select("min(a.time)")

JdbcTemplate returns empty list

JdbcTemplate is returning an empty list when executing the "query" method.
public List<Loan> getLoanDAO(Employee employee, String s) {
final String SQL = "SELECT CTLPCODCIA, CTLPCODSUC, CTLPCODTRA, EMPNOMBRE, EMPAPATERN, EMPAMATERN, CTLPCODPRE, "
+ "CTLPTIPPRE, TIPDESPRE, CTLPMONEDA, CTLPESTADO, CTLPMONTOP, CTLPNROCUO, CTLPCUOTA, FLAGTIPOCUOTA, CTLGLOSA, CTLDIASFR, "
+ "CTLDOCADJ, CTLUSUCREA, CTLFECCREA "
+ "FROM HR_CTLPREC_SS INNER JOIN HR_EMPLEADO ON CTLPCODCIA=EMPCODCIA AND CTLPCODSUC=EMPCODSUC AND CTLPCODTRA=EMPCODTRA "
+ "INNER JOIN HR_TIPPRE ON CTLPCODCIA=TIPCODCIA AND CTLPCODSUC=TIPCODSUC AND CTLPTIPPRE=TIPCODPRE "
+ "WHERE TIPFLGEST = '1' AND TIPSELFSERVICE = '1' "
+ "AND CTLPCODCIA = ? AND CTLPCODSUC = ? AND EMPCODTRAJEF = ? AND CTLPESTADO = ? ";
List<Loan> loans = jdbcTemplate.query(SQL, new Object[] {
employee.getCTLPCODCIA(), employee.getCTLPCODSUC(), employee.getCTLPCODTRA(), s }, loanMapper);
return loans;
}
However, when replacing the "?" with the same parameters used in execution and executing in sqldeveloper, it returns 4 rows. I don't know what is wrong since I've been doing de data access code in the same way for all the other entities.
Problem solved
As stated by #Julian:
JdbcTemplate is a proved spring component used by a huge number of applications so in my opinion it must be a bug in your code.
It was not a problem from JdbcTemplate, neither my code. It was an issue from the IDE. I just build my project from scratch using maven console commands and the code worked as intended.
Thanks folks.
JdbcTemplate is a proved spring component used by a huge number of applications so in my opinion it must be a bug in your code.
Not sure what version of Spring you are using but jdbcTemplate.query would expect a Loan Mapper class as one of its arguments. There is no such a mapper present in your code.
I suggest you put a breakpoint just before the query and inspect the employee fields and see if they match the values you are playing in the sqldeveloper.
One other thing that it attracts my attention is the third one where u have EMPCODTRAJEF = ? in the query definition but you use employee.getCTLPCODTRA() as the argument. Obviously I don't know your data model but should it rather be employee.getEMPCODTRAJEF() or the other way around?
If this won't work double check your arguments.
final String SQL = "SELECT CTLPCODCIA, CTLPCODSUC, CTLPCODTRA, EMPNOMBRE, EMPAPATERN, EMPAMATERN, CTLPCODPRE, "
+ "CTLPTIPPRE, TIPDESPRE, CTLPMONEDA, CTLPESTADO, CTLPMONTOP, CTLPNROCUO, CTLPCUOTA, FLAGTIPOCUOTA, CTLGLOSA, CTLDIASFR, "
+ "CTLDOCADJ, CTLUSUCREA, CTLFECCREA "
+ "FROM HR_CTLPREC_SS INNER JOIN HR_EMPLEADO ON CTLPCODCIA=EMPCODCIA AND CTLPCODSUC=EMPCODSUC AND CTLPCODTRA=EMPCODTRA "
+ "INNER JOIN HR_TIPPRE ON CTLPCODCIA=TIPCODCIA AND CTLPCODSUC=TIPCODSUC AND CTLPTIPPRE=TIPCODPRE "
+ "WHERE CTLPCODCIA=? AND CTLPCODSUC = ? AND EMPCODTRAJEF = ? AND CTLPESTADO = ? "
+ "AND TIPFLGEST='1' AND TIPSELFSERVICE='1'";
Add this to application.properties to debug your query.
logging.level.org.springframework.jdbc.core = TRACE

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;

How do I form this complex query in Hibernate?

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

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.

Categories