Hibernate criteria and comparator - java

I'm using hibernate template and it's
findByCriteria(criteria, offset, maxResults) method
to get results paginated.
To get results ordered before calling findByCriteria
I set in criteria OrderBy property. The problem is
a want to order this column not just as simple string, but take into
account that it may contain numbers and sort it in alphanumeric way:
entity 2
entity 19
entity 22
not like this:
entity 19
entity 2
entity 22
To do this I'm using comparator it works ok with Collections.sort. But I need a way to bind it to criteria and get already ordered after firing findByCriteria method. Is there is a way to accomplish this?
Thanks!

I think what you need is to sort in your criteria query the elements itself by some "criteria". Basically you will be using a SQL query (build internally by Hibernate when you specify your order criteria) to sort the elements themselves, instead of sorting them after being retrieved> You will end up having a better performant, cleaner and straightforward solution.
Remember you are fetching a chunk of data every time and you can't tell how to sort the whole set, but that small one you already have plus the previous ones...what you might get (in the next ones) is foreseen.

Related

Better to query once, then organize objects based on returned column value, or query twice with different conditions?

I have a table which I need to query, then organize the returned objects into two different lists based on a column value. I can either query the table once, retrieving the column by which I would differentiate the objects and arrange them by looping through the result set, or I can query twice with two different conditions and avoid the sorting process. Which method is generally better practice?
MY_TABLE
NAME AGE TYPE
John 25 A
Sarah 30 B
Rick 22 A
Susan 43 B
Either SELECT * FROM MY_TABLE, then sort in code based on returned types, or
SELECT NAME, AGE FROM MY_TABLE WHERE TYPE = 'A' followed by
SELECT NAME, AGE FROM MY_TABLE WHERE TYPE = 'B'
Logically, a DB query from a Java code will be more expensive than a loop within the code because querying the DB involves several steps such as connecting to DB, creating the SQL query, firing the query and getting the results back.
Besides, something can go wrong between firing the first and second query.
With an optimized single query and looping with the code, you can save a lot of time than firing two queries.
In your case, you can sort in the query itself if it helps:
SELECT * FROM MY_TABLE ORDER BY TYPE
In future if there are more types added to your table, you need not fire an additional query to retrieve it.
It is heavily dependant on the context. If each list is really huge, I would let the database to the hard part of the job with 2 queries. At the opposite, in a web application using a farm of application servers and a central database I would use one single query.
For the general use case, IMHO, I will save database resource because it is a current point of congestion and use only only query.
The only objective argument I can find is that the splitting of the list occurs in memory with a hyper simple algorithm and in a single JVM, where each query requires a bit of initialization and may involve disk access or loading of index pages.
In general, one query performs better.
Also, with issuing two queries you can potentially get inconsistent results (which may be fixed with higher transaction isolation level though ).
In any case I believe you still need to iterate through resultset (either directly or by using framework's methods that return collections).
From the database point of view, you optimally have exactly one statement that fetches exactly everything you need and nothing else. Therefore, your first option is better. But don't generalize that answer in way that makes you query more data than needed. It's a common mistake for beginners to select all rows from a table (no where clause) and do the filtering in code instead of letting the database do its job.
It also depends on your dataset volume, for instance if you have a large data set, doing a select * without any condition might take some time, but if you have an index on your 'TYPE' column, then adding a where clause will reduce the time taken to execute the query. If you are dealing with a small data set, then doing a select * followed with your logic in the java code is a better approach
There are four main bottlenecks involved in querying a database.
The query itself - how long the query takes to execute on the server depends on indexes, table sizes etc.
The data volume of the results - there could be hundreds of columns or huge fields and all this data must be serialised and transported across the network to your client.
The processing of the data - java must walk the query results gathering the data it wants.
Maintaining the query - it takes manpower to maintain queries, simple ones cost little but complex ones can be a nightmare.
By careful consideration it should be possible to work out a balance between all four of these factors - it is unlikely that you will get the right answer without doing so.
You can query by two conditions:
SELECT * FROM MY_TABLE WHERE TYPE = 'A' OR TYPE = 'B'
This will do both for you at once, and if you want them sorted, you could do the same, but just add an order by keyword:
SELECT * FROM MY_TABLE WHERE TYPE = 'A' OR TYPE = 'B' ORDER BY TYPE ASC
This will sort the results by type, in ascending order.
EDIT:
I didn't notice that originally you wanted two different lists. In that case, you could just do this query, and then find the index where the type changes from 'A' to 'B' and copy the data into two arrays.

Order by a temporary column computed using case when

I have 2 entities Book and BookProperty in a one-to-many relationship. I need to retrieve books sorted by upload_date, english language books coming first using JPA Criteria with pagination and stuff (JPQL/native sql is not an option).
This is what the native query for this operation would look like (postgres):
select distinct
b.book_id,
b.name, b.author, ... ,
b.upload_date,
case when p.property_name='language' and lower(p.property_value)='english'
then 0 else 1 end as book_language,
from books b
left outer join book_properties p
on b.book_id=p.book_id
order by book_language asc, b.upload_date desc;
The problem is that I can't get the 'case' part to be selected via criteria API and sort by it. I know that it is possible via a multiselect and a tuple but I would like to avoid that, because really I do not need this column in the application. I would like to just retrieve sorted Book objects and not tuples of (Book, Integer).
I tried to move the case part into the order by and managed to compute the query via JPA Criteria, but in that case setDistinct(true) resulted in an error, because all columns in order by must be part of distinct. So moving the case part to order doesn't look like an option.
Please help me implement this query using JPA Criteria, preferably without using tuples or wrapper objects, but that will do as well if there are no other options.

How to rearrange rows of resultset data in java?

List userProcessedCountCol = new ArrayList();
while (iResultSet1.next()) {
afRealTimeIssuance afRealTimeIssuance = new afRealTimeIssuance();
Integer i = 0;
afRealTimeIssuance.setSub_channel(iResultSet1.getString(++i));
afRealTimeIssuance.setAgent_names(iResultSet1.getString(++i));
afRealTimeIssuance.setFtd(iResultSet1.getDouble(++i));
afRealTimeIssuance.setMtd(iResultSet1.getDouble(++i));
afRealTimeIssuance.setQtd(iResultSet1.getDouble(++i));
userProcessedCountCol.add(afRealTimeIssuance);
}
where afRealTimeIssuance is ActionForm
Using the above snippet I get something like below output
1 A 100
2 B 200
3 C 300
4 D 400
But I want to rearrange the output as
2 B 200
4 D 400
3 C 300
1 A 100
In short I want to rearrange the rows as I want.How to arrange the resultset data based on one particular value.Please guide
you can act as at two levels here:
Database level
Java level
At the database level the only way to manipulate the order of results to be returned is using ''ORDER BY ASC/DESC'' in your sql query. Note, that you can't rely on any other way to get the ordered results from the database.
At the java level you can store your objects like this:
- use a sortable collection. Make your action form comparable or implement a comparator that
allows to sort elements as you wish.
Then your can use This method to get the ordered collection by your own criteria.
You can consider also using TreeSet instead of ArrayList
This data structure will allow you to just add the data and given the comparator that you've defined in advance it will be always sorted. The addition has a logarithmic complexity though, so its up to you to decide.
Hope this helps
The ResultSet cannot be rearranged manually (only with sql) . What you can rearrange is your data structure that you hold your Objects
You can use an ArrayList of your row Objects and insert each row in the position you would like.
Lets say in your example, in the while loop:
userProcessedCountCol.add(index, element);
There are two ways of doing this. One you can modify the query to use ORDER BY clause to arrange the results. Second you can implement the Comparator interface and define your comparator classes and use Collection.sort(List list,Comparator c) to order the data.
Either use an ORDER BY clause in your SQL query, or Collections.sort() the List using a Comparator<afRealTimeInssuance>. The former is easier and places the load on the database, the latter more versatile as you can sort based on external information.
On a side note, you should name your classes using the Java conventions: AFRealTimeInssuance instead of afRealTimeInssuance.

Can lucene only sort and search for nothing?

I want to list the lastest 10 rows order by id DESC
Sort sort = new Sort(new SortField[]{new SortField("id",SortField.INT,true)});
TopDocs topDocs=indexSearch.search(null,null,10,sort);//no need Query,only sort
...
I got a 500 exception because the Query parameter is null
How can I implement it in a best way?
btw:id field is a NumericField,write using:
new NumericField("id",Integer.MAX_VALUE,Field.Store.YES,true)
You should use the MatchAllDocsQuery for that.
Lucene Query is a peculiar object that isn't only the specification of the query semantics, but also the implementation of the most efficient execution strategy for each particular query type. That's why there must be a special Query even for this "no-op"
BTW: if you want to search the latest X rows it's better you add a new date field with the time this doc was added to repository and not to rely on the counter (id on your case).
try to think what happen if you update an existed doc or you reach Integer.MAX_VALUE

insert data into a lot of columns in java JDBC

I have a table with 50 columns and I want to insert all items in a HashMap variable into it (HashMap keys and table column names are the same).
How can I do that without writing 50 lines of code?
Get the key set for the HashMap. Iterate that key set to build a String containing your insert statement. Use the resulting String to create a PreparedStatement. Then iterate that key set again to set parameters by name using the Objects you retrieve from the HashMap.
You might have to write a few extra lines of special-case code if any of your values are of a Class that the JDBC driver isn't sure how to map.
I'd suggest you bite the dust and simply write a method that will do the dirty work for you containing 50 lines of parameter setting code. This isn't so bad, and you only have to write it once. I hope you aren't that lazy ;-)
And by the way, isn't 50 columns in a table a bit much? Perhaps a normalization process could help and lower complexity of your database and the code that will manipulate it.
Another way to go is to use an ORM like Hibernate, or a more lightweight approach like Spring JDBC template.
Call map.keySet() to get the name of all columns.
Create an INSERT statement by iterating the key set.
The column is from an item (a key) in the key set.
The data is from map.get(key).

Categories