JPA: select random row - java

This is my JPA ENTITY
#Entity
#NamedQueries({
#NamedQuery(name = "Question.randQuestion", query = "SELECT q FROM Question AS q ORDER BY RANDOM")
})
#Table(name = "questions")
public class Question implements Serializable {
.....
}
The problem is:
eclipse gives me an error for this namedQuery. It says:
"The identification variable 'RANDOM' is not defined in the FROM clause"
I've tried also with RAND() instead of RANDOM and also NEWID().
Thanks.

To get a Random Row, first get list of total question and get any one.
public Question getRandomQuestion(EntityManager em) {
Query countQuery = em.createNativeQuery("select count(*) from Question");
long count = (Long)countQuery.getSingleResult();
Random random = new Random();
int number = random.nextInt((int)count);
Query selectQuery = em.createQuery("select q from Question q");
selectQuery.setFirstResult(number);
selectQuery.setMaxResults(1);
return (Question)selectQuery.getSingleResult();
}
Note: You may need to implement a logic to avoid duplicates while calling method more than once.

I had to solve a specific case of this problem where I had to select random records from a set of records matching a certain input criteria. The solution also had to support limit. I describe my solution below starting with assumptions.
Assumptions:
Given the set of criteria as input, it is possible to count number of records that match a selection criteria, as supported by the org.springframework.data.querydsl.QueryDslPredicateExecutor<T>.count(Predicate predicate) method.
Pages are zero indexed.
It is possible to request specific page as supported by the org.springframework.data.domain.PageRequest(int page, int size) method.
Algorithm
Count all records matching the input criteria.
Calculate total number of pages based on the count and specified limit.
Generate a random page index in range [0, total pages).
Request the page with index generated in previous step.
Shuffle elements in the returned page.
Code
Long totalRecords = someRepository.count(somePredicate);
Long totalPages =
(totalRecords % someLimit == 0)
? (totalRecords / someLimit)
: ((totalRecords / someLimit) + 1);
int pageIndex = (int) (Math.random() * totalPages);
PageRequest pageRequest = new PageRequest(pageIndex, someLimit);
Page<T> somePage = someRepository.findAll(somePredicate, pageRequest);
List<T> someList;
if (somePage.getTotalElements() > 0) {
someList = new ArrayList<>(somePage.getContent());
} else {
someList = new ArrayList<>();
}
Collections.shuffle(someList);
The second shuffle is to ensure records within the page are also randomized. The general case of this solution is that there is no criteria and so the count() has to be invoked with no predicate thus getting a count of all rows in the table.

As far as I understand you want to select random question from the table. You'd rather use WHERE clause, with providing some parameter from your code, like:
SELECT q FROM Question AS q WHERE id = :id
Then in the code, which creates your query you must generate random id to be selected:
query.setParam("id", getRandomId());
And to get random id, you may want to query number of rows from DB and use java.util.Random.nextInt(rowsCount) (if all ids are there, of course).
Btw, something similar is described here: http://www.shredzone.de/cilla/page/53/how-to-fetch-a-random-entry-with-hibernate.html

Related

How to get Deleted Row Count in HQL

I´m making a DELETE FROM in HQL:
String queryText = DELETE FROM Books WHERE author = 'author1'
final Query query = getCurrentSession().createQuery(queryText);
query.executeUpdate();
How can I get the number of deleted rows in the query?
The method executeUpdate return the number of entities deleted.
So you will get the number of deleted rows (n) by the following code :
int n = query.executeUpdate();
The preparedStatement.executeUpdate() returns the number of rows affected.
For ex:
int row =preparedStatement.executeUpdate();

How to Select Random 10 records in JPQL?

I have to select random 10 records from the user table,
Below is the SQL Query which gives random 10 records.
SELECT * FROM user_table ORDER BY RANDOM() LIMIT 10
What is the JPQL alternative for this, Do we have Random() support for JPQL? is it a good practice to use RANDOM() ?
I don't know whether there is RANDOM in JPA. As an alternative solution you can use this trick :
Query queryCount = em.createQuery("SELECT COUNT(u) FROM UserTable u");
Long size = (Long) queryCount.getSingleResult();
//I use this way of Java8, you can use the way you see it better to generate random indexes
int[] index = new Random().ints(0, size, 10).toArray();
Query query = em.createQuery("SELECT u FROM UserTable u WHERE INDEX(u) IN :indexs");
^^^^^^^^^^^^^^^^^^^
query.setParameter("indexs", index);
List<UserTable> listUsersRandom = query.getResultList();
Global idea
This solution based on INDEX. The idea is:
First query - find the size of your objects
Generate a list of index between 0 and the size of your list
Second query - select the objects which is IN this generated list

Generating random output from SQLite Database

Hi I currently have a database that has 5 questions and answers stored in it. I want to randomly output three of the questions each time the app runs. I have the below code but my app crashes when it loads
public List<Question> getAllQuestions() {
List<Question> quesList = new ArrayList<Question>();
int nQuestions = 3; //select COUNT(*) from questions
Random random = new Random();
int id = random.nextInt(nQuestions);
String selectQuery = "SELECT id FROM " + TABLE_QUEST;
dbase=this.getReadableDatabase();
Cursor cursor = dbase.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Question quest = new Question();
quest.setID(cursor.getInt(0));
quest.setQUESTION(cursor.getString(1));
quest.setANSWER(cursor.getString(2));
quest.setOPTA(cursor.getString(3));
quest.setOPTB(cursor.getString(4));
quest.setOPTC(cursor.getString(5));
quesList.add(quest);
} while (cursor.moveToNext());{
id = random.nextInt(nQuestions);
}
}
// return quest list
return quesList;
}
Sorry that I write this as an answer, but I don't have permissions to write comments to a question, so I want to tell you that first of all you will always become one of the first 3 added questions as a random question, because for the random id you use as limitations 3 id = random.nextInt(nQuestions); instead of nQuestions (which is 3 for you, you should use questList.size() and do it 3 times for the numbers from 0 to questList.size()
The issue is, you are only selected the id from the table, for all entries.
Your random will also select only questions 0, 1 or 2. Your random should be random.nextInt(5) since you have 5 questions.
Finally, this should be in a loop and then add multiple wheres to your query to get multiple questions.
String query = "SELECT * FROM " + TABLE_QUEST + " WHERE ";
for (int x = 0; x < nQuestions; x++) {
if (x > 0) {
query += " OR ";
}
query += "id=" + random.nextInt(5);
}
This will make your query look like this:
SELECT * FROM TABLE_QUEST WHERE id=4 OR id=2 OR id=3
Finally, change the way your cursor.moveToNext() is. Your code should look something like this:
public List<Question> getAllQuestions() {
List<Question> quesList = new ArrayList<Question>();
int nQuestions = 3; //select COUNT(*) from questions
Random random = new Random();
String query = "SELECT * FROM " + TABLE_QUEST + " WHERE ";
for (int x = 0; x < nQuestions; x++) {
if (x > 0) {
query += " OR ";
}
query += "id=" + random.nextInt(5);
}
dbase=this.getReadableDatabase();
Cursor cursor = dbase.rawQuery(query, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Question quest = new Question();
quest.setID(cursor.getInt(0));
quest.setQUESTION(cursor.getString(1));
quest.setANSWER(cursor.getString(2));
quest.setOPTA(cursor.getString(3));
quest.setOPTB(cursor.getString(4));
quest.setOPTC(cursor.getString(5));
quesList.add(quest);
} while (cursor.moveToNext());
}
// return quest list
return quesList;
}
There are a couple of mistakes in your code.
First, your SQL query is wrong:
String selectQuery = "SELECT id FROM " + TABLE_QUEST;
gives you all the values of the "id" column in the table TABLE_QUEST, not the column with the id you determined with your call to Random.nextInt.
String selectQuery = "SELECT * FROM " + TABLE_QUEST + " WHERE id=" + id;
The "*" stands for "all columns in this table" and the "WHERE" enables you to filter your rows according to the conditions that follow (i.e. id=3).
Note while it's not a problem in this case, passing unescaped values (i.e., the WHERE id=" + id part is very bad form because it might make you vulnerable to SQL injections as soon as you use user input for this).
Second: the rest of the code doesn't make much sense for various reasons. I'll try to list a few:
if your code would work (i.e. with * instead of id in the SELECT, but without the WHERE clause), you'd just add all of the questions in your database to the list, because there's no condition.
if the condition would work, you'd get just one row with a certain id. your do-while loop would run only once.
if you had a loop around the select, then it might work, but it'd probably return the same rows TWICE or even more often.
you'd still get occasional crashes for various reasons, among them
primary keys - the id attribute which is probably auto-increment - is not necessarily in the range 0..n-1. it might be in your case, but that's not exactly typical. auto-incrementing primary keys usually start at 1.
if your question-answers records are CRUD (create, read, update, delete), the ids might have holes, i.e. "1, 2, 6, 8, 12".
i'd rewrite the whole method; there are a couple of ways of doing it. just a few hints:
if there are very few records in the table (e.g. less than a couple hundreds) you can just load all the questions into list A.
create list A
add all records from the database to list A
create a list B
as long as there are fewer elements in list B than you want (i.e. listB.size() < 3) remove a random element from List A and add it to List B.
return listB. // this seems wasteful, but would probably be ok in your case.
let the database do the randomizing:
SELECT * FROM table ORDER BY RANDOM() LIMIT 3;
read all the records into a list
return the list
done! (note: this works with sqlite, but is, as far as i know, not universally accepted by different database systems).
there are countless other ways of achieving your goal. but i'd just use the ORDER BY RANDOM() LIMIT x trick, it's probably the easiest way.

Sum column values in JPQL

I am workin on an evaluation system, and I need to retrieve the sum of the values of each answer given by the person that is evaluating.
This is the query I have now:
SELECT e.evaluado.nombre, e.evaluado.apellido, e.evaluador.nombre, e.evaluador.apellido,
(pd.puntaje*rc.escala.valor/SIZE(c.preguntaCerradaCollection)), e.evaluado.centroCosto.descripcion
FROM Criterio c, PreguntaCerrada p, Redacciones r,
Evaluacion e, RespuestaPreguntaCerrada rc, Ponderacion pd, Escalas es
WHERE c.preguntaCerradaCollection = p
AND r.preguntaCerrada = p
AND r.instrumentos = e.idInstrumento
AND e.respuestaPreguntaCerradaCollection = rc
AND rc.preguntaCerrada = p
AND pd.criterio = c AND pd.instrumentos = e.idInstrumento
AND rc.escala = es
AND e.idPeriodo.idPeriodo = '2013-p1'
ORDER BY e.evaluado.nombre, e.evaluador.nombre, c.descripcion, e.evaluado.centroCosto
So, I have here, the Criteria, Questions, Answers, values of each Criteria, etc. This column is the one that gives back the numeric values of each answer:
(pd.puntaje*rc.escala.valor/SIZE(c.preguntaCerradaCollection))
But I need the total of those values for each employee because I need to know who were the 'n' best employees, or the worst.
If I use the SUM function, the query executor returns this:
Error 130: Cannot perform an aggregate function on an expression containing an aggregate or a subquery
What could be the solution for this?
Thanks in advance

how to sort a column of a table in database with hibernate

I want to find 10 nearset value of a column of a table in a database to my value.
so I want to sort the value of that column, and then find 10 smaller or bigger value than my value.
how can I do this
thanks a lot for your help
HQL supports ORDER BY.
Either you do
Query q = session.createQuery("from Table order by abs(value - :v) asc";
q.setXxx("v", myValue); /* Xxx is Float or Long or Integer or... */
q.setMaxResults(10);
List<Table> l = q.list();
or
Query q1 = session.createQuery("from Table where value >= :v order by value asc";
q1.setXxx("v", myValue); /* Xxx is Float or Long or Integer or... */
q1.setMaxResults(10);
List<Table> l1 = q1.list();
Query q2 = session.createQuery("from Table where value < :v order by value desc";
q2.setXxx("v", myValue); /* Xxx is Float or Long or Integer or... */
q2.setMaxResults(10);
List<Table> l2 = q2.list();
/* now find the 10 nearest elements in Java code */
...
while (...) {
...
}
In the second example you have the inconvenience of two selects which give you 20 rows altogether and then you have to find the 10 nearest in Java code, but if there is a database index on the value column it might b a lot faster. The result will be the same for both examples.

Categories