Hibernate :"detached entity passed to persist" error [duplicate] - java

I am trying to run this basic JPA/EJB code:
public static void main(String[] args){
UserBean user = new UserBean();
user.setId(1);
user.setUserName("name1");
user.setPassword("passwd1");
em.persist(user);
}
I get this error:
javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.JPA.Database
Any ideas?
I search on the internet and the reason I found was:
This was caused by how you created the objects, i.e. If you set the ID property explicitly. Removing ID assignment fixed it.
But I didn't get it, what will I have to modify to get the code working?

The error occurs because the object's ID is set. Hibernate distinguishes between transient and detached objects and persist works only with transient objects. If persist concludes the object is detached (which it will because the ID is set), it will return the "detached object passed to persist" error. You can find more details here and here.
However, this only applies if you have specified the primary key to be auto-generated: if the field is configured to always be set manually, then your code works.

Let's say you have two entities Album and Photo. Album contains many photos, so it's a one to many relationship.
Album class
#Entity
public class Album {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Integer albumId;
String albumName;
#OneToMany(targetEntity=Photo.class,mappedBy="album",cascade={CascadeType.ALL},orphanRemoval=true)
Set<Photo> photos = new HashSet<Photo>();
}
Photo class
#Entity
public class Photo{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Integer photo_id;
String photoName;
#ManyToOne(targetEntity=Album.class)
#JoinColumn(name="album_id")
Album album;
}
What you have to do before persist or merge is to set the Album reference in each photos.
Album myAlbum = new Album();
Photo photo1 = new Photo();
Photo photo2 = new Photo();
photo1.setAlbum(myAlbum);
photo2.setAlbum(myAlbum);
That is how to attach the related entity before you persist or merge.

remove
user.setId(1);
because it is auto generate on the DB,
and continue with persist command.

I got the answer, I was using:
em.persist(user);
I used merge in place of persist:
em.merge(user);
But no idea, why persist didn't work. :(

if you use to generate the id = GenerationType.AUTO strategy in your entity.
Replaces user.setId (1) by user.setId (null), and the problem is solved.

Here .persist() only will insert the record.If we use .merge() it will check is there any record exist with the current ID, If it exists, it will update otherwise it will insert a new record.

I know its kind of too late and proly every one got the answer. But little bit more to add to this: when GenerateType is set, persist() on an object is expected to get an id generated.
If there is a value set to the Id by user already, hibernate treats it as saved record and so it is treated as detached.
if the id is null - in this situation a null pointer exception is raised when the type is AUTO or IDENTITY etc unless the id is generated from a table or a sequece etc.
design: this happens when the table has a bean property as primary key.
GenerateType must be set only when an id is autogenerated.
remove this and the insert should work with the user specified id.
(it is a bad design to have a property mapped to primary key field)

If you set id in your database to be primary key and autoincrement, then this line of code is wrong:
user.setId(1);
Try with this:
public static void main(String[] args){
UserBean user = new UserBean();
user.setUserName("name1");
user.setPassword("passwd1");
em.persist(user);
}

I had this problem and it was caused by the second level cache:
I persisted an entity using hibernate
Then I deleted the row created from a separate process that didn't interact with the second level cache
I persisted another entity with the same identifier (my identifier values are not auto-generated)
Hence, because the cache wasn't invalidated, hibernate assumed that it was dealing with a detached instance of the same entity.

Related

Get just added Object with EntityManagerFactory

I'm developing a java app with MySql database, JPA objects and EntityManagerFactory with EclipseLink to manage the database. Everything works Ok but I have an issue.
One of my JPA objects is like this
public class JPAObject1{
#Id
#GeneratedValue
private int id;
#OneToMany(//things here)
List<JPAObject2> list1;
...
}
So the id field will be autogenerated by the EntityManagerFactory when I store it in the database. Asumming em type EntityManager and object type JPAObject1:
em.getTransaction().begin();
em.persist(object);
em.getTransaction().commit();
//house work closing things
The JPAObject1 is added correctly, I can see all fields in my database. As field id is the key to do the find operation, my question is:
Is there a way to get the last added object on the EntityManager on just the moment it is added?
Because I have others objects that use the JPAObject1 id field as a foreign key and I need that field when just the object is added to the database to link the others, but the only way I know to get it is getting all the JPAObjects and getting the last one in the Collection. So, with a few Objects it won't be a problem but if one process insert on database and another do the same before process 1 does the findAll to get the last added, there will be a coherence error....
I think I've explained it well.
Thanks a lot!
you can use this code
Obejct en = new Obejct ();
en.setxxx("My name");
em.persist(en);
em.flush();
System.out.println(en.getId());
the id genrated after flush
Note that the datas saved to database is a set, not list. So they don't have the order or anything like that, and you can't get the last one you've added. If you want to, pls add a column like date, time..., and the query will be like:
" SELECT * FROM Table ORDER BY dateColumn DESC LIMIT 1"

Hibernate #MapsId why getting this errors?

So, I have Class A and Class B.
They share their primary key, using the following configuration:
In Class A I reference Class B as a child
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
public B getB()
{
return b;
}
In Class B, in order to get ID from parent Class A, I use the following annotations:
#Id
#GeneratedValue(generator = "customForeignGenerator")
#org.hibernate.annotations.GenericGenerator(name = "customForeignGenerator", strategy = "foreign", parameters = #org.hibernate.annotations.Parameter(name = "property", value = "a"))
#Column(name = "a_id")
public Long getId()
{
return id;
}
#MapsId("id")
#OneToOne(mappedBy = "b")
#PrimaryKeyJoinColumn
public A getA()
{
return a;
}
The problem is that uppon saving A with
session.saveOrUpdate(aInstance);
DB returns the following error:
Duplicate entry '123456' for key 'PRIMARY'
This tells us 2 things, first is that #MapsId is working correctly, giving A's Id to B as it should, the second is that hibernate decided it was a 'save' and not an 'update', and this only happens on saveOrUpdate when Id is null right? (wierd?)
The usual solution would be to get the old B from DB and merge, if existed, but that arrises a whole lot of problems like also getting the old A from DB to session or making the dreaded "a different object with the same identifier value was already associated with the session" hibernate errors for the assossiated objects. Also not very performance friendly, doing unecessery DB hits.
Is there an error in my anotations? Am I doing it wrong? What is the normal configuration for this?
EDIT:
It kind of defeats the purpose of using #MapsId, setting the IDs manually, but since no solution was found I did set the IDs manually like this:
if(aInstance.getId() != null)
aInstance.getB().setId(aInstance.getId());
session.saveOrUpdate(aInstance);
Just until moments ago this was returning the following error:
org.hibernate.StaleStateException:
Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
But for some reason it stopped throwing the error and now it works. In all cases, the previous code is still all valid since aInstance might not have Id, and in that case, the MapId works perfectly inserting a new A and B in BD. The problem was only on Update.
Was\Is it an hibernate bug? probably. I'll let you guys know when StaleStateException turn up again.
For now this is a temporary solution, until someone comes up with the actual solution.
I finally found the answer to all the problems.
To understand the root of the problem we must remind how saveOrUpdate(object) works.
1) If object has ID set, saveOrUpdate will Update else it will Save.
2) If hibernate decides it is a Save but object is already on DB, you want to update, the Duplicate entry '123456' for key 'PRIMARY' exception will occur.
3) If hibernate decides it is an Update but object is not in DB, you want to save, the StaleStateException Exception occurs.
The problem relies on the fact that if aInstance exists in DB and already has an ID, #MapsId will give that ID to B ignoring the rules above, making Hibernate think B also exists in DB when it may not. It only works properly when both A and B dont exist in DB or when they both exist.
Therefor the workaround solution is to make sure you Set the ID only and only if each object exists in DB, and set ID to null when it does not:
B dbB = (B) unmarshaller.getDetachedSession().createCriteria(B.class).add(Restrictions.idEq(aInstance.getId())).uniqueResult();
if (dbB != null) //exists in DB
{
aInstance.getB().setId(aInstance.getId()); //Tell hibernate it is an Update
//Do the same for any other child classes to B with the same strategy if there are any in here
}
else
{
aInstance.getB().setId(null); //Tell hibernate it is a Save
}
unmarshaller.getDetachedSession().clear();
(using detached session, so that main session stays clear of unwanted objects, avoiding the "object with the same identifier in session" exception)
If you dont need the DB object, and only want to know if it exists or not in the DB, you can use a Count, making it much lighter:
String query = "select count(*) from " + B.class.getName() + " where id = " + aInstance.getId();
Long count = DataAccessUtils.uniqueResult(hibernateTemplate.find(query));
if (count != null && count > 0)
{
aInstance.getB().setId(aInstance.getId()); // update
}
else
{
aInstance.getB().setId(null); // save
}
Now you can saveOrUpdate(aInstance);
But like i said, #MapsId strategy is not very Hibernate friendly.
Some key realization that helped me understanding #MapsId better:
The #MapsId annotation changes the ID type of the entity from Assign to Generate. (I wish I could override this behavior and set the IDs manually.)
Generate assumes that an entity with a non null ID already exists in DB. Therefore, setting the ID manually leads to StaleObjectException because hibernate issues an EntityUpdateAction instead of Create, but there is nothing to update inside the DB.
On persist the null ID will be automatically set by using the ID of the other side of the #OneToOne relationship.
If the other side is missing and the ID is null an Exception is raised.

Struts2 and Hibernate insert operation error [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.

Safe embedded entity with objectify

I have two entities.
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
#Entity
public class Step {
#Id
private Long id;
private String instruction;
}
And the following Clound Endpoint
#ApiMethod(
name = "insert",
path = "recipe",
httpMethod = ApiMethod.HttpMethod.POST)
public Recipe insert(Recipe recipe) {
ofy().save().entities(recipe.getSteps()).now(); //superfluous?
ofy().save().entity(recipe).now();
logger.info("Created Recipe with ID: " + recipe.getId());
return ofy().load().entity(recipe).now();
}
I'm wondering how do I skip the step where I have to save the emebedded entity first. The Id of neither entity is set. I want objectify to automatically create those. But if don't save the embedded entity I get an exception.
com.googlecode.objectify.SaveException: Error saving com.devmoon.meadule.backend.entities.Recipe#59e4ff19: You cannot create a Key for an object with a null #Id. Object was com.devmoon.meadule.backend.entities.Step#589a3afb
Since my object structure will get a lot more complex, I need to find a way to skip this manual step.
I presume you are trying to create real embedded objects, not separate objects stored in the datastore and linked. Your extra save() is actually saving separate entities. You don't want that.
You have two options:
Don't give your embedded object an id. Don't give it #Entity and don't give it an id field (or at least eliminate #Id). It's just a POJO. 90% of the time, this is what people want with embedded objects.
Allocate the id yourself with the allocator, typically in your (non-default) constructor.
Assuming you want a true embedded entity with a real key, #2 is probably what you should use. Keep in mind that this key is somewhat whimsical since you can't actually load it; only the container object can be looked up in the datastore.
I suggest going one step further and never use automatic id generation for any entities ever. Always use the allocator in the (non-default) constructor of your entities. This ensures that entities always have a valid, stable id. If you always allocate the id before a transaction start, it fixes duplicate entities that can be created when a transaction gets retried. Populating null ids is just a bad idea all around and really should not have been added to GAE.
The concept of the embedded is that the embedded content is persisted inside the main entity.
Is this the behaviour you are trying to configure?
The default behaviour of a Collection (List) of #Entity annoted class is to refer them instead of embed them. As you current configuration, the List<Step> variable does not have any annotation to override the default configuration, which is a different entity related to another one.
The error you are getting is because Objectify, when it saves the recipe entity, is trying to get the key of each step to create the relationship (and save them in the recipe entity), but if the entity step is not saved yet on the datastore, does not have a key
If you are trying to persist the steps inside the recipe entity, you need to setup objectify like this
#Entity
public class Recipe {
#Id
private Long id;
private List<Step> steps;
}
public class Step {
private Long id;
private String instruction;
}
As you can see, I removed the #Id annotation (an embedded Entity does not require an ID because is inside another entity) and the #Entity from the Step class. With this configuration, Objectify save the step entities inside the recipe entity
Source: https://code.google.com/p/objectify-appengine/wiki/Entities#Embedded_Object_Native_Representation

Efficiently determining the IDs of entities referenced via OneToMany relationship

Let's say I have a Hibernate entity that declares a OneToMany relationship to a different entity:
#Entity
public class SomeEntity {
#OneToMany(fetch = FetchType.LAZY)
private List<OtherEntity> otherEntities = new LinkedList<OtherEntity>();
[...]
}
When mapping SomeEntity to the corresponding DTO, all I need are the IDs that identify OtherEntity as primary key (i.e., I am not actually interested in OtherEntity instances).
Does Hibernate support this pattern, i.e., only retrieving the IDs of entities referenced via a OneToMany relationship?
I cannot influence how SomeEntity is retrieved (i.e., I have an existing SomeEntity instance retrieved within te scope of the current Hibernate session), but let's assume that lazy loading has not yet taken place, so just retrieving the child objects' IDs (rather than the complete objects) would actually yield a performance benefit.
Well, if you only need the entities' ids and you want to be economical about it, when you get those entities from the database you should state in your query that you only want to get the ids of each entry, using projections, something like :
SELECT Entity.id as entity FROM Entity WHERE ...
This will return an array of objects of the same type as Entity's id field type.
You can try obtaining the primary key without accessing the entity itself (without otherEntities.get(0).getId()). To do this you can use the PersistenceUnitUtil class:
PersistenceUnitUtil#getIdentifier(yourEntity)
The PersistenceUnitUtil can be obtained from the EntityManagerFactory. So it could be something like:
EntityManager em = ...
PersistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
Unfortunately, I'm not aware if this will prevent the entity loading from occuring. However, just accessing the otherEntities collection or even obtaining references to each entity will not make the instance to be loaded; you need to invoke a method on the fetched entity in order to be sure it will be loaded.
You also might consider creating a #NamedQuery and return only the OtherEntity ID's.
HTH!
From hibernate reference, section 2.2.2.1.
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-mapping-property
Declare your columns as lazy initialized:
#Basic(fetch = FetchType.LAZY)
private String getYourProperty() {
}
You also need to disable proxies for your entity class and byte instrument it. There is an example here:
Making a OneToOne-relation lazy
You can use the below HQL as told in the documentation to establish this.
session.createQuery(select new OtherEntity(oe.id) OtherEntity oe
where oe.parentSomeEntity.someId = :someId).list();//also set someId.
Add a constructor in OtherEntity to set the id also there should be a mapping to SomeEntity in OtherEntity.
This HQL will give you a List<OtherEntity> with only id set in the bean.

Categories

Resources