when is criteria better than HQL or nativeSQL Query? - java

In my Case after getting a certain list I need to to iterate that list to set some other fields of the POJO class.
if (transportHeaderList.get(i) instanceof TransportHeaderIiss){
transHeadIiss=(TransportHeaderIiss)transportHeaderList.get(i);
customerVendor= tOManagementDAO.getVendorCode(transHeadIiss.getCustVendUid());
}
if(customerVendor!=null){
transHeadIiss.setVendorCode(customerVendor.getCustVendCode());
}
The Above code calls getVendorCode method to get custVendorCode value from the database. The code for getVendorCode is as follows
public CustomerVendorIiss getVendorCode(Long custVendUid) {
List list=new ArrayList();
/* Criteria criteria = sessionFactory.getCurrentSession().createCriteria(CustomerVendorIiss.class);
criteria.add(Restrictions.eq("companyCode",user.getDefaultCompany().getCompanyCode()));
if(custVendUid!=null && custVendUid.intValue()>0)
{
criteria.add(Restrictions.eq("custVendUid",custVendUid));
}
list=criteria.list();*/
UsersIiss user= ApplicationContextProvider.getLoggedInUser();
String sqlQuery="select custVendCode as custVendCode from CustomerVendorIiss where companyCode ='"+ user.getDefaultCompany().getCompanyCode() +"' and custVendUid= "+custVendUid;
Query query = sessionFactory.getCurrentSession().createQuery(sqlQuery);
query.setResultTransformer(Transformers.aliasToBean(CustomerVendorIiss.class));
list=query.list();
if(list.size()>0){
return (CustomerVendorIiss)list.get(0);
}else{
return null;
}
}
When I executed above code with criteria, it took a lot time to get the values from table and set it to POJO class and sometimes I would get java.lang.OutOfMemoryError: Java heap space error . I guess that's because I am not de-allocating the criteria object.
when I executed the above code using createQuery() method I did not run into that issue and all that process of getting and setting was faster.
I want to understand what is that I am doing wrong here?
it would be great to know how and when criteria is better or HQL is better ?
Thank you !!

Actually these queries are different. The second one has an additional restriction
companyCode ='"+ user.getDefaultCompany().getCompanyCode() +"'
So try to add the same to the criteria
criteria.add(Restrictions.eq("companyCode",user.getDefaultCompany().getCompanyCode()));
Also it's not god to concate strings this way to get the query. SQL injection is possible. Use parameters instead.
Criteria and HQL is better than SQL in one case - you need DB independent logic to swap DB when necessary without rewriting code.

Related

Handling dynamic queries with Spring

The problem I'm trying to solve here is, filtering the table using dynamic queries supplied by the user.
Entities needed to describe the problem:
Table: run_events
Columns: user_id, distance, time, speed, date, temperature, latitude, longitude
The problem statement is to get the run_events for a user, based on a filterQuery.
Query is of the format,
((date = '2018-06-01') AND ((distance < 20) OR (distance > 10))
And this query can combine multiple fields and multiple AND/OR operations.
One approach to solving this is using hibernate and concatenating the filterQuery with your query.
"select * from run_events where user_id=:userId and "+filterQuery;
This needs you to write the entire implementation and use sessions, i.e.
String q = select * from run_events where user_id=:userId and "+filterQuery;
Query query = getSession().createQuery(q);
query.setParameter("userId", userId);
List<Object[]> result = query.list();
List<RunEvent> runEvents = new ArrayList<>();
for(Object[] obj: result){
RunEvent datum = new RunEvent();
int index = -1;
datum.setId((long) obj[++index]);
datum.setDate((Timestamp) obj[++index]);
datum.setDistance((Long) obj[++index]);
datum.setTime((Long) obj[++index]);
datum.setSpeed((Double) obj[++index]);
datum.setLatitude((Double) obj[++index]);
datum.setLongitude((Double) obj[++index]);
datum.setTemperature((Double) obj[++index]);
runEvents.add(datum);
}
This just doesn't seem very elegant and I want to use the #Query annotation to do this i.e.
#Query(value = "select run_event from RunEvent where user_id = :userId and :query order by date asc")
List<RunEvent> getRunningData(#Param("userId") Long userId,
#Param("query") String query,
);
But this doesn't work because query as a parameter cannot be supplied that way in the query.
Is there a better, elegant approach to getting this done using JPA?
Using Specifications and Predicates seems very complicated for this sort of a query.
To answer the plain question: This is not possible with #Query.
It is also in at least 99% of the cases a bad design decision because constructing SQL queries by string concatenation using strings provided by a user (or any source not under tight control) opens you up for SQL injection attacks.
Instead you should encode the query in some kind of API (Criteria, Querydsl, Query By Example) and use that to create your query. There are plenty of questions and answers about this on SO so I won't repeat them here. See for example Dynamic spring data jpa repository query with arbitrary AND clauses
If you insist on using a SQL or JPQL snippet as input a custom implementation using String concatenation is the way to go.
This opens up attack for SQL injection. Maybe that’s why this feature is not possible.
It is generally a bad idea to construct query by appending random filters at the end and running them.
What if the queryString does something awkward like
Select * from Foo where ID=1234 or true;
thereby returning all the rows and bringing a heavy load on DB possibly ceasing your whole application?
Solution: You could use multiple Criteria for filtering it dynamically in JPA, but you’ll need to parse the queryString yourself and add the necessary criteria.
You can use kolobok and ignore fields with null values.
For example create one method like bellow
findByUserIdAndDistanceaLessThanAndDistancebGreaterThan....(String userid,...)
and call that method only with the filter parameters while other parameters are null

Can't cast the result of query to entity

everyone. I'm new to Hibernate. And I'm making desktop application. I have 2 tables: Worker and Ceh (i.e. Department). Relation between them: many-to-one, i.e. 1 Ceh may contain many workers.
I run hql query with inner join to show info about all workers including name of the department and want to show the results in JTable.
The hql query:
private static String query_All_Workers="select W.fio, W.nomer, W.salary, C.name from Worker W Inner Join W.ceh C;
The method that runs query:
try {
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
Query q = session.createQuery(hql);
List resultList = q.list();
displayResult(resultList);
session.getTransaction().commit();
} catch (HibernateException he) {
he.printStackTrace();
}
The method displayResult(List resultList):
Vector<String> tableHeaders = new Vector<>();
tableHeaders.add("FIO");
tableHeaders.add("Nomer");
tableHeaders.add("Salary");
tableHeaders.add("Ceh");
Vector tableData = new Vector();
for(Object o : resultList) {
Worker worker = (Worker)o;
Vector<Object> oneRow = new Vector<Object>();
oneRow.add(worker.getFio());
oneRow.add(worker.getNomer());
oneRow.add(worker.getSalary());
oneRow.add(worker.getCeh());
tableData.add(oneRow);
}
resultTable.setModel(new DefaultTableModel(tableData, tableHeaders));
And the exception occurs like this:
"java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
workers.entity.Worker"
It happens because the list contains objects which are results of inner join query. So I don't know how I can correctly cast the object to Worker entity in order to use its getters.
You’re getting the “java.lang.ClassCastException” because you are trying to cast object of type java.lang.Object to custom class Worker:
Worker worker = (Worker)o;
There’s nothing wrong with what you’re trying to do just make sure that the result set returns actually Worker, which is not the case. In your example you are returning resultSet of Objects, because you’re writing regular JDBC select SQL statement.
In order to fix that you will need to checkout Hibernate’s Query Language (HQL) syntax and write HQL query instead of regular JDBC one.
Quick tutorial here http://www.tutorialspoint.com/hibernate/hibernate_query_language.htm
I will advise you do to so from now on, because you will gain the following benefits:
When you write select statements (or any other for that matter) with HQL you think and use Java objects, not DB tables (it helps a lot with table foreign key mappings);
HQL returns whole Java object back since Hibernate will do the necessary conversion for you.
In your case you just need to replace the query_All_Workers with this: “from Worker”. Yup, that’s it! Looks weird but as I said before, Hibernate is taking care of all conversions;
Once you’ve done that, and assuming that your Java class is properly mapped to Hibernate entity, resultSet will contain Workers this time, from which you can easily extract Ceh’s name by using Java getter method:
worker.getCeh.getName();
Also using HQL you will not need to make a second select to Ceh table, just to get the name, like you need to do right now.
Hope that helps.

How to return hibernate collection entities without returning root entity

I would like to get elements of a collection (Set<SheetConfig>) for a set of objects (WorkbookConfig) from Hibernate without getting the primary objects(WorkbookConfig).
The underlying tables look like this:
workbook_config -> workbook_config_sheet_join <- sheet_config
If I just run it in my SQL utility, a successful SQL statement looks like this:
SELECT DISTINCT sheet_config_id FROM sheet_config AS sc
LEFT JOIN workbook_config_sheet_join AS wcsj
ON sc.sheet_config_id = wcsj.sheet_config_id
LEFT JOIN workbook_config AS wc
ON wc.workbook_config_id = wcsj.workbook_config_id
WHERE wc.group_id ="1"
ORDER BY sheet_name;
I would like to do this properly without using HQL.
My UNSUCCESSFUL attempt has resulted in this:
#SuppressWarnings("unchecked")
public List<SheetConfig> findAllForUser() {
List<SheetConfig> sheetConfigs = null;
Session session = getSession();
Criteria crit = session.createCriteria(WorkbookConfig.class)
.add(Restrictions.in(GROUP, getGroupsForUser()))
.setFetchMode(SHEET_CONFIGS, FetchMode.JOIN);
sheetConfigs = (List<SheetConfig>) crit.list();
return sheetConfigs;
}
This is still giving me WorkbookConfigs, but what I would like to do in a single pass is get SheetConfigs. I have spent the day on the Internet trying to find a coherent explanation of the Hibernate API, and I haven't been able to find what I would think is a solution to a fairly common requirement. I can always back out and just do most of the work in Java, but it seems like I should be able to do this with the Hibernate API. I appreciate any help, and also, if you can recommend a reference that explains not simply querying collections, but returning them, I would be grateful.
Did you look into the setProjection method on Criteria?
Using the setProjection method lets you select properties or objects from the executed query.
Hibernate Criteria documentation

executing case expressions in jpql

I'm learning JPA and doing some hands one with JPQL. I am having trouble in CASE expressions.
For example, this query,
Query caseQuery = em
.createQuery("SELECT t , CASE WHEN t.salary = 20000 THEN '20k' WHEN t.salary = 40000 THEN '40k' ELSE 'No salary' END FROM Teacher t");
and executing it using
List<Teacher> teachers = (List<Teacher>) caseQuery.getResultList();
but whenever I try to print the results out, I'm getting ClassCastException that Object cannot be converted to Teacher
I've tried using TypedQuery for Teacher but it didn't work. Could you experts please throw some light on executing this CASE statements in JPQL?
The reason is due to multiple select expressions in your query and not due to CASE Expression ,I believe with multiple select expressions the result will be Object[] rather than translated to the Entity. Some references JPA Tutorial ;JPQL. A related query already in stack overflow.
You should modify
List<Teacher> teachers = (List<Teacher>) caseQuery.getResultList();
to
List<Object[]> teachers = (List<Object[]>) caseQuery.getResultList();
where Object[] will be an array of Teacher& String
For TypedQuery also similar change should work.

How to use Hibernate to query a MySQL database with indexes

I have an application developed based on MySQL that is connected through Hibernate. I used DAO utility code to query the database. Now I need optimize my database query by indexes. My question is, how can I query data through Hibernate DAO utility code and make sure indexes are used in MySQL database when queries are executed. Any hints or pointers to existing examples are appreciated!
Update: Just want to make the question more understandable a little bit. Following is the code I used to query the MySQL database through Hibernated DAO utility codes. I'm not directly using HQL here. Any suggestions for a best solution? If needed, I will rewrite the database query code and use HQL directly instead.
public static List<Measurements> getMeasurementsList(String physicalId, String startdate, String enddate) {
List<Measurements> listOfMeasurements = new ArrayList<Measurements>();
Timestamp queryStartDate = toTimestamp(startdate);
Timestamp queryEndDate = toTimestamp(enddate);
MeasurementsDAO measurementsDAO = new MeasurementsDAO();
PhysicalLocationDAO physicalLocationDAO = new PhysicalLocationDAO();
short id = Short.parseShort(physicalId);
List physicalLocationList = physicalLocationDAO.findByProperty("physicalId", id);
Iterator ite = physicalLocationList.iterator();
while(ite.hasNext()) {
PhysicalLocation physicalLocation = (PhysicalLocation)ite.next();
List measurementsList = measurementsDAO.findByProperty("physicalLocation", physicalLocation);
Iterator jte = measurementsList.iterator();
while(jte.hasNext()){
Measurements measurements = (Measurements)jte.next();
if(measurements.getMeasTstime().after(queryStartDate)
&& measurements.getMeasTstime().before(queryEndDate)) {
listOfMeasurements.add(measurements);
}
}
}
return listOfMeasurements;
}
Just like with SQL, you don't need to do anything special. Just execute your queries as usual, and the database will use the indices you've created to optimize them, if possible.
For example, let's say you have a HQL query that searches all the products that have a given name:
select p from Product where p.name = :name
This query will be translated by Hibernate to SQL:
select p.id, p.name, p.price, p.code from product p where p.name = ?
If you don't have any index set on product.name, the database will have to scan the whole table of products to find those that have the given name.
If you have an index set on product.name, the database will determine that, given the query, it's useful to use this index, and will thus know which rows have the given name thanks to the index. It willl thus be able to only read a small subset of the rows to return the queries data.
This is all transparent to you. You just need to know which queries are slow and frequent enough to justify the creation of an index to speed them up.

Categories