How to create Many-One Mapping in hibernate? - java

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.

Related

JPA unidirectional #OneToOne relationship with shared primary key always trigger a secondary query even if fetchType is EAGER

I am building a blog system, and like to provide the upvote/downvote feature for the blog. Since the vote count number of blog should be persisted, i choose to use MySQL to act as the data store. And i use Spring JPA(Hibernate) to do the ORM job. Here's my data objects:
class Blog{
// ...
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(optional = false, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn
private BlogVoteCounter voteCounter;
}
And the counter class:
#Entity
public class BlogVoteCounter extends ManuallyAssignIdEntitySuperClass<Long> {
#Id
private Long id;
private Integer value;
}
The reason why i separate the BlogVoteCounter from Blog is that i think the voteCount field will be modified by a totally different frequency comparing to other fields of Blog, since i want to use cache to cache the Blog, following this guide, i choose to separate them.
However, since the VoteCount field might be always needed when return the Blog object to the front end, and to avoid the n+1 problem, i declared the BlogVoteCounter field in Blog class with EAGER fetch type.
I've already seen this article. Thus according to my personal comprehension, i use unidirectional relationship and only declare OneToOne in the Blog side.
However, when i examine the query, it turns out that jpa will still trigger a secondary query to retrieve BlogVoteCounter from database without simply using a join when use findAll method on BlogRepository.
select
blogvoteco0_.id as id1_2_0_,
blogvoteco0_.value as value2_2_0_
from
blog_vote_counter blogvoteco0_
where
blogvoteco0_.id=?
So how should i config, to always make the BlogVoteCounter field in Blog be fetched eagerly.
The usage of ManuallyAssignIdEntitySuperClass is following the Spring JPA doc, since i manually assign id for BlogVoteCounter class.
#MappedSuperclass
public abstract class ManuallyAssignIdEntitySuperClass<ID> implements Persistable<ID> {
#Transient
private boolean isNew = true;
#Override
public boolean isNew() {
return isNew;
}
#PrePersist
#PostLoad
void markNotNew(){
this.isNew = false;
}
}
And the BlogRepository is derived from JpaRepository
public interface BlogRepository extends JpaRepository<Blog, Long>{
// ...
}
I trigger the query by using findAll method, but using findById or other conditional query seems no difference.
When to fetch vs How to fetch : fetchType defines when to fetch the association ( instantlyvs later when someone access) the association but not how to fetch the association(i.e second select vs join query). So from JPA Spec point of view, EAGER means dont wait until someone access that field to populate it but JPA provider is free to use JOIN or second select as long as they do it immediately.
Even though they are free to use join vs second select, still I thought they should have optimised for join in the case of EAGER. So interested in finding out the logical reasoning for not using the join
1. Query generated for repository.findById(blogId);
select
blog0_.id as id1_0_0_,
blog0_.vote_counter_id as vote_cou2_0_0_,
blogvoteco1_.id as id1_1_1_,
blogvoteco1_.value as value2_1_1_
from
blog blog0_
inner join
blog_vote_counter blogvoteco1_
on blog0_.vote_counter_id=blogvoteco1_.id
where
blog0_.id=?
2. Updated Mapping
public class Blog {
#Id
private Long id;
#ManyToOne(optional = false, cascade = ALL, fetch = FetchType.EAGER)
#PrimaryKeyJoinColumn
private BlogVoteCounter voteCounter;
public Blog() {
}
public Blog(Long id, BlogVoteCounter voteCounter) {
this.id = id;
this.voteCounter = voteCounter;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public BlogVoteCounter getVoteCounter() {
return voteCounter;
}
public void setVoteCounter(BlogVoteCounter voteCounter) {
this.voteCounter = voteCounter;
}
}
3. Issues with current Mapping
As per your mapping, it is impossible to create blog and votecounter as it causes a chicken and egg problem.
i.e
blog and votecounter need to share the same primary key
blog's primary key is generated by database.
so in order to get the primary key of blog and assign it to votecounter as well, you need to store blog first
but the #OneToOne relationship is not optional, so you cannot store blog first alone
4.Changes
Either need to make the relationship optional so blog can be stored first, get the id, assign to BlogVoteCounter and save the counter
Or Don't auto generate Id and manually assign the id so blog and votecounter can be saved at the same time.(I have gone for this option but you can do first option)
5.Notes
default repository.findAll was generating 2 queries so I overridden that method to generate one join query
public interface BlogRepository extends JpaRepository<Blog, Long> {
#Override
#Query("SELECT b from Blog b join fetch b.voteCounter ")
List<Blog> findAll();
}
select
blog0_.id as id1_0_0_,
blogvoteco1_.id as id1_1_1_,
blog0_.vote_counter_id as vote_cou2_0_0_,
blogvoteco1_.value as value2_1_1_
from
blog blog0_
inner join
blog_vote_counter blogvoteco1_
on blog0_.vote_counter_id=blogvoteco1_.id

How to join two tables using Criteria API if join relationship is on EmbeddedId

I have the following classes:
#Entity
public class EventOrderLine {
#EmbeddedId private EventOrderLineId id;
}
#Embeddable
public class EventOrderLineId implements Serializable {
#ManyToOne
#JoinColumn(name = "eventid")
#JsonIgnore
private Event event;
#ManyToOne
#JoinColumn(name = "orderlineid")
#JsonIgnore
private OrderLine orderLine;
}
#Entity
public class OrderLine {
#OneToMany
#JoinColumn(name = "orderlineid")
#JsonIgnore
private List<EventOrderLine> eventOrderLines = new ArrayList<>()
}
Basically I'm trying to join the two tables via the Criteria API but having issues since this is what I want to do:
Root eventOrderLine = criteriaQuery.from(EventOrderLine.class);
Join orderLine = eventOrderLine.join("orderLine");
Of course this give me this issue since the mapping isn't directly on the entities themselves:
Unable to locate Attribute with the the given name [orderLine] on this ManagedType [com.EventOrderLine]
I've been trying to tweak the join to drill into the embeddedId but not sure if I need to go a step further and modify how my entities are mapped. I feel like it's probably something simple I'm missing but having trouble finding this specific question.
The event field is a member of EventOrderLineId and not EventOrderLine. In your criteria query, you first need to navigate to id. The catch is that Root.path("id") returns an instance of Path, which does not allow further joins.
The trick is to use a 'fake' join with the id field like so: eventOrderLine.join("id").join("event")
eventOrderLine.get("id").get("event") would likely work just as well, but it wouldn't allow you to specify the join type.
first try to get the property id of EventOrderLine entity and then join. So, it would be -
Root eventOrderLine = criteriaQuery.from(EventOrderLine.class);
Join orderLine = eventOrderLine.get("id").join("orderLine")

Separate Table vs Extra Columns in JPA/Hibernate

I am wondering about best practices in database design with Hibernate.
I have a User entity that is going to have a lot of different settings. For each set of settings, I have to either add them as extra columns in the User table or create a separate entity and connect them with a #OneToOne relationship. It is my understanding that #OneToMany and #ManyToOne relationships should generally take place in separate tables because you should not have columns that are optional.
But it is kind of unclear for #OneToOne relationships. I think there is a case for using #OneToOne because ORMs will select all single attributes by default and having a lot of columns will slow down that process.
An example of what I am talking about can be illustrated by
#Entity
public class User{
#OneToOne
private ForumSettings forumSettings;
#OneToOne
private AccountSettings accountSettings;
#OneToOne
private SecuritySettings securitySettings;
}
vs
#Entity
public class User{
#Column
private boolean showNSFWContent; //Forum Setting
#Column
private int numberOfCommentsPerPage; //Forum Setting
#Column
private boolean subscribedToNewsLetter; //Account Setting
#Column
private boolean isAccountBanned; //Account Setting
#Column
private boolean isTwoFactorAuthenticationEnabled; //Security Setting
#Column
private boolean alertForSuspiciousLogin; //Security Setting
}
The above is a simple example to show the concept, but in practice there would be many more columns in the 2nd portion.
I know that this might be opinion based, but I am hoping someone could share the pros/cons of both choices.
Thank you very much
Your question is in general about Data normalization. Normalization is itself extensive field of study and basically is a way of structuring database tables avoiding redundancy and making sure that updates don’t introduce anomalies.
And first rule of normalization says a table shall contain no repeating groups. In your case it does.
SOLUTION 1 : Store UserSettings as Entity as map as OneToMany relationship
#Entity
public class User
#OneToMany
private List<UserSettings> userSettings;
And then you can query for particular setting type by joining User and UserSettings entities.
For example (JPQL)
SELECT user u
JOIN u.settings us
WHERE us.settings_type = 'account_settings'
and us.settings_value = 'secure' // or any other logic
Advantage of this approach is that UserSettings will have it is own persistence identity and can be queried by it's own. It it is not dependent on parent.
For example :
SELECT q from Query q where ...
Solution 2 : Store settings in a collection of basic elements
You can store User Settings in the collection (Each user will have it's own set of settings)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#ElementCollection
#CollectionTable(name="USER_SETTINGS")
#MapKeyColumn(name="SETTINGS_TYPE")
#Column(name="SETTINGS_VALUE")
Map<String, Boolean> userSettings = new HashMap<>();
UserSettings collection will be stored in a separate table with foreign key to User table. UserSettings does not have it is own persistence ID, is dependent on User entity and can be queried only through it is parent ('User')
Solution 3: Store User Settings as Embedded type
Embedded type is not an entity, it does not have it is own persistence ID and is depends on parent type, stored as part of parent record in database (in User table)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#Embedded
private UserSettings userSettings;
UserSettings is in separate class, but stored in User table.
#Embeddable
public class UserSettings {
private List<String> securitySettings; // or any other collection type
private List<Boolean> forumSettings;

Hibernate OneToMany mapping

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)

HQL delete associations from many to many mapping

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 !

Categories