EntityManager em = EMF.get().createEntityManager();
EntityTransaction tx = null;
List<Profile> list = null;
Query q = null;
try{
tx = em.getTransaction();
tx.begin();
q = em.createNamedQuery("Profile.getRandomProfile");
q.setParameter("random", Math.random());
q.setMaxResults(8);
list = (List<Profile>) q.getResultList();
if (list != null){
Collections.shuffle(list);
}
tx.commit();
} catch(NoResultException ex){
System.out.println("ERROR CATCHED: " +ex.getMessage());
if(tx != null && tx.isActive())
tx.rollback();
} catch(Exception e){
e.printStackTrace();
}
finally{
em.close();
}
Shuffling the list have error:
java.lang.UnsupportedOperationException: Query result sets are not modifiable
How to overcome the problem?
Copy the results to a secondary list and shuffle it instead of the query result list.
ArrayList copyList = new ArrayList();
Collections.copy(copyList,list);
Collections.shuffle(copyList);
In the line
list = (List<Profile>) q.getResultList();
after it, you should create a new list based on the result one, like this:
List<Profile> anotherList= new ArrayList<Profile>(listaOrdenes);
This way you have a "new" list and this one you can modify it.
maybe like this ?
List<Profile> profiles = null;
List<Profile> results = (List<Profile>) q.getResultList();
if(results != null) {
profiles = new ArrayList<Profile>();
profiles.addAll(results);
Collections.shuffle(profiles);
}
Just like the other two people said, you should copy the results to your own list because they come back in read-only mode. The other possibility is that the List implementation that comes back, doesn't support the operations that shuffle invokes. You can also try to see what the type of list is to verify, but I doubt this is the case.
There are 2 options:
1)Create new List (2)Use ORDER BY clause in query.
Collections.sort(...) will sort the list that you give it. So, it will modify the list. However, the list that you are trying to sort is unmodifiable. When Collections.sort(...) calls one of the methods of the list to add or remove an element, it will throw an exception.
One solution is to create a new, modifiable list from the original list, and then sort that list.
// Create a new ArrayList that contains all elements from the list 'identities'
List<Identity> data = new ArrayList<Identity>(identities);
// Sort the new list
Collections.sort(data);
But, since you're presumably getting the list from the database using a JPA query, it would be a better idea to change the database query to include an "order by" clause, to let the database do the sorting. You wouldn't need to do the sorting in your Java code then.
Related
I use JPA 1.0:
Query query;
query = em.createNamedQuery("getThresholdParameters");
query.setParameter(1, Integer.parseInt(circleId));
List<Object[]> resultList = new ArrayList();
resultList = query.getResultList();
Here I get result as List<Object[]>, thus I have to type convert all the parameters of the row to their respective types which is cumbersome.
In JPA 2.0 there is TypedQuery which return an entity object of type one specifies.
But as I am using JPA 1 I can't use it.
How to get result as Entity object of type I want??
EDIT:
QUERY
#Entity
#Table(name="GMA_THRESHOLD_PARAMETERS")
#NamedQuery(
name = "getThresholdParameters",
query = "select gmaTh.minNumberOc, gmaTh.minDurationOc, gmaTh.maxNumberIc, gmaTh.maxDurationIc, gmaTh.maxNumberCellId,"
+ "gmaTh.distinctBnumberRatio, gmaTh.minPercentDistinctBnumber from GmaThresholdParameter gmaTh "
+ "where gmaTh.id.circleId=?1 AND gmaTh.id.tspId=?2 AND gmaTh.id.flag=?3 "
)
Your query selects many fields. Such a query always returns a list of Object arrays. If you want a list containing instances of your GmaThresholdParameter entity, then the query should be
select gmaTh from GmaThresholdParameter gmaTh
where gmaTh.id.circleId=?1 AND gmaTh.id.tspId=?2 AND gmaTh.id.flag=?3
The code to get the list of entities would then be
List<GmaThresholdParameter> resultList = query.getResultList();
You'll get a type safety warning from the compiler, that you can ignore.
I can't respond to this as a comment so I'll just go ahead and make it an answer.
List<Object[]> resultList = new ArrayList(); // CREATE an empty ArrayList object
resultList = query.getResultList(); // getResultList ALSO returns its own ArrayList object
And since you assign the list that getResultList() returns to the same variable as you used for your own empty ArrayList, your application loses any connection to your own empty ArrayList and Java will collect it as garbage. Essentially you created it for absolutely no purpose.
what JB Nizet posted is enough.
List<GmaThresholdParameter> resultList = query.getResultList();
I have done something similar since I was using JPA 1 at that time:
final Collection<YourType> typedResult = new ArrayList<YourType>
for(final Object result : query.getResultList())
{
typedResult.add((YourType) result);
}
return typedResult;
List<GmaThresholdParamerter> result= query.getResultList();
for( GmaThresholdParamerter res : result)
{
System.out.println("" +res.getMinNumberOc());
System.out.println("" +res.getMinDurationOc());
}
It has been established that when you use Hibernate's Restrictions.in(String property, List list), you have to limit the size of list.
This is because the database server might not be able to handle long queries. Aside from adjusting the configuration of the database server.
Here are the solutions I found:
SOLUTION 1: Split the list into smaller ones and then add the smaller lists separately into several Restrictions.in
public List<Something> findSomething(List<String> subCdList) {
Criteria criteria = getSession().createCriteria(getEntityClass());
//if size of list is greater than 1000, split it into smaller lists. See List<List<String>> cdList
if(subCdList.size() > 1000) {
List<List<String>> cdList = new ArrayList<List<String>>();
List<String> tempList = new ArrayList<String>();
Integer counter = 0;
for(Integer i = 0; i < subCdList.size(); i++) {
tempList.add(subCdList.get(i));
counter++;
if(counter == 1000) {
counter = 0;
cdList.add(tempList);
tempList = new ArrayList<String>();
}
}
if(tempList.size() > 0) {
cdList.add(tempList);
}
Criterion criterion = null;
//Iterate the list of lists, add the restriction for smaller list
for(List<String> cds : cdList) {
if (criterion == null) {
criterion = Restrictions.in("subCd", cds);
} else {
criterion = Restrictions.or(criterion, Restrictions.in("subCd", cds));
}
}
criteria.add(criterion);
} else {
criteria.add(Restrictions.in("subCd", subCdList));
}
return criteria.list();
}
This is an okay solution since you will only have one select statement. However, I think it's a bad idea to have for loops on the DAO layer because we do not want the connection to be open for a long time.
SOLUTION 2: Use DetachedCriteria. Instead of passing the list, query it on the WHERE clause.
public List<Something> findSomething() {
Criteria criteria = getSession().createCriteria(getEntityClass());
DetachedCriteria detached = DetachedCriteria.forClass(DifferentClass.class);
detached.setProjection(Projections.property("cd"));
criteria.add(Property.forName("subCd").in(detached));
return criteria.list();
}
The problem in this solution is on the technical usage of DetachedCriteria. You usually use it when you want to create a query to a another class that is totally not connected (or does not have relationship) on your current class. On the example, Something.class has a property subCd that is a foreign key from DifferentClass. Another, this produces a subquery on the where clause.
When you look at the code:
1. SOLUTION 2 is simpler and concise.
2. But SOLUTION 1 offers a query with only one select.
Please help me decide which one is more efficient.
Thanks.
For Solution 1 : Instead of using for loops, you can try as below
To avoid this use an utility method to build the Criterion Query IN clause if the number of parameter values passed has a size more than 1000.
class HibernateBuildCriteria {
private static final int PARAMETER_LIMIT = 800;
public static Criterion buildInCriterion(String propertyName, List<?> values) {
Criterion criterion = null;
int listSize = values.size();
for (int i = 0; i < listSize; i += PARAMETER_LIMIT) {
List<?> subList;
if (listSize > i + PARAMETER_LIMIT) {
subList = values.subList(i, (i + PARAMETER_LIMIT));
} else {
subList = values.subList(i, listSize);
}
if (criterion != null) {
criterion = Restrictions.or(criterion, Restrictions.in(propertyName, subList));
} else {
criterion = Restrictions.in(propertyName, subList);
}
}
return criterion;
}
}
Using the Method :
criteria.add(HibernateBuildCriteria.buildInCriterion(propertyName, list));
hope this helps.
Solution 1 has one major drawback: you may end up with a lot of different prepared statements which would need to be parsed and for which execution plan would need to be calculated and cached. This process may be much more expensive than the actual execution of the query for which the statement has already been cached by the database. Please see this question for more details.
The way how I solve this is to utilize the algorithm used by Hibernate for batch fetching of lazy loaded associated entities. Basically, I use ArrayHelper.getBatchSizes to get the sublists of ids and then I execute a separate query for each sublist.
Solution 2 is appropriate only if you can project ids in a subquery. But if you can't, then you can't use it. For example, the user of your app edited 20 entities on a screen and now they are saving the changes. You have to read the entities by ids to merge the changes and you cannot express it in a subquery.
However, an alternative approach to solution 2 could be to use temporary tables. For example Hibernate does it sometimes for bulk operations. You can store your ids in the temporary table and then use them in the subquery. I personally consider this to be an unnecessary complication compared to the solution 1 (for this use case of course; Hibernate's reasoning is good for their use case), but it is a valid alternative.
I am attempting to add in a users friend list into my application so that they can quickly find people that they follow on twitter, currently I am able to do this but the results come back based on the most recent people that the user followed but not in alphabetical order which is what I want, is there a way that I can essentially take a list of lists and sort the entire list based one one specific element in the list?
EDIT: sorry totally forgot to add in my code, my apologies
long cursor = -1;
List<User> users = new ArrayList<User>();
try {
PagableResponseList<User> userGroups = null;
do {
userGroups = twitter.getFriendsList(app.getUserId(), cursor);
for (User user : userGroups) {
users.add(user);
}
} while ((cursor = userGroups.getNextCursor()) != 0);
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Use the Collections.sort() method. You will have to create a custom Comparator for this, but there are many available tutorials on this.
What you basically do is you subclass the Comparator to compare two User objects, and return a result based on the name. If the names are Strings, you can just return user1.name.compareTo(user2.name);, since Strings already have a compare-method.
Change your List to a Set
Set<User> users = new TreeSet<User>();
This assumes that your Users are unique.
You may have to wrap the User class with your own class so you can write your own Comparator.
I've some code below as that I use for querying the db and adding data to the list,
Query q= session.createQuery("select tally_receipt_prefix, tally_receipt_no, tally_head, tally_amount from Tally_table where tally_system_date='"+fmtd_date+"' and tally_dbcr_indicator='DB' and tally_mode='Ca' order by tally_head,tally_receipt_prefix,tally_receipt_no");
payincash = new ArrayList();
for(Iterator it=q.iterate(); it.hasNext(); )
{
Object[] row= (Object[]) it.next();
payincash.add((String)row[0]);
payincash.add((String)row[1]);
payincash.add((String)row[2]);
payincash.add((String)row[3]);
}
System.out.println("cash list in dao: "+payincash);
The list returned looks something like [prefix1, no1, head1, amt1, prefix2, no2, head2, amt2,]. I'm trying to make a receipt in jsp on the lines of
head1
prefix1/no1 amt1
prefix2/no2 amt 2
head3
prefix3/no3 amt3
So seemingly I want to group all records by head column in the receipt - jsp file. How do I go about this? Any help completely appreciated. Please excuse my English.
Edit: Here is what I tried,
Query q= session.createQuery("select tally_receipt_prefix, tally_receipt_no, tally_head, tally_amount from Tally_table where tally_system_date='"+fmtd_date+"' and tally_dbcr_indicator='DB' and tally_mode='Ca' order by tally_head,tally_receipt_prefix,tally_receipt_no");
System.out.println("query "+q);
List heads=new ArrayList();
for(Iterator it=q.iterate(); it.hasNext(); )
{
Object[] row= (Object[]) it.next();
payincash1=new LinkedHashMap<String, List>();
heads.add((String)row[2]);
List tails = null;
tails=new ArrayList();
tails.add((String)row[0]);
tails.add((String)row[1]);
tails.add((String)row[3]);
System.out.println("heads in dao from iter 1: "+heads);
System.out.println("tails in dao from iter1 on: "+tails);
if(heads.contains((String)row[2])) // for head in temp list
{
System.out.println("in first if");
if(payincash1.containsKey((String)row[2]))
{
System.out.println("map if repeat: "+payincash1);
payincash1.put((String)row[2],tails);
}
}
else
{
System.out.println("map if not repeat: "+payincash1);
payincash1.put((String)row[2], tails);
}
}
Where is the head column of the receipt stored? at what column?
In my humble opinion it should be stored at the database as well.
Let's say the head information is kept in column "head" at DB, so you should change your query, by adding:
order by head
At the end.
After that, you should iterate over the Result, and maybe keep the information in a data structure that looks like this:
Map<String,List<ReceiptInformation> map = new HashMap<>(); //using JDK7 syntax here
The key in the map should be the value of "head" in each iteration.
The value in the map should be an ArrayList (or any other class implementing List) that holds ReceiptInfo objects.
ReceiptInfoObject holds all the rest of the values per record.
Then, you can iterate on the map.keySet() collection , and for each key, prting the head, and then print the receipts using an internal loop.
Edited per request of the user who asked the question:
In order to add new entries (i.e - new RecepitInformation object to the map) one should perform:
List<RecepitInformation> listForHead = map.get(headValue);
if (listForHead == null) {
listForHead = new ArrayList<ReceiptInformation>();
map.put(headValue,listForHead);
}
listForHead.add(recepitInformation);
As usual, I did not compile this, but i think it should work
With GS Collections you can use list.groupBy(Function) as long as your list is a MutableList. For a JDK list, you could use Iterate.groupBy(Iterable, Function). The result of groupBy will be a Multimap.
I do a query that returns a list of entities. How can I retrieve the entities from a ScrollableResults:
Session s = ....;
Query q = s.createQuery("....") # returns 100000s rows
ScrollableResults sr = q.scroll();
sr.scroll(45999); # just a number
Employee employee = ???
How do I get an employee in the last line of code
try the get(0) method, or get()[0]
Here's a link to API: ScrollableResults
get() returns the entire current row, get(index) returns object at index position without initializing the rest of them. There are also a bunch of convenience getXXX() methods that cast result to given type.
I do a query that returns a list of entities. How can I retrieve the entities from a ScrollableResults... How do I get an employee.
Just to improve the other answers, the ScrollableResults does the entity conversion for you although this isn't immediately clear from the Javadocs.
As #Bozho says, calling sr.get() will return the entity at the current location, but wrapped in an array. In looking at the code for ScrollableResultsImpl the current row's result is set with:
if ( result != null && result.getClass().isArray() ) {
currentRow = (Object[]) result;
} else {
currentRow = new Object[] { result };
}
So ScrollableResults.get() always returns an array of results and if your entity is not an array, it will be at get()[0].
So, with your code you would do something like:
while (sr.next()) {
// get the entity which is the first element in an Object[]
Employee employee = sr.get()[0];
...
}
To retrieve entities the simplest way would be to cast the object to whichever object you want:
E.g:
ScrollableResults sr = q.scroll();
while (sr.next()) {
CustomObject object = (CustomObject) sr.get()[0]; // Now CustomObject will have all the properties mapped
}
This works perfect for all the scenarios.