Duplicate Records using Bidrectional Mapping in Hibernate - java

I want to store workers in an Organization object, For this, I have an Organization class having One to Many Mapping for workers.
#OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "ORGANIZATION_WORKER", joinColumns = { #JoinColumn(name = "orgIdPK") }, inverseJoinColumns = { #JoinColumn(name = "id") })
public Set<Worker> workerList;
"ORGANIZATION_WORKER is a temporary table that holds the mapping"
Then I have a Worker class where I have Many to One Mapping as follow.
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "orgIdPK")
public Organization organization;
Now when I save the organization after inserting workers into it using hibernate session, i-e
session.saveOrUpdate(org);
insert query runs twice (N+1 issue). `
Hibernate: insert into ORGANIZATION_WORKER (orgIdPK, id) values (?, ?)
Hibernate: insert into ORGANIZATION_WORKER (orgIdPK, id) values (?, ?)
So when I expose Organization data on rest API, all records are shown twice because of which pagination gets wrong on the rest API. Anyone knows how to solve this issue?

The problem is that you are creating a circular reference. You are inserting a organization which has workers in it, and those workers have that same organization as a parameter, so it is inserting twice.
To solve this issue, Hibernate has some keywords applicable to columns: "insertable" and "updatable", that specify whether or not the value of that column will be inserted or updated in DB.
In general, the proper thing to do is to make the OneToMany relationship non-insertable and non-updatable; if you want to change the Organization of a Worker, you will probably do it from that Worker; the class responsible for updating that value is the Worker itself, that is, the class with the ManyToOne association.
So you need to do this:
#OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER, insertable=false, updatable=false)
This way all changes in this column will be ignored when the entity inserts or updates in DB.

Remove the annotations from Worker.
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "orgIdPK")
http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#collections-map-unidirectional

Related

How do we use Integer or Long type of objects as Foreign key mapping in JPA/Hibernate?

I tried below sample code which does not seem to work, while trying to fetch the object from table, hibernate tries to set the User Object on top of Long objects and fails when tried to load the Parent entity which has this createdBy field
any help?
#JoinColumn(name = "CREATED_BY", referencedColumnName = "USER_ID", updatable = false, nullable = false)
#OneToOne(targetEntity = User.class)
private Long createdBy;
I want to use Long/Integer as type of the field and want to make sure its a valid Foreign key to User table which has Primary key USER_ID
Please note that i DO NOT want to use User as type of my object, for example , i do not want below declaration in my class
private User createdBy;
Edit:
Reason for such requirement:
Ahh well!!, i'll try to keep it short which is basically one of the problem with ORM's like Hibernate. I have a Super Class AuditLogModel which is extended by each and every entity class in my software (100+ entities). This AuditLogModel class has CreatedBy & ModifiedBy field. If i keep the types of this fields as User, then every entity in my software tries to create a join with user table twice on operations like getResultsList/Merge/refresh, where Merge and Refresh calls on entity manger cannot be controlled by us, its all eager loading in one select query. Since my entities have child entities and they have further childs and so on, this creates more than 61 joins and sometime 100+ joins and causes query performance issues. These createdBy and modified by columns are updated with every insert/update but not required with any query when any entity is loaded, and hence I want to avoid the unneccessari joins here. Hope its understood
Ah OneToOne mapping is used for Entities like this:
#JoinColumn(name = "CREATED_BY", referencedColumnName = "USER_ID", updatable = false, nullable = false)
#OneToOne
private User createdBy;
If you want to use just the database value then you must remove the OneToOne mapping:
#Column(updatable = false, nullable = false)
private Long createdBy;

JPA Hibernate Lazy many-to-one fetch proxy

I'm using JPA 2.1 and Hibernate 4.3.7
I tried to tuned my app so I turn relationships to lazy and fetch only what I need them
I have a problem with the many-to-one relationships, when turn to lazy when I load the entity again Hibernate replace the entity by a proxy even if I fetch the entity and this proxy is not working in the view part (JSF) of the application.
The problem disapear when the many-to-one is in eager mode but hibernate perform one select more for each many-to-one even if I don't need them
#Entity
public class Department {
#Id
private Integer id;
//...
}
1/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e LEFT JOIN FETCH e.department WHERE e.id=:id
=> one select query => faster but department is of type Department_$$_jvst3ac_5f (employee.getDepartment().getClass().getCanonicalName()) and this proxy doesn't work in the view part of the application
2/
#Entity
public class Employee {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.EAGER, optional = true)
#JoinColumn(name = "id_department", referencedColumnName = "id")
private Department department;
//...
}
the JPQL query:
SELECT e FROM Employee e WHERE e.id=:id
=> two selects => slower but department is loaded as Department and everything goes fine in the view part of the application
The relation is unidirectional, Department have no references of emplyees
Is this possible to have the department without proxy when using FETCH JOIN?
After the response of Luiggi I will precised that the data are fetched with lazy many-to-one + fetch join. When I do a employee.getDepartment().toString() I have Department{ id=11, ...} but the class of this department is still Department_$$_jvst3ac_5f. For reason I don't know, the JSF/PrimeFaces selectOneMenu component don't work properly whith HibernateProxy even if the data are fetched
I tried to use the Hibernate annotation #LazyToOne(LazyToOneOption.FALSE) in addition of #ManyToOne(fetch = FetchType.LAZY) but the result is similar of #ManyToOne(fetch = FetchType.EAGER) alone...
The problem is that when you use lazy loading you will obtain a proxy of the class (as you already stated) and this proxy can fetch the data from database only if the hibernatesession is still open. Seems like your session is being closed when returning the data to the view, so when trying to use the lazily-loaded field in the view you're getting the exception.
Possible solutions:
Keep the field as fetch eager and pay the overhead for each query against your entity (probably this isn't good and can affect performance, but is a solution).
Maintain your field as lazy and use the proper get method before the Hibernate session is closed in order to the proxy to retrieve the relevant data to be used after the session is closed.

hibernate many-to-many relationship updating unexpectingly

I have two classes:
class TrainingCourse {
Integer id;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name = "TrainingCourseClass", joinColumns = { #JoinColumn(name = "CourseID") }, inverseJoinColumns = { #JoinColumn(name = "ClassID") })
private Set<TrainingClass> trainingClasses;
}
class TrainingClass {
Integer id;
}
In the database they are mapped using a join table. So this is a unidirectional relationship.
From the UI, when a TrainingCourse is created, a list of previously created TrainingClasses are selected from the UI.
Now if I create the TrainingCourse, then it automatically updates the associated TrainingClasses also. But trainingClass is independent of TrainingCourse and can exist independently. So TrainingClasses are created and updated separately from the TrainingCourse. So saving the TrainingCourse should save data in the TrainingCourse table and it will also save the association in the join Table TrainingCourseClass. Nothing should happen in the table TrainingClass.
However if I add these to the columns:
nullable=false, updatable=false and CascadeType.REMOVE
#OneToMany(cascade = CascadeType.REMOVE, fetch=FetchType.EAGER)
#JoinTable(name = "TrainingCourseClass", joinColumns = { #JoinColumn(name = "CourseID", nullable=false, updatable=false) }, inverseJoinColumns = { #JoinColumn(name = "ClassID", nullable=false, updatable=false) })
private Set<TrainingClass> trainingClasses;
Then the problem is fixed ie creating trainingCourse doesn't update the trainingClass table. Now I am not 100% sure whether it is the right solution or how it is working to solve the problem. There is also another thing called MappedBy. I am not sure whether this is relevant here.
I just used it as a guess and it is working. Moreover, this seems to be really a many-to-many relationship ie The same class can belong to many courses and one course can include many classes. But one-to-many relationship is also working. This is not very convincing. The trainingclass is really unaware of what training courses include it. It looks like the difference between one-to-many and many-to-many is like whether or not to have bidirectional pointers to each other.
Hence please suggest whether the above approach is correct to prevent updating the trainingclass while creating the trainingcourse.
Thanks
Your first mapping uses cascade = ALL. That means that every operation you make on a TrainingCourse (persist, merge, remove, etc.) will also be applied on the associated TrainingClass. That's precisely what you don't want, if I understand correctly. So just don't set any cascade to this association.
Regarding OneToMany vs. ManyToMany: if what you really want is a OneToMany (i.e. a TraningClass should not be associated with more than one TrainingCourse), then you should have a unique contraint on the TrainingCourseClass.ClassID column. That's what guarantees that the association is a OneToMany and not a ManyToMany.

Hibernate OneToMany with JoinTable ID generation

Could somebody help me in setting appropriate annotation in hibernate for following case:
I have three tables:
Account, Card and AccountCard.
AccountCard is joining table for OneToMany relationship between Card and Account (account has many cards, card is attached to only one account).
I need to add to Account a List cards property and to Card model Account account property. This is the easy thing.
The problem is that I get "Cannot insert null value to AccountCard.id" while persisting Account with Cards.
Also I need to use sequence to generate IDs for joining table but don't know how.
Any help would be very appreciated.
Here is the code in Card:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "account_card", joinColumns = #JoinColumn(name = "crd_id"), inverseJoinColumns = #JoinColumn(name = "acc_id"))
private Account account;
I don't want to have a mapping in Account class so List cards is not added.
In your #JoinTable annotation, I see reference to an account_name table and not AccountCard. Is there actually an AccountCard table somewhere?
A join table usually doesn't need an id key of its own, and if you have hibernate autogenerate your table DDL it won't include one.
If you do indeed need an id on the join table, I don't think there's a way or a need to make hibernate aware of it, but you should make the column NOT NULL AUTO_INCREMENT in your SQL DDL.
Did you try to generate the tables first in the database (in my case mysql) and then create the entity with an ide like nebans? An auto increment id column in mysql then ends with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
Check out http://netbeans.org/kb/docs/javaee/ecommerce/entity-session.html for a sample how to use netbeans to create entities from database

How to retrieve nested JPA entities in a single query

I am trying to retrieve entities using eclipselink JPA and am looking for a way to reduce the number of queries run to retrieve a single entity. I believe I should be using the #JoinFetch annotation to retrieve sub-entities in the same query as the main entity. This works fine for a single level of join, but not for multiple levels.
In the example below, EntityA contains a collection of EntityB which contains an EntityC. When I retrieve EntityA, I want a single query to return all 3 sets of entity data. In reality it generates 2 queries, 1 joining EntityA and EntityB and then a separate query joining EntityB and EntityC.
Is it possible to combine this into one query?
class EntityA {
#OneToMany(mappedBy = "entityALink", fetch = FetchType.EAGER)
#JoinFetch
private Collection<EntityB> entityBs;
}
class EntityB {
#JoinColumn(name = "X", referencedColumnName = "Y")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private EntityA entityALink;
#JoinColumn(name = "A", referencedColumnName = "B")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinFetch
private EntityC entityCLink;
}
class EntityC {
#Id
#Basic(optional = false)
#Column(name = "SomeColumn")
private String someField
}
If you need reduce number of queries, you may using lazy initialization - FetchType.LAZY instead of FetchType.EAGER - in this way jpa get data from databases when need. But you must remember, this is not working when entity is disconnected from manager. So if you send this entity to other servers in serialize the form (ex. in multi-level application) you must again connected this entity with manager. If you application runs in one server, then you don't have this problem.
Summing up is not the exact answer to your question, but maybe helpful for optimize this code.
Exact answer for you question:
You may using named queries, but then query is parse to sql native query, and you don't sure that this working as you want. But maybe you may using native query method?
em.createNativeQuery("SELECT ... your queries")
For this purpose, please read about using #SqlResultSetMapping annotation to configure result entity class...
First write a query to get EntityA.
EntityA entity = <your Query> ;
then call
Collection<EntityB> entityB = entity.getEntityBs();
for(EntityB eachB : entityB){
EntityC entityCLink = eachB.getEntityCLink();
}
Note: Create setter & getters in each entity.

Categories

Resources