JPA merge foreign key lost - java

I'm new to JPA Hibernate implementation and I'm trying to understand how EntityManager.merge works.
I have the following scenario of two classes with #ManyToOne relation e.g
class A{
private String name;
........
get...
set...
}
class B{
private String name;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE)
private A a;
........
get...
set...
}
And I have GUI where you can view and change only B property's - the A class is not loaded and there is no Open Session in View.
When trying to save(merge) changes in back-end:
entityManager.merge(b); //the object "b" created from view has "a" set to null
In database in table B the foreign key relationship(referencing to table A) is lost - set to null;
This is not what a want I want to keep the relationship and merge-save the changes made in the GUI to object B with out losing the Many-To-One relationship
Maybe what I'm expecting is not possible and what I need to do is:
dbB b = entityManager.find(B.class, id);
dbB.setName(b.getName());
dbB.set........
entityManager.merge(dbB);

If in entityManager.merge(b); the relation b.a is null, merge will delete the foreign key. That's because the value null doesn't mean the reference should be ignored but that the reference should be removed (i.e. there is no reference anymore).
I want to keep the relationship and merge-save the changes made in the GUI to object B with out losing the Many-To-One relationship
One option would be to read the entity from the database with a being initialized to a lazy proxy and pass it to the gui. During that process the entity will most likely get detached and hence you need to merge it during save. However, since a won't be null in that case, the reference should not get lost.

Related

org.springframework.orm.jpa.JpaSystemException: identifier of an instance of com.cc.domain.User was altered from 90 to null; [duplicate]

org.hibernate.HibernateException: identifier of an instance
of org.cometd.hibernate.User altered from 12 to 3
in fact, my user table is really must dynamically change its value, my Java app is multithreaded.
Any ideas how to fix it?
Are you changing the primary key value of a User object somewhere? You shouldn't do that. Check that your mapping for the primary key is correct.
What does your mapping XML file or mapping annotations look like?
You must detach your entity from session before modifying its ID fields
In my case, the PK Field in hbm.xml was of type "integer" but in bean code it was long.
In my case getters and setter names were different from Variable name.
private Long stockId;
public Long getStockID() {
return stockId;
}
public void setStockID(Long stockID) {
this.stockId = stockID;
}
where it should be
public Long getStockId() {
return stockId;
}
public void setStockId(Long stockID) {
this.stockId = stockID;
}
In my case, I solved it changing the #Id field type from long to Long.
In my particular case, this was caused by a method in my service implementation that needed the spring #Transactional(readOnly = true) annotation. Once I added that, the issue was resolved. Unusual though, it was just a select statement.
Make sure you aren't trying to use the same User object more than once while changing the ID. In other words, if you were doing something in a batch type operation:
User user = new User(); // Using the same one over and over, won't work
List<Customer> customers = fetchCustomersFromSomeService();
for(Customer customer : customers) {
// User user = new User(); <-- This would work, you get a new one each time
user.setId(customer.getId());
user.setName(customer.getName());
saveUserToDB(user);
}
In my case, a template had a typo so instead of checking for equivalency (==) it was using an assignment equals (=).
So I changed the template logic from:
if (user1.id = user2.id) ...
to
if (user1.id == user2.id) ...
and now everything is fine. So, check your views as well!
It is a problem in your update method. Just instance new User before you save changes and you will be fine. If you use mapping between DTO and Entity class, than do this before mapping.
I had this error also. I had User Object, trying to change his Location, Location was FK in User table. I solved this problem with
#Transactional
public void update(User input) throws Exception {
User userDB = userRepository.findById(input.getUserId()).orElse(null);
userDB.setLocation(new Location());
userMapper.updateEntityFromDto(input, userDB);
User user= userRepository.save(userDB);
}
Also ran into this error message, but the root cause was of a different flavor from those referenced in the other answers here.
Generic answer:
Make sure that once hibernate loads an entity, no code changes the primary key value in that object in any way. When hibernate flushes all changes back to the database, it throws this exception because the primary key changed. If you don't do it explicitly, look for places where this may happen unintentionally, perhaps on related entities that only have LAZY loading configured.
In my case, I am using a mapping framework (MapStruct) to update an entity. In the process, also other referenced entities were being updates as mapping frameworks tend to do that by default. I was later replacing the original entity with new one (in DB terms, changed the value of the foreign key to reference a different row in the related table), the primary key of the previously-referenced entity was already updated, and hibernate attempted to persist this update on flush.
I was facing this issue, too.
The target table is a relation table, wiring two IDs from different tables. I have a UNIQUE constraint on the value combination, replacing the PK.
When updating one of the values of a tuple, this error occured.
This is how the table looks like (MySQL):
CREATE TABLE my_relation_table (
mrt_left_id BIGINT NOT NULL,
mrt_right_id BIGINT NOT NULL,
UNIQUE KEY uix_my_relation_table (mrt_left_id, mrt_right_id),
FOREIGN KEY (mrt_left_id)
REFERENCES left_table(lef_id),
FOREIGN KEY (mrt_right_id)
REFERENCES right_table(rig_id)
);
The Entity class for the RelationWithUnique entity looks basically like this:
#Entity
#IdClass(RelationWithUnique.class)
#Table(name = "my_relation_table")
public class RelationWithUnique implements Serializable {
...
#Id
#ManyToOne
#JoinColumn(name = "mrt_left_id", referencedColumnName = "left_table.lef_id")
private LeftTableEntity leftId;
#Id
#ManyToOne
#JoinColumn(name = "mrt_right_id", referencedColumnName = "right_table.rig_id")
private RightTableEntity rightId;
...
I fixed it by
// usually, we need to detach the object as we are updating the PK
// (rightId being part of the UNIQUE constraint) => PK
// but this would produce a duplicate entry,
// therefore, we simply delete the old tuple and add the new one
final RelationWithUnique newRelation = new RelationWithUnique();
newRelation.setLeftId(oldRelation.getLeftId());
newRelation.setRightId(rightId); // here, the value is updated actually
entityManager.remove(oldRelation);
entityManager.persist(newRelation);
Thanks a lot for the hint of the PK, I just missed it.
Problem can be also in different types of object's PK ("User" in your case) and type you ask hibernate to get session.get(type, id);.
In my case error was identifier of an instance of <skipped> was altered from 16 to 32.
Object's PK type was Integer, hibernate was asked for Long type.
In my case it was because the property was long on object but int in the mapping xml, this exception should be clearer
If you are using Spring MVC or Spring Boot try to avoid:
#ModelAttribute("user") in one controoler, and in other controller
model.addAttribute("user", userRepository.findOne(someId);
This situation can produce such error.
This is an old question, but I'm going to add the fix for my particular issue (Spring Boot, JPA using Hibernate, SQL Server 2014) since it doesn't exactly match the other answers included here:
I had a foreign key, e.g. my_id = '12345', but the value in the referenced column was my_id = '12345 '. It had an extra space at the end which hibernate didn't like. I removed the space, fixed the part of my code that was allowing this extra space, and everything works fine.
Faced the same Issue.
I had an assosciation between 2 beans. In bean A I had defined the variable type as Integer and in bean B I had defined the same variable as Long.
I changed both of them to Integer. This solved my issue.
I solve this by instancing a new instance of depending Object. For an example
instanceA.setInstanceB(new InstanceB());
instanceA.setInstanceB(YOUR NEW VALUE);
In my case I had a primary key in the database that had an accent, but in other table its foreign key didn't have. For some reason, MySQL allowed this.
It looks like you have changed identifier of an instance
of org.cometd.hibernate.User object menaged by JPA entity context.
In this case create the new User entity object with appropriate id. And set it instead of the original User object.
Did you using multiple Transaction managers from the same service class.
Like, if your project has two or more transaction configurations.
If true,
then at first separate them.
I got the issue when i tried fetching an existing DB entity, modified few fields and executed
session.save(entity)
instead of
session.merge(entity)
Since it is existing in the DB, when we should merge() instead of save()
you may be modified primary key of fetched entity and then trying to save with a same transaction to create new record from existing.

Spring Data JPA get entity foreign key without causing the dependent entity lazy load

I have an #Entity A that references another entity B using OneToOne relation ship. I fetch entity A using spring data JpaRepository
A a = aRepository.findById(1);
int b_id = a.getB().getId();
As you can see I need to query ID of the B table, however in order to do that, I need to call getter of the B table, which will cause lazy-loading the B table itself. I do not want to do that because the only thing I need is the get ID, nothing else, and that ID is present in the first A table.
Is there any trick that will help me to get ID of the dependent table without triggering new query?
UPDATE
#Entity
class A {
#Id
private Long id;
#OneToOne
private B b;
}
#Entity
class {
#Id
private Long id;
}
Without looking at the entity mapping, I suspect, your entity classes might be using hibernate annotations on the field. With this if you call even the getId() method as in a.getB().getId() on the entity it will result in initializing the proxy (i.e., B object) and hits the database to fetch it.
So if the intent is only to get the id of the entity you can place the hibernate annotations on the getter methods instead. This doesn't result initializing the proxy (B object) to return the id. Although accessing any property other than id will result in hitting the database.
Have a look at related bug at HHH-3718
So, try using property/getter AccessType instead of field access. As an example instead of placing the annotations on field
#Id
#GeneratedValue(...)
private long id;
place them on the getters
#Id
#GeneratedValue(...)
public long getId() { ... }
Make sure you make similar changes to all the fields of B entity. Although you can explore #Access(AccessType.PROPERTY/FIELD) later.
There is already a related bug HHH-3718 regarding this behavior.
And a related topic on hibernate forum regarding field vs property access type that might be of interest for you Field Vs Property access
Posting your entities classes would help, if this doesn't resolve the issue.

Hibernate trying to save a detached entity - LazyInitializationException

I have the following entities:
#Entity
public class B{
#OneToMany
private List<C> cList;
private Long d;
}
In my managed bean, I need to load a specific b (which is perfectly working) in order to edit the contained attributes (cList, d):
#ManagedBean
public class Bean{
private B b;
public void onEvent(Long bId){
b = bManager.load(bId);
}
}
The attributes of B will have to be edited using a JSF-Form. I do not want these changes to be reflected to the database.
The problem is pretty much the same like in this (old) thread.
But none of the suggestions worked in my case (tried em.flush(), em.detach(), session.evict()).
Is there no solution except deep copying?
It is possible to do it, but first would be better to know what hibernate is doing and why you are getting exception. Here is documentation about object states
If you want to access list with objects C or you want to modify some of them, you must fetch it before it gets to your managed bean. By default hibernate is fetching objects lazy and associated objects will be loaded when you access them, but preconditions is to have a transaction and session attached to objects. So in your managed bean objects are detached and list of C cannot be fetch at that time. To solve that problem you must fetch all object that you want to change before they gets to the managed bean. i.e.
#OneToMany
#Fetch(FetchMode.JOIN) // load it with sql join
private List<C> cList;
There many other ways you can achieve same result. So now you can update your B and list of C
entities and then call update function for your B entity.
Hope it helps.

How to persist two entities at the same time with #OneToOne relationship?

I have two entities Travel and Assurance with #OneToOne relationship. Both entities must be created via the same interface with a Save botton. I use this method:
ManagedBean.java:
public String add(){
newTravel = manager.createTravel(arrivalDate, returnDate, lengthToStay, addToStay, visitPurpose);
newAssurance = manager.createAssurance(company, assuranceStart, assuranceEnd, newTravel);
return "Travellers";
}
In the database, I found the Travel_Id associated to the Assurance but The Assurance_Id is null in the Travels Table.
It seems that your relationship is not bilateral (i.e you do not use the mappedBy annotation property). If you used one, you would have only one column (either Travel_Id or Assurance_Id, depending where you put the mappedBy).
Also consider doing the saving inside the same transaction, e.g by using the same manager method and setting both side of the relationship.

JPA: Merge #OneToMany Duplicate entry error

I'm alwways getting the following error:
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-1' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO ENTITY_ENTITY (relationships_ID, Entity_ID) VALUES (?, ?)
bind => [1, 2]
Query: DataModifyQuery(sql="INSERT INTO ENTITY_ENTITY (relationships_ID, Entity_ID) VALUES (?, ?)")
My classes look like:
#javax.persistence.Entity
public class Entity {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
protected Long id;
#Column(nullable=false, updatable=false)
protected String name;
#OneToMany(cascade=CascadeType.MERGE)
protected Collection<Entity> relationships = new ArrayList<Entity>();
}
One instance of this class can reference other instances of the same type (self-referencing).
Now, while creating one instance which references another, everything works fine.
But the problem is, when such an (detached - I'm not quite sure, because the instance is edited via a Facelets form) instance is updated, i.e. the name, then by executing the EntityManager merge operation the exception - as mentioned above - is thrown.
EDIT:
1. Create one instance of Entity (i.e. E1).
2. Persist E1 via em.persist(E1).
3. Later, create another instance of Entity (E2) which refreneces E1 via the property relationships.
4. And persist E2: em.persist(E2).
--- Until now everything works fine.
Load to browse all persisted Entity instances and choose E2 for editing (via a Web interface Facelet).
Change i.e. the name of E2.
To save the changes: Call em.merge(E2).
---Now the Exception is thrown!
END EDIT
What's wrong with my code?
Please, help me!!!
It seems the way you are detaching/merging your objects is somehow corrupting the object to have a two references to the same Entity, or to think a reference is new when it was existing. This is causing the INSERT into the OneToMany join table that is a duplicate and is raising the constraint violation.
Trying debugging or checking the state of the collection at every stage, does it get a duplicate instance added to it?
Possibly it could have something to do with caching or change tracking. You could try turning off the shared cache, or refreshing the object, or disabling weaving.
http://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Caching#How_to_refresh_the_cache
http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#How_to_Disable_Weaving_Using_EclipseLink_Persistence_Unit_Properties

Categories