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)
Related
I am using Spring Data JPA with Hibernate.
Lets say I have the following entity defined:
#Entity
#Table(name = "foods")
public class Food {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "food_id")
private Long foodId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "food_type_id")
#NotNull
private FoodType foodType;
...
}
#Entity
#Table(name = "food_types")
public class FoodType {
public static final Integer PERISHABLE;
public static final Integer NON_PERISHABLE;
#Id
#Column(name = "food_type")
private Integer foodTypeId;
private String name;
...
}
Every time when I want to create a Food entity and save it to the database, currently code looks like this:
Food food = new Food();
FoodType foodType = foodTypeRepository.findById(FoodType.PERISHABLE); // Call to DB to get Entity
food.setFoodType(foodType);
....
foodRepository.save(food);
If we consider FoodType to be constant in the DB. Can I use it like this:
Food food = new Food();
FoodType foodType = new FoodType();
foodType.setFoodTypeId(FoodType.PERISHABLE); // No Call to DB
food.setFoodType(foodType);
....
foodRepository.save(food);
I have tested it and yes I can use it that way, hibernate will save the Food entity, but are there any downsides, pitfalls, etc... I am not seeing.
PS. This is just a simple example illustrating the idea, it is part of old legacy project which I cannot modify to remove constant from DB, and use an enum instead.
To avoid extra call to DB you should use:
FoodType foodType = foodTypeRepository.getOne(FoodType.PERISHABLE);
under the hood it calls EntityManager.getReference that obtain a reference to an entity without having to load its data as opposed to the foodTypeRepository.findById that lead to call EntityManager.find that obtain an entity along with its data.
See also this section of the hibernate documentation.
P.S. You can not use:
Food food = new Food();
FoodType foodType = new FoodType();
foodType.setFoodTypeId(FoodType.PERISHABLE);
as in this case hibernate consider foodType as a transient entity (not associated with a persistence context) and will try to save it as a new record if you have a proper cascading on your #ManyToOne association.
P.S.S. As it's mentioned in the documentation the method JpaRepository#getOne(ID) is deprecated and you should use JpaRepository#getById(ID) instead.
You do not need to fetch the entity associated with FoodType.PERISHABLE in order to set the relation on a Food entity to it and I'm not aware of any side effects or pitfalls of using FoodType.PERISHABLE directly as long it is a valid FoodType id.
As others mentioned, you could also use JpaRepository#getById(ID id) and that's probably the more canonical way of addressing this problem:
T getById(ID id) Returns a reference to the entity with the given
identifier. Depending on how the JPA persistence provider is
implemented this is very likely to always return an instance and throw
an EntityNotFoundException on first access. Some of them will reject
invalid identifiers immediately.
This question already has answers here:
Difference between FetchType LAZY and EAGER in Java Persistence API?
(18 answers)
Closed 7 years ago.
Hi am new to java server side to create JSON API, am using ManytoMany mapping in hibernate to join the two tables.I have two classes one is Product.class and Offers.class.
Product.class
#Entity
#Table(name = "products")
public class Product {
#Column(name = "merchant_code")
private String merchant_code;
#Column(name = "branch_code")
private String branch_code;
#Column(name = "product_category_code")
private String product_category_code;
#Id #GeneratedValue
#Column(name = "product_code")
private String product_code;
#Column(name = "product_short_desc")
private String product_short_desc;
#Column(name = "product_long_desc")
private String product_long_desc;
#Column(name = "image")
private String image;
#Column(name = "price")
private String price;
#Column(name = "Active_Inactive")
private String Active_Inactive;
#ManyToMany(mappedBy = "offer_relation_code", fetch = FetchType.EAGER)
#Where(clause = "offer_type_code = 1")
private List<Offers> offer;
//here my getter setter
}
Offers.class
#Entity
#Table(name = "offers")
public class Offers {
#Id #GeneratedValue
#Column(name = "offer_code")
private int offer_code;
#Column(name = "offer_type_code")
private int offer_type_code;
#Column(name = "offer_relation_code")
private int offer_relation_code;
#Column(name = "branch_code")
private int branch_code;
#Column(name = "valid_from")
private String valid_from;
#Column(name = "valid_until")
private String valid_until;
#Column(name = "offer_value")
private int offer_value;
#Column(name = "offer_desc")
private String offer_desc;
//here my getter setter
}
To fetch data
factory = cfg.configure().addAnnotatedClass(Product.class).buildSessionFactory(registry);
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Criteria criteria = session.createCriteria(Product.class);
criteria.setFetchMode("product",FetchMode.JOIN);
Criterion merchant_code_Criterion = Restrictions.eq("merchant_code", new String(merchant_code));
Criterion branch_code_Criterion = Restrictions.eq("branch_code", new String(branch_code));
LogicalExpression andExp = Restrictions.and(merchant_code_Criterion,branch_code_Criterion);
criteria.add(andExp);
search_products = (ArrayList<Product>) criteria.list();
tx.commit();
} catch (HibernateException e) {
// TODO: handle exception
if (tx != null)
tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
Am join the offer table with product table like #ManyToMany(mappedBy = "offer_relation_code", fetch = FetchType.EAGER) am search it in Google ,many of them said don't use EAGER, it leads to some issue, but when i am using #ManyToMany(mappedBy = "offer_relation_code", fetch = FetchType.LAZY) is shows error like failed to lazily initialize a collection of role: could not initialize proxy - no Session. When am using EAGER its working fine without error.Using EAGER is good or bad.Can any one Explain.
Both EAGER and LAZY have use cases when they are useful, they are not good or bad generally speaking.
When some relationship is marked as EAGER it means all of the data from that relation will be fetched from the database when the parent entity is fetched. One SQL will be used for all data.
With LAZY relationship, only the parent entity's data is fetched initially. The lazy relation is replaced with Hibernate's proxy class, which will fetch the child entity's data on first access to any of its properties, using a separate SQL statement. However, there has to be an active Hibernate session in order for this to work. When called outside of active session, you get the exception failed to lazily initialize a collection.
For #nToMany mappings, LAZY is the default which makes perfect sense because Many can really mean many, and there is a good chance you don't need all of the mapped data. So, it's generally a good idea to leave that at LAZY and fetch the data in services where needed. Hibernate has a utility method for initializing lazy relations, Hibernate.initialize(parent.getLazyChild()).
But, as I said in the beginning, it all depends on the use case and it's best if you know all the implications of EAGER and LAZY so you can make your own decision.
Let's first talk about the two POJO's you have.
Offer class represent one table. In other words, it represents all the columns from just one table, with no reference to any other other table.
Product class also represents only one table, but then you have a reference to Offer class.
Now, if you would like to get records for product code 'abc'
EAGER: When using this, you are asking JPA to populate the POJO with data from product and also the corresponding data from offer table.
LAZY: When using this, you are asking JPA to populate POJO with data only from product table. Only when you call, getOffers() then another database call should be made to populate corresponding data from offer table.
You can use LAZY, when the data from a referenced table is not required that often. There is a good probability (>25%) that Offer data may never be shown.
You should use EAGER, when the data from a referenced table,is almost always, required.
For your error, in your final block you are calling session.close(). When you are using LAZY, the transaction is closed after initial fetch. And when you call getOffers(), JPA tries to make a db connection - but fails as it's using already closed connection/session.
EAGER tells hibernate to always fetch all the components on the Many relation (all the Offers in your question), even in situations when you don't need them.
With LAZY, you are responsible to get those Offers inside the transaction, when you really need them.
I Have two entities CRImageType & CRVariable with a many to many relation as follows:
CRImageType entity:
#Entity
#Table(name = "imageviewer_crimagetype")
public class CRImageType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "ImTypeId")
private Long imTypeId;
#Column(name = "ImTypeName")
private String imTypeName;
#Column(name = "ImTypeDescription")
private String imTypeDescription;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="imageviewer_imtype_variable",
joinColumns={#JoinColumn(name="ImTypeId")},
inverseJoinColumns={#JoinColumn(name="VarId")})
private Set<CRVariable> crvariables = new HashSet<CRVariable>();
CRVariable entity:
#Entity
#Table(name = "imageviewer_crvariable")
public class CRVariable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "VarId")
private Long varId;
#Column(name = "VarName")
private String varName;
#Column(name = "VarDescription")
private String varDescription;
#ManyToMany(mappedBy="crvariables")
private Set<CRImageType> crimagetypes = new HashSet<CRImageType>();
In my database the relation is mapped by two tables "imageviewer_crimagetype" & "imageviewer_crvariable" and a third one "imageviewer_imtype_variable" for their many to many relation.
I would like only to DELETE association records from table "imageviewer_imtype_variable". How can be done using an HQL query since i can not directly access "imageviewer_imtype_variable table.
I would like the HQL equivalent of an SQL query like
delete from imageviewer_imtype_variable where ImTypeId='%%%'
This is JPA, not Hibernate specifically. The fact that you have a standardized API on top here makes it easier to find answers if you search in the context of the API, not the implementation.
The way to do it (as far as I remember, I don't use many to many relationships that often) is to remove the related entities from each other's collection mapping fields. So if you have EntityA and EntityB, you remove EntityA from EntityB and EntityB from EntityA. The persistence provider should then be triggered to remove the record from the join table.
Native queries should only be a last resort IMO.
You can execute a native SQL query:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-creating
So in your case, something like:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
You can also specify a custom native SQL DELETE query within your CrImageType entity:
http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch18.html#querysql-cud
From my experience, handling the ManyToMany relation is one of the few case where getting out of ORM integrism is the best option for our mind's sake. Particularly, when you use a two way navigation (i.e. when the relation and the inverse relation are usefull).
#Gimby is correct in his answer, the thing is that with a complex cross referencing it is far harder to make it work, than doing a simple native query.
So:
session.createSQLQuery("DELETE FROM imageviewer_imtype_variable").executeUpdate();
is easier, if it still does not work because of cross referencing, you migh even add a :
session.clear();
OK. This is another ORM integrism infringment, but get you out of the cesspit in two lines ;-) .
The answer from #Gimby is the correct one, in a many to many relationship removing related instances from each other mapping collection triggers a delete from the relationship mapping table.
In the entity class CRVariable you add :
#PreRemove
private void removeCRVariableFromCRImageType() {
for (CRImageType crImageType: CRImageType) {
crImageType.getCrvariables ().remove(this);
}
}
This method will override the JPA action PreRemove in order to detach the CRVariable object to be removed from the set crvariables (technically the table imageviewer_imtype_variable)
Hope this helps you !
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.
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.