I need to read a complex model in an ordered way with eclipselink. The order is mandantory because it is a huge database and I want to have an output of a small portion of the database in a jface tableview. Trying to reorder it in the loading/quering thread takes too long and ordering it in the LabelProvider blocks the UI thread too much time, so I thought if Eclipselink could be used that way, that the database will order it, it might give me the performance I need. Unfortunately the object model can not be changed :-(
The model is something like:
#SuppressWarnings("serial")
#Entity
public class Thing implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#PrivateOwned
private List<Property> properties = new ArrayList<Property>();
...
// getter and setter following here
}
public class Property implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
#OneToOne
private Item item;
private String value;
...
// getter and setter following here
}
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private int id;
private String name;
....
// getter and setter following here
}
// Code end
In the table view the y-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Thing m ORDER BY m.name ASC");
using the "name" attribute from the Thing objects as label.
In the table view the x-axis is more or less created with the query
Query q = em.createQuery("SELECT m FROM Item m ORDER BY m.name ASC");
using the "name" attribute from the Item objects as label.
Each cell has the value
Things.getProperties().get[x].getValue()
Unfortunately the list "properties" is not ordered, so the combination of cell value and x-axis column number (x) is not necessarily correct. Therefore I need to order the list "properties" in the same way as I ordered the labeling of the x-axis.
And exactly this is the thing I dont know how it is done. So querying for the Thing objects should return the list "properties" "ORDER BY name ASC" but of the "Item"s objects. My ideas are something like having a query with two JOINs. Joing Things with Property and with Item but somehow I was unable to get it to work yet.
Thank you for your help and your ideas to solve this riddle.
May be the answer to this other question could help you:
Defining the order of a list
I think you may have to use another query to get the list of properties order by item.name for each thing.
Something like:
SELECT p FROM Property p WHERE p.thing = ?1 ORDER BY p.item.name
Try the #OrderBy JPA annotation. Something like
#OrderBy('name ASC')
#OneToMany(cascade=CascadeType.ALL)
private List<Property> properties = new ArrayList<Property>();
Related
I have two model classes: Equity and EquityData. There is a OneToMany relationship from Equity to EquityData. I'm having a hard time getting Hibernate to bind the way I want it to.
#Entity
#Table(name="equities")
public class Equity
{
#Id
#GeneratedValue
#Column(name="Equity_ID")
private Integer id;
private String symbol;
#OneToMany(mappedBy="equity")
private List<EquityData> equityData;
...
}
#Entity
public class EquityData
{
#Id
#GeneratedValue
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name="Equity_ID")
private Equity equity;
#Column(name="quote_time") private Date quoteTime;
#Column(name="quote_type_id") private Integer quoteTypeId;
#Column(name="value") private BigDecimal value;
...
}
Now an Equity can have many EquityQuotes, but there will always be a "most recent" quote (the one with the latest quoteTime). Right now, the way I have Hibernate bind to my entities, it'll retrieve the Equity and all the EquityData's. I only want it to retrieve the latest EquityData for each EquityDataType (i.e. i dont care about yesterday's data, just today's).
In SQL, it would look like this:
select d.equity_id, d.quote_type_id, d.value, max(quote_time)
from equities e, equity_data d
where e.equityID = d.equity_id and e.symbol = :symbol
group by d.equity_id, d.quote_type_id;
I'd appreciate any help! I don't think it matters, but I'm using this in the Stripes Web Framework.
You can set the fetch type as LAZY on your equity data list and then write a named query and fetch only today's equity data. Since you are using Hibernate, you can achieve this using the Criteria API as well.
You can set a criteria to fetch the latest quote from the database like,
Criteria crit = session.createCriteria(Equity.class);
//your criteria goes here....
crit.createCriteria(last_quoted);
List<?> entity = crit.list();
for(Iterator<?> it = equity.iterator();it.hasNext();){
Equity equity = (Equity) it.next();
//print the latest quotes based on the criteria you provided
}
session.close();
}
//catch(Exception e){ //display exeption;}
You can create another field to recover only the EquityData from today, adding an #Where clause to it:
#OneToMany(mappedBy = "equity")
#Where(clause = "quoteTime >= TODAY")
private List<EquityData> equityDataFromToday;
Then, you must use the field equityDataFromToday to access your data, instead of equityData (which will contain all of them)
I want to get a specific row in a OneToMany relation. E.g. getting the cheapest item of an order
Example:
public class Order {
#Id
#Column(name = "ORDER_ID")
private Long id;
???
private Item cheapestItem;
}
public class Item {
#Id
#Column(name = "ITEM_ID")
private Long id;
private Long price;
}
How can I do this?
Try specifying a where clause in the hibernate #Where annotation (Not sure if you can apply it to a non-collection, though)
I want to get (...) the cheapest item of an order
If you really want to get the cheapest Item (without actually persisting it), it should be is doable with a ManyToOne and a JoinColumnOrFormula. Requires Hibernate 3.5+, see issues like HHH-4382 and HHH-5041 for examples.
Retrieving only the price would be much easier and doable with previous versions of Hibernate. See Hibernate Derived Properties - Performance and Portability.
How to generate id for entity using constructor parameters - is that possible ? I need that to make tree structure (category -> subcategories...) passing to constructor parent category id.
Category category = new Category(parentId);
CategoryDAO.add(category);
One idea would be to use "id" "generators" from Hibernate.
See docs here : http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html#mapping-declaration-id
Looking at your use case, the "assigned" or "class" generators seem a fit
This does not directly answer your question but, unless I misunderstood your requirements, what you actually want is to model a self-referencing association, something like this:
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
private String name;
#ManyToOne(optional=true)
private Category parent;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private Set<Categories> subCategories;
// ...
public void addToSubCategories(Category category) {
category.setParent(this);
this.subCategories.add(category);
}
}
If this is not what you want, and if you don't want to use a generated identifier, then simply don't use the #GeneratedValue annotation and assign the identifier manually (in the constructor, using the setter, anything). But I fail to see why you'd need this.
I want to create Many-One Mapping between two tabels, Expense(ID, NAME, CATEGORY) and
Category(ID, NAME).
In my class i have created a field 'Category category' and its setters and getters.
I did them after seeing some stuff from internet. What are all the changes i have to do in my Category.java class. For now, its looks like,
public class Category{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int catId;
private String catName;
public Category() {
}
public int getCatId() {
return this.catId;
}
public void setCatId(int catId) {
this.catId = catId;
}
public String getCatName() {
return this.catName;
}
public void setCatName(String catName) {
this.catName = catName;
}
}
I dont want to do mappings with xml config. I think, annotations is good for a beginner like me.
And my Old! SQL query looks like,
SELECT EXPENSES.EXPNS_ID, EXPENSES.CAT_ID, EXPENSES.NAME, CATEGORY.CAT_NAME FROM EXPENSES INNER JOIN CATEGORY ON EXPENSES.CAT_ID = CATEGORY.CAT_ID WHERE USER_NAME="+currentUserName
How to use inner join in Hibernate?
Any Suggestions!!
Thanks!
Update
Thanks for all answerers,
I tried what you told and it returns a empty list.
To, test i set the 'userName=Tamil' which is in the table.
The query generated by Hibernate is looks like below,
select expens0_.expnsId as expnsId1_, expens0_.catId as catId1_, expens0_.category_catId as category7_1_, expens0_.userName as userName1_ from Expens expens0_ inner join Category category1_ on expens0_.category_catId=category1_.catId where expens0_.userName=?
As a beginner, i have some doubts in JPQL, I want catName from Category[catId, catName] table. And the catId is also available in Expens[expnsId, catId, userName].
By adding the below lines in Expens.java class, how it will give me catName along with the other variables in the Expens table.
#ManyToOne
private Category category
// getters, setters
I cant able to understand it. Without understanding this i cant move further, i have to give more mappings in my project. If clear with this mapping, i can move to the rest with confidence.
The query i used is pascal's version: Query query = hSession.createQuery("SELECT e FROM Expens e JOIN e.category c WHERE e.userName = :userName").setParameter("userName", userName);
For me, the query generated by hibernate is looks like same as my Old SQl query. I cant able to find problem here.
Actually, a big part of the documentation that would be useful in your case is located in the Hibernate Annotations Reference Guides (links provided below). Reading it would be very worth it.
That being said, regarding your specific question, the simplest possible mapping would be:
#Entity
public class Expense {
#Id #GeneratedValue
private Long;
#ManyToOne
private Category category
// getters, setters
...
}
That's all.
If you want to make it bi-directional, you'll have to add a OneToMany on the other side (and don't forget the mappedBy element since the association is bidirectional):
#Entity
public class Category {
#Id #GeneratedValue
private Long id;
#OneToMany(mappedBy="category")
private Set<Expense> expenses = new HashSet<Expense>();
....
}
And a possible JPQL query would be:
SELECT e FROM Expense e JOIN e.category c WHERE e.username = :username
Update: Hibernate and JDBC are different. With Hibernate, you need to think objects and the above HQL query (which was more an example) will actually return a List<Expense>. To get a category name, iterate over the results and navigate through the association. For example:
List<Expense> expenses = ... // some code to retrieve a list by username
for (Expense expense : expenses) {
System.out.println(expense.getCategory().getName());
}
References
2.2. Mapping with JPA (Java Persistence Annotations)
2.2.5.2. Many-to-one
As Bozho suggested,
#ManyToOne(fetch=FetchType.EAGER) // Gonna be eager by default anyway
#JoinColumn(name="CATEGORY_ID")
private Category category;
Plus this in your Category class to make it bidirectional,
#OneToMany(mappedBy="category")
private List<Expense> expense;
You need not do an inner join like that. When you query the expense, the related category will automatically get loaded eagerly, most likely using join.
In your Expense class have:
#ManyToOne
#JoinColumn(name="CATEGORY_ID")
private Category category
As pointed in the comments, if you need to access all expenses in a given category, i.e. have the one-to-many relationship, you can have:
#OneToMany
private List<Expense> expenses;
I, for example, prefer to use as little #OneToMany mappings as possible - you'd have to manager eager/lazy loading, at some point limiting the number of results, etc. For them I tend to use HQL queries that fetch the subset of objects (expenses in your case) that I need.
I'm using java persistence to save a list of entities that are associated to another entity. Here's a quick rundown of where I'm having some problems.
#Entity public class Offer implements Serializable {
#Id private Long offerId;
#OneToMany
#Column List<OfferCategory> offerCategories;
}
#Entity public class OfferCategory implements Serializable {
#Embeddable public static class Id implements Serializable
{
#Column(name="offer_id")
private Long offerId;
#Column(name="category_id")
private Long categoryId;
public Id() {}
public Id(Long offerId, Long categoryId) {
this.offerId = offerId;
this.categoryId = categoryId;
}
public boolean equals(Object o) {
if(o != null && o instanceof Id) {
Id other = (Id) o;
return this.offerId.equals(other.offerId) &&
this.categoryId.equals(other.categoryId);
}
else
return false;
}
public int hashCode() {
return offerId.hashCode() + categoryId.hashCode();
}
}
#EmbeddedId private Id id = new Id();
}
Essentially, due to an architecture I cannot change, I need to save a list of Categories as being assigned to an Offer.
Right now, I'm getting a list of Categories from the user and then putting them into the offerCategories field of Offer. However, this doesn't work for new Offers, because there's no way for me to set the ID of a new item.
I'm new to JPA and Seam, so if someone could give me a nudge in the right direction it would be greatly appreciated.
I have not tried using a composite ID before, but one thing to note is that #Column is only used to change the properties of the database column the field is using. It doesn't stipulate a relation, so you still need something like this:
#OneToMany
List<OfferCategory> offerCategories;
As I looked into tutorial I found this:
You cannot use an IdentifierGenerator to generate composite keys. Instead the application must assign its own identifiers.
So you have to assign the id by yourself. Maybe you can make a DB sequence and fetch its values with native query?
And one remark - if you want to use List mapping (order of Categories in Offer is defined by database), you need an index column to contain the index in list. If the order of categories is not important, Set would be more convenient.
Well, my solution for now is that I persist each one (creating keys for new entries), then stuff them into a list, then stuff them into their container object, then persist that object.