I have a comments tables from which I want to select and group comments by its foreign key profile_id.
I'm using jpa 2/hibernate and the tables have a one-to-many relationship from profile to comment.
Currently, the sql query retuns a list of comments and through the many-to-one relationship I get the profile for each comment.
I'd appreciate help in answering the following regarding post and pre query execution sorting:
How can I sort the returned list of comments, i.e. List<Comment>, by the profile_id (foreign key) of the comments table or the id (primary key) of the profile table?
How can/should I construct my sql query to sort the comments by profile?
Which of the two - pre or post sort - is a better practice?
Let the database do the sort for you : it's very efficient at doing it.
The HQL for this is very simple :
select c from Comment c order by c.profile.id
"sorting" (post sort) will be done by java in memory while "order by" (pre sort) will be done by the rdbms. Letting the rdbms do it with SQL is more efficient in most cases. The java sort will be controlled by the compareTo method of whatever object you are sorting
The SQL is as simple as adding an order by clause to your HQL like this:
from Comments order by profile_ID
or, using annotations, you can specify in the mapping how to order by:
#org.hibernate.annotations.OrderBy(
clause = "profile_id asc"
)
to have your results sorted in memory by java, you could map the collection of comments as a SortedMap or SortedSet
#org.hibernate.annotations.Sort(
type=org.hibernate.annotations.SortType.NATURAL
)
private SortedSet<Comment> comments = new TreeSet<Comment>();
The "NATURAL" in this case would expect Comment class to make sure the comments were sorted by profile_id by using the compareTo method.
you could also use SortType.CoMPARATOR for even more control if your Comments class implemented comparator.
referenced: Java Persistence with Hibernate
and http://henko.net/imperfection/sorted-collections-in-hibernate/
Related
I am new to Java Persistence etc
I have a one to many relation defined and it works, but I cannot define the where clause in the many entity.
For instance, my search is returning a list of orders and a collection of order items per order.
But, how do I apply a where clause in the LineItem entity class e.g.
The native SQL will look like this (roughly)
SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders, LineItems WHERE Orders.OrderID = LineItems.OrderID
AND LineItems.Description IN ('XXX1', 'XXXXX2','XXXX3')`
AND LineItems.Quantity = 5
I dont know how to define the :
AND LineItems.Description IN ('XXX1', 'XXXXX2','XXXX3')
AND LineItems.Quantity = 5
in the LineItems class.
Please Help.
This is more of a workaround than an answer to your question, but it may be the only way to do it: Create a NamedQuery that restricts the results the way you want and use the NamedQuery instead. Here's a tutorial on how to do that.
The answers to this question imply that my suggestion is the only way to accomplish what you need. Those answers are Hibernate specific.
OK, this looks mostly like (another) bug/unimplemented functionality in Hibernate.
Is it possible to map a Set association in such a way that the "order by" clause in HQL is respected?
For example, say you have 2 entities: PageEntity and QuestionEntity. A page has a Set.
How do I make the following HQL work:
from
PageEntity p
left outer join fetch p.questionEntities q
order by
q.orderIndex
What I don't want:
to use sort in Java (we have "order by" in SQL so we don't have to do this!). SortedSet, Comparators etc. are out of the question
to map it as a List with a <list-index>. This will add the "order by" to all SQL queries, and I don't want this
to use a <set order-by="orderIndex"> because again, this will be applied to all queries
Debugging, I see that the implementation of Set that is being used is org.hibernate.collection.PersistentSet which wraps a Set. The implementation of the wrapped Set is HashSet. I would expect Hibernate to be smart enough to use a LinkedHashSet instead, so that it can honor my "order by" HQL clause.
I'm pretty sure Hibernate can't do this for you, and would recommend using a List instead. To remove duplicates introduced by the join, you can use the distinct keyword. The resulting List will be just as good as any Set.
If you still need them in an ordered Set (maybe there's a 3rd party API involved) you could create your own LinkedHashSet and move all objects there:
List<PageEntity> list = runQuery(...);
return new LinkedHashSet<PageEntity>(list);
It seems you want to get results sorted by q.orderIndex but
do you want the resulting list of PageEntity to be sorted?
or (as I think) do you want the set of QuestionEntitys for each PageEntity to be sorted?
If you want the latter: Sets in Java do not preserve order.
After your edit: no Hibernate does not have to be smart: if you define your collection to be a Set it will be treated as a set. There are many applications where preserving the order costs resources and is not needed.
order by applies to the results of the query. Not to the entities in the collection of questions. I don't think it's a bug: what would it do if one of the returned page entity is already loaded in the session, using another query with another order by clause?
If you want an ordered list of questions, select the questions:
select q from QuestionEntity q inner join fetch q.page p order by q.orderIndex
or
select p, q from PageEntity p
left outer join fetch p.questionEntities q
order by q.orderIndex
should do the trick.
Related to my last question
I have worked with that answer, but now I require to do with a bit modification using hibernate.
I need to now add a string to TestData class say String name
Then I want to fetch Collection of TestData from db using hibernate with the distinct name and sorting as I did in the CollectionSort class.
Can anyone help me how to do this using hibernate?
Thanks.
You can reuse that comparator in Hibernate, using #Sort annotation
Reference
I have a table called Gift, which has a one-to-many relationship to a table called ClickThrough - which indicates how many times that particular Gift has been clicked. I need to query for all of the Gift objects, ordered by ClickThrough count. I do not need the ClickThrough count returned, as I don't need anything done with it, I just want to use it for purposes of ordering.
I need the query to return a List of Gift objects directly, just ordered by ClickThrough count. How do I do this using the Criteria API? I can find a lot of documentation out there on similar information to this, but nothing quite like what I need.
Note for anyone else that comes through here looking to order by a property/column:
When using the approaches mentioned here, no results were found. The fix was to use criteria.addOrder(Order.asc(property)); instead. Notice the difference is to use addOrder, rather than add;
I've had this issue several times after running here for a quick reference.
If you want to return a list of entities or properties in combination with a count you will need a group by. In criteria this is done through ProjectionList and Projections. e.g.
final ProjectionList props = Projections.projectionList();
props.add(Projections.groupProperty("giftID"));
props.add(Projections.groupProperty("giftName"));
props.add(Projections.count("giftID"), "count"); //create an alias for the count
crit.setProjection(props);
crit.add(Order.desc("count")); //use the count alias to order by
However, since you're using ProjectionList you will also need to use AliasToBeanConstructorResultTransformer.
You have a one-to-many relationship from Gift to ClickThrough so I assume each ClickThrough is a record with some datetime or other information associated with it. In this case, I would add a "count" field to your mapping file and attach the ordering to the criterion:
criterion.add(Order.asc(count)))
The mapping property looks something like:
<property name="count" type="integer" formula="(select count(*) from Gift g inner join ClickThrough ct on ct.gift_id=g.id where g.id=ID)"/>
If you can't or don't want to change the mapping file, you could use Collections.sort() with a Comparator though it seems less efficient having to return so much data from the DB.
In legacy database tables we have numbered columns like C1, C2, C3, C100 or M1, M2, M3, M100.
This columns represent BLOB data.
It is not possible to change anything it this database.
By using JPA Embeddable we map all of the columns to single fields. And then during embedding we override names by using 100 override annotations.
Recently we have switched to Hibernate and I've found things like UserCollectionType and CompositeUserType. But I hadn't found any use cases that are close to mine.
Is it possible to implement some user type by using Hibernate to be able to map a bundle of columns to a collection without additional querying?
Edit:
As you probably noticed the names of columns can differ from table to table. I want to create one type like "LegacyArray" with no need to specify all of the #Columns each time I use this type.
But instead I'd use
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "A"),
#Parameter(name = "size", value = "128")
})
List<Integer> legacyA;
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "B"),
#Parameter(name = "size", value = "64")
})
List<Integer> legacyB;
I can think of a couple of ways that I would do this.
1. Create views for the collection information that simulates a normalized table structure, and map it to Hibernate as a collection:
Assuming your existing table is called primaryentity, I would create a view that's similar to the following:
-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)
Now from Hibernate's perspective, childentity is just a normalized table that has a foreign key to primarykey. Mapping this should be pretty straight forward, and is covered here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/collections.html
The benefits of this approach:
From Hibernate's point of view, the tables are normalized, it's a fairly simple mapping
No updates to your existing tables
The drawbacks:
Data is read-only, I don't think your view can be defined in an updatable manner (I could be wrong)
Requires change to the database, you may need to create lots of views
Alternately, if your DBA won't even let you add a view to the database, or if you need to perform updates:
2. Use Hibernate's dynamic model mapping facility to map your C1, C2, C3 properties to a Map, and have some code you your DAO layer do the appropriate conversation between the Map and the Collection property:
I have never done this myself, but I believe Hibernate does allow you to map tables to HashMaps. I'm not sure how dynamically Hibernate allows you to do this (i.e., Can you get away with simply specifying the table name, and having Hibernate automatically map all the columns?), but it's another way I can think of doing this.
If going with this approach though, be sure to use the data access object pattern, and ensure that the internal implementation (use of HashMaps) is hidden from the client code. Also be sure to check before writing to the database that the size of your collection does not exceed the number of available columns.
The benefits of this approach:
No change to the database at all
Data is updatable
O/R Mapping is relatively simple
The drawbacks:
Lots of plumbing in the DAO layer to map the appropriate types
Uses experimental Hibernate features that may change in the future
Personally, I think that design sounds like it breaks first normal form for relational databases. What happens if you need C101 or M101? Change your schema again? I think it's very intrusive.
If you add Hibernate to the mix it's even worse. Adding C101 or M101 means having to alter your Java objects, your Hibernate mappings, everything.
If you have 1:m relationships with C and M tables, you'd be able handle the cases I just cited by adding additional rows. Your Java objects contain Collection<C> or Collection<M>. Your Hibernate mappings are one-to-many that don't change.
Maybe the reason that you don't see any Hibernate examples to match your case because it's a design that's not recommended.
If you must, maybe you should look at Hibernate Component Mapping.
UPDATE: The fact that this is legacy is duly noted. My point in bringing up first normal form is as much for others who might find this question in the future as it is for the person who posted the question. I would not want to answer the question in such a way that it silently asserted this design as "good".
Pointing out Hibernate component mapping is pertinent because knowing the name of what you're looking for can be the key when you're searching. Hibernate allows an object model to be finer grained than the relational model it maps. You are free to model a denormalized schema (e.g., Name and Address objects as part of a larger Person object). That's just the name they give such a technique. It might help find other examples as well.
Sorry if I'm misunderstanding your problem here, I don't know much about Hibernate. But couldn't you just concatenate during selection from database to get something like what you want?
Like:
SELECT whatever
, C1||C2||C3||C4||...||C100 AS CDATA
, M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...
(Of course, the concatenation operator differs between RDBMSs.)
[EDIT] I suggest to use a CompositeUserType. Here is an example. There is also a good example on page 228f in the book "Java Persistence With Hibernate".
That allows you to handle the many columns as a single object in Java.
The mapping looks like this:
#org.hibernate.annotations.Columns(columns = {
#Column(name="C1"),
#Column(name="C2"),
#Column(name="C3"),
...
})
private List<Integer> c;
Hibernate will load all columns at once during the normal query.
In your case, you must copy the int values from the list into a fixed number of columns in nullSafeSet. Pseudocode:
for (int i=1; i<numColumns; i++)
if (i < list.size())
resultSet.setInt(index+i, list.get(i));
else
resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());
In nullSafeGet you must create a list and stop adding elements when a column is NULL. For additional safety, I suggest to create your own list implementation which doesn't allow to grow beyond the number of columns (inherit from ArrayList and override ensureCapacity()).
[EDIT2] If you don't want to type all the #Column annotations, use a code generator for them. That can be as simple as script which you give a name and a number and it prints #Column(...) to System.out. After the script ran, just cut&paste the data into the source.
The only other solution would be to access the internal Hibernate API to build that information at runtime but that API is internal, so a lot of stuff is private. You can use Java reflection and setAccessible(true) but that code probably won't survive the next update of Hibernate.
You can use UserTypes to map a given number of columns to any type you wish. This could be a collection if (for example) for collections are always bounded in size by a known number of items.
It's been a while (> 3 years) since I used Hibernate so I'm pretty rusty but I recall it being very easy to do; your BespokeUserType class gets passed the ResultSet to hydrate your object from it.
I too have never used Hibernate.
I suggest writing a small program in an interpreted language (such as Python) in which you can execute a string as if it were a command. You could construct a statement which takes the tedious work out of doing what you want to do manually.