Google App Engine / Java - inconsistent ORDER BY - java

I am using JPA on top of App Engine (I am quite new to both) and I am currently facing a behaviour I do not understand.
Each time I refresh the page, the order of the fetched items changes.
Here is the code snippet:
Set<Cast> results = new HashSet<Cast>();
EntityManager entityManager = entityManagerFactory.createEntityManager();
Query query = entityManager.createQuery(FIND_ALL_SORTED_BY_DESCENDING_BROADCAST_DATE);
#SuppressWarnings("unchecked")
List<Cast> casts = query.getResultList();
for (Cast cast : casts) {
if (verifySecondaryFields(cast)) {
results.add(synchronizeTechnicalFields(cast));
}
}
entityManager.close();
return Collections.unmodifiableSet(results);
where FIND_ALL_SORTED_BY_DESCENDING_BROADCAST_DATE actually is SELECT cast FROM Cast cast ORDER BY cast.broadcastDate DESC.
entityManagerFactory is an autowired member of my repository class.
The thing is the ORDER BY clause seems to be ignored and the results show up randomly. Can you spot what is wrong?

Sets don't preserve order. Lists do. Try List<Cast> = new ArrayList<Cast>(); and carry on from there.

Related

Eclipselink jpa - sql query result mapping to java object

I am trying to return a typed list from getResultList() but I am having issues with mapping my sql result list to a typed list. It keeps returning a list of generic objects. This is my current code:
EntityManager em = this.emPool.createEntityManager();
TypedQuery<Runtime> query = Runner.getRuntime(em);
List<Runtime> runtimeList = query.getResultList();
Also, in Runner class I have this:
public static TypedQuery<Runtime> getRuntime(EntityManager em) {
return em.createNamedQuery(COUNT_RUNTIMES_SQL_EXPRESSION, Runtime.class);
}
And here is the query:
SELECT u.runner_id as runnerId, COUNT(u.times) FROM RUNNER u"
+ " WHERE u.age = :60 GROUP by u.runner_id
Any tips will be greatly appreciated.
NOTE: the query I am running is a report query -> a simple group by and count
You need to use constructor expression in select clause. For example,
SELECT new com.example.Runtime(u.runner_id as runnerId, COUNT(u.times)) FROM RUNNER u"
+ " WHERE u.age = :60 GROUP by u.runner_id
Define the corresponding constructor in the class Runtime.
If using Criteria API, CriteriaBuilder.construt(...) should be used. Tested with Cmobilecom JPA for the criteria API.
Disclaimer: I am a developer of Cmobilecom JPA (for java and android)

Hibernate find an existing object in db

Simple question here :
If i've got an object with initialized and uninitialized values in it. Is there an easy way to find in my db all the Entities that fit this one with hibernate ? (without listing and checking every variable of the object)
Example :
I got this class :
public class User {
private int id;
private String name;
private String email;
private boolean activ;
}
I would like to be able to do that :
User user1 = new User();
user.setActive() = true;
User user2 = new User();
user.setActive(true);
user.setName("petter")
listUser1 = findAllUser(user1);
listUser2 = findAllUser(user2);
Here listUser1 will contain all the active users and listUser2 will contain all the active user that are named petter.
Thx guys !
Edit/Solution
So my here is my code (i used a class wich is similar at the one of my example).
It work just fine but the problem is that according to Eclipse : "The method createCriteria(Class) from the type SharedSessionContract is deprecated"...
public static List<Personne> findAllPersonne(Personne personne) {
List<Personne> listPersonne;
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("testhibernate0");
EntityManager entityManager = entityManagerFactory.createEntityManager();
Session session = entityManager.unwrap(Session.class);
Example personneExample = Example.create(personne);
Criteria criteria = session.createCriteria(Personne.class).add(personneExample);
listPersonne = criteria.list();
entityManager.close();
return listPersonne;
}
So .. How could i do that in a better way? I've looked into CriteriaQuery but i can't find how to use it with an example.
Yes it exists : the key word for google is "query by exemple" or "qbe".
https://dzone.com/articles/hibernate-query-example-qbe
In general, if an entity instance is already in your Persistence context, you can find it by primary key with EntityManager.find. Otherwise, you can pick up a result from your database by way of JPQL or native querying.
For your particular use case, it sounds like a querying solution would be the best fit; use one of the linked query creation methods from your entity, then use the Query.getResultList() method to pick up a list of objects that match the query criteria.
QueryByExample is also a good and valid solution, as Mr_Thorynque indicates, but as the article he linked mentions, that functionality is specific to certain JPA providers (Hibernate among them) and not JPA provider agnostic.

Trying to optmize a Hibernate Query with read only

I'm trying to optmize a query that is currently taking a little longer than expected. The query returns about 11000 entities, but since they are a bit complicated and have nested entities it's somewhat slow. Since I'm not going to modify the entities, I tried setting the query/session to read-only, but it hasn't helped, it still takes just as long, maybe I'm doing something wrong. Below is a simplified code, sorry it's a little messy:
#Entity
#NamedQueries(value = {#NamedQuery(name = "demand.all", query = "select d from Demand d")})
public class Demand {
private Long ID;
private Division division;
private Client client;
private Product product;
private String code;
...
}
#Transactional(readOnly=true)
public List<Demand> getAll() {
SessionImpl sessionImpl = ((SessionImpl)em.getDelegate());
Session session = sessionImpl.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
try {
sessionImpl.connection().setReadOnly(true);
Query query = session.getNamedQuery("demand.all");
List<Demand> resultList = query.setReadOnly(true).setCacheable(false).setFlushMode(FlushMode.MANUAL).list();
sessionImpl.connection().setReadOnly(false);
tx.commit();
} catch(Exception e) {
resultList = null;
}
session.close();
return resultList;
}
I read that making the query read-only is not enough, so I tried setting the connection and transaction read-only too, but I'm not sure if it's necessary. Anyways, am I doing something wrong? What other way is there to optimize this query?
One way of doing this faster would be fetching the objects in a lazy way or depending on which are necessary and which are not. Like maybe you only need to show 5 columns in a table, instead of every single object in the hierarchy, so you create DTO to get them. If you need more information on one of them.. lets say the user clicks on a row, then you would bring the whole object hierarchy of it..
It may not apply in your case, but its one way of efficiently getting data.

Constructing DTO:s with Querydsl and ConstructorExpression.create()

I have a problem involving querydsl and DTO:s
I have some query object:
QPerson person = QPerson.person;
QExamCode examCode = QExamCode.examCode;
QExamGrade examGrade = QExamGrade.examGrade;
QProgram gradeProgram = examGrade.program;
From them I try to query and list instances of a DTO class (that is not an entity) that is called CompletedCreditsSummary.
CompletedCreditsSummary has a constructor which takes: Long,Long,Float.
JPQLQuery query = new JPAQuery(manager);
query = query.from(person, examCode, examGrade);
query = query.where(person.studies.examGrades.contains(examGrade).and(examGrade.examCode.eq(examCode)).and(examGrade.passed.isTrue()));
I am able to do this (Without group by and with CompletedCreditsSummary requiering all the parameters it needs to be able to create person and program objects, in this case simplified to person.id and program.id)
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,program.id,examCode.credits);
return query.list(completedCreditsSummaryExpression);
This works. But when I want to add this to the query:
query.groupBy(person, examGrade.program);
and create CompletedCreditssummary with examCode.credits.sum() i.e.
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,gradeProgram.id,examCode.credits.sum());
instead of
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,gradeProgram.id,examCode.credits);
I get a: java.lang.IllegalArgumentException: argument type mismatch.
The question here is what the difference between examCode.credits (NumberPath) and examcode.credits.sum() (NumberExpression) and what I could do to solve my problem.
As I am learning querydsl by trial and error there is probably something fundamental that I have overlooked. Would really appreciate any help!
Regards Rasmus
EDIT: Preferably I would something like this to work (with group by and CompletedCreditsSummary constructor taking Person,Program,Float.):
ConstructorExpression<Person> personExpression = ConstructorExpression.create(Person.class,person.id);
ConstructorExpression<Program> programExpression = ConstructorExpression.create(Program.class,gradeProgram.id);
ConstructorExpression<CompletedCreditsSummary> completedCreditsSummaryExpression = ConstructorExpression.create(CompletedCreditsSummary.class,personExpression,programExpression,examCode.credits.sum());
EDIT: Got it to work by having the CompletedCreditsSummary Constructor accepting: Long,Long,Number. That is I changed Float to Number. This is not an ideal solution but at least it works.
Try something like this
JPAQuery query = new JPAQuery(manager);
query.from(person, examCode, examGrade)
.where(
person.studies.examGrades.contains(examGrade),
examGrade.examCode.eq(examCode),
examGrade.passed.isTrue())
.groupBy(person, examGrade.program)
.list(ConstructorExpression.create(
CompletedCreditsSummary.class,
person, examGrade.program, examCode.credits.sum()));
You need to make sure that the argument for ConstructorExpression after the class are compatible with the arguments to the constructor you want to invoke. Replacing entities with ids caused your problems.

How can I get Toplink generated query by using getTranslatedSQLString?

So what I'm doing is creating a subquery that gets a list of ID values, then the main query gets all the necessary values and adds ordering.
What I have is this:
ReportQuery querySub = new ReportQuery(Predmet.class, generatedExpression);
querySub.addAttribute("m_id");
DatabaseRow row = new DatabaseRow();
querySub.prepareCall(getSession(), row);
// This part is the problem
String sql = querySub.getTranslatedSQLString(getSession(), row);
The problem with this code is that it doesn't return TranslatedSQLString, it returns the same result as querySub.getSQLString(). Now in all the example code I saw, they either instanced row as a new object or didn't bother to write from where they got the reference but whatever the case, this doesn't work (TopLink version issue?). I'm guessing I need to populate the DatabaseRow object myself, but I can't find any example online.
I didn't manage to find any way to do this by using getTranslatedSQLString. I suppose the DatabaseRow needs to be populated, but I have yet to find the proper way. For now, I'm using "bruteforce" substitution, I "memorize" all of my parameters and do a find/replace on each "?" sign in the query.
you need get session like this:
JpaHelper.getDatabaseSession(getEntityManager().getEntityManagerFactory());
The Database that you need is:
TypedQuery<T> typedQuery = getEntityManager().createQuery(cq);
DatabaseQuery databaseQuery = typedQuery.unwrap(JpaQuery.class).getDatabaseQuery();
So the final example is:
Session session = JpaHelper.getDatabaseSession(getEntityManager().getEntityManagerFactory());
DatabaseQuery databaseQuery = null;
if(typedQuery instanceof EJBQueryImpl)
databaseQuery = ((EJBQueryImpl<T>)typedQuery).getDatabaseQuery();
else
databaseQuery = typedQuery.unwrap(JpaQuery.class).getDatabaseQuery();
sql = databaseQuery.getTranslatedSQLString(session, databaseQuery.getTranslationRow());
That work for me.

Categories