org.hibernate.AssertionFailure: Unable to perform

2019-04-11 01:46发布

问题:

I am getting this hibernate assertion error, when i try to make a read after some delete operations.

I couldn't find anything regarding this ' Unable to perform un-delete' error, except the soure code, so i think that, maybe i am doing something so obviously wrong...

The stack trace is below,

AssertionFailure:43 -  - HHH000099: an assertion failure occured (this may indicate a bug in         Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: Unable     to perform un-delete for instance X
org.hibernate.AssertionFailure: Unable to perform un-delete for instance X
   at org.hibernate.engine.spi.ActionQueue.unScheduleDeletion(ActionQueue.java:508)
   at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:157)
   at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
   at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
   at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
   at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
   at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
   at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
   at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
   at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
   at org.hibernate.event.internal.DefaultPersistEventListener.justCascade(DefaultPersistEventListener.java:190)
   at org.hibernate.event.internal.DefaultPersistEventListener.entityIsDeleted(DefaultPersistEventListener.java:229)
   at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:158)
   at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
   at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
   at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
   at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
   at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
   at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
   at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
   at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:423)
   at org.hibernate.event.internal.DefaultPersistEventListener.justCascade(DefaultPersistEventListener.java:190)
   at org.hibernate.event.internal.DefaultPersistEventListener.entityIsPersistent(DefaultPersistEventListener.java:183)
   at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:147)
   at org.hibernate.internal.SessionImpl.firePersistOnFlush(SessionImpl.java:870)
   at org.hibernate.internal.SessionImpl.persistOnFlush(SessionImpl.java:863)
   at org.hibernate.engine.spi.CascadingAction$8.cascade(CascadingAction.java:346)
   at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:380)
   at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323)
   at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
   at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:409)
   at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:350)
   at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:326)
   at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:208)
   at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:165)
   at org.hibernate.event.internal.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:160)
   at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:151)
   at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:88)
   at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
   at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1186)
   at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1241)
   at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
   at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:257)
   at org.hibernate.ejb.criteria.CriteriaQueryCompiler$3.getResultList(CriteriaQueryCompiler.java:254)

Regards,

回答1:

I had this same problem. What happened is that I was deleting related beans, let's call them A and B. I had deleted Bs and then proceeded to find entities C that link to As, and gave bean of type A as parameter to query and making the query caused this Exception. My best guess is that a Cascade annotation on the entity D, referencing both A and B, had deleted the As when I deleted the Bs and then giving an entity that was already deleted on DB level as parameter to query cause the problem.

Rearranging the code so that I first remove the references from C to A and then proceed to delete both B and A seems to work around this.



回答2:

I too faced this same stack trace today, using Hibernate 4.x as a JPA 2.x provider. Here is what I learned about what I needed to change to fix this raised exception.

In my JPA mapped Domain Model, I have a deep OneToMany based graph, A has many Bs, B has many Cs, C has many Ds, D has many Es. The cascade type on all the one to manys is ALL. The back pointing ManyToOne exists at each level and the code we had written worked fine when those ManyToOne relationships were all defaulted (i.e. eagerly loaded)

In the offending use case I diagnosed today, we read a discrete set of Ds with a JPQL query, and as we iterate through that set, we call EntityManager.remove(c), where we get the c instance by asking a D instance for its parent/owner C instance.

If that c instance is obtained by the D to C relationship being lazy loaded, the call to EntityManager.remove(c) does not complain, but a subsequent call to EntityManager.flush() results in the subject Exception.

The subject Exception is NOT raised, if within that JPQL query, the relationship from D to C is eagerly loaded by adding "join fetch d.c c".

So, just thought I would share these results in case anyone else is facing this issue and the above gives them an idea of what they might change to silence the problem.



回答3:

This is for all who come here via a search for the cannot un-delete assertion (like me).

I had the same problem with the Assertion in some code a Contractor left us with but for me the problem wasn't the cascade, that worked fine.

For some reason Hibernate was loading the same entity twice in the same session. I only found this out when digging deep into the Hibernate internals.

I used org.hibernate.stat.SessionStatistics which you can get of the Session and PersistenceContext which you get of the SessionImpl

Session session = (Session)this.entityManager.unwrap(Session.class);
persistenceContext = ((SessionImpl)session).getPersistenceContext();

to find out what Hibernate had loaded.

When I printed out the complete list of entities with

public  void printSessionInfo(String where,HashSet<String> printClasses){
        Log log = Logging.getLog(PersistentSesionInfo.class);
        Session session = (Session)this.entityManager.unwrap(Session.class);

        SessionStatistics stats = session.getStatistics();
        HashMap<String,Counter> entityMap = new HashMap<String,Counter>();
        if(stats!=null){
            log.info("EntityManager #0 has #1 managed entities and #2 collections at #3", getHash(entityManager),stats.getEntityCount(),stats.getCollectionCount(),where);
            List<EntityKey> entities = com.google.common.collect.Lists.newArrayList(stats.getEntityKeys());
            Iterator<EntityKey> iter = entities.iterator();
            while(iter.hasNext()){
                EntityKey ek = iter.next();
                if(entityMap.containsKey(ek.getEntityName())){
                    entityMap.get(ek.getEntityName()).increase();
                }else{
                    entityMap.put(ek.getEntityName(), new Counter());
                }
                if(printClasses!=null && printClasses.contains(ek.getEntityName())){
                    try{
                    Object o = ((HibernateSessionProxy)session).get(ek.getEntityName(), ek.getIdentifier());
                    if(o!=null){
                        log.info("Entity #0 of type #1  id=#2 System hash=#3 object hash #4", o,Hibernate.getClass(o).getSimpleName(),ek.getIdentifier(),Integer.toHexString(System.identityHashCode(o)),Integer.toHexString(o.hashCode()));
                    }
                    }catch(javax.persistence.EntityNotFoundException e ){
                        log.error("Entity #0 with id #1 cannot be found anymore", ek.getEntityName(),ek.getIdentifier());
                    }
                }
            }
            for(Entry<String,Counter> entry : entityMap.entrySet()){
                log.info("Entity #0 count #1", entry.getKey(),entry.getValue());
            }

        }
    }
public class Counter {
        int count = 1;

        public void increase(){
            count++;
        }

        public int getCount(){
            return count;
        }

        public String toString(){
            return String.valueOf(count);
        }
    }

My Entities were listed there but when I used

persistenceContext.getEntry(myEntity)

it returned null (entityManager.contains(myEntity) returned true)!

I iterated over all EntityKeys from persistenceContext.getEntitiesByKey() (you might need to copy the list before iterating over it)

List<EntityKey> list = new ArrayList<EntityKey>();
list.addAll(persistenceContext.getEntitiesByKey().keySet());

In there the EntityKey for myEntity was there but it was pointing to a different instance (different System.identiyHashCode).

I must assume that the problem ultimately was with the equals and hashCode methods of the entity (as so often with entity problems). I noticed that similar entity instances would have the same hashCode even though their parent was different.

I've used 'must assume' as the problem didn't go away straight away (didn't restart the server as I am using liverebel). Only after restarting the server with TRACE enabled on Hibernate was the problem gone. After rolling back all other debug changes and only leaving the equals/hashCode methods it was still working.

So whenever you have strange unexplainable problems with your entities - check the equals and hashCode methods! And use the Hibernate classes PersistenceContext and SessionStatistics to debug it.



回答4:

I know this is really late answer but this might help someone, sometime in future!! This answer is about similar error (if someone lands on this question, luckily): AssertionFailure - HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)

I was getting this error because I was trying to save the session with entity whose not-null attribute-a primary key was set to null.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "calc_id", unique = true, nullable = false)
private Long calcId;

here GenerationType.IDENTITY was not working and not setting the primary key value before save. And Tadaa.... I got the same error as I mentioned. So I just replaced the code with the following.

@SequenceGenerator(name="identifier", sequenceName="mytable_id_seq", allocationSize=1)  
@GeneratedValua(strategy=GenerationType.SEQUENCE, generator="identifier")


回答5:

it might be a bug of hibernate, If you read (load() a proxy instance) and then call delete and save on it . https://hibernate.atlassian.net/browse/HHH-8374



回答6:

I had the same error. I had the classes A and B. B has a list of As and A has a reference back to B. A has also a reference to object C (Cascade.All) which causes the exception on undeletion.

I tried to relocate an object of A to another object of B.

This causes the exception:

objectOfA.getObjectOfB().getListOfAs().remove(objectOfA);
objectOfB1.getListOfAs().add(objectOfA);
objectOfA.seObjectOfB(objectOfB1);

dao.flush()

This was the working solution.

objectOfA.seObjectOfB(objectOfB1);

dao.flush()

bjectOfA.getObjectOfB().getListOfAs().remove(objectOfA);
objectOfB1.getListOfAs().add(objectOfA);

The last two lines are not always mandatory because hibernate corrects this after a reload.

I'm not shure if its a bug in hibernate. The exception occoured because hibernate compares the object C with a proxy of the same C with "==" in ActionQueue.unScheduleDeletion()



回答7:

I've also suffered by the problem once. In the case of using bidirectional mapping when we use multiple @OneToMany and use mappedby annotatin and then the server show such kind of error. So you can easily solve the problem by using

@JoinTable(name="c",joinColumns=@JoinColumn(name="c_id"),inverseJoinColumns=@JoinColumn(name="l_id"))

instead of using mappedby..



回答8:

I used cascade = CascadeType.ALL and it worked, suppose you are getting it on entity B which is reffered from entity A. Please put cascade all on A,

Also depends who is keeping the relation in my case the relation was mapped by A.



回答9:

My two cents, for those arriving at this thread and using Grails.

In mi case, the problem came from changing a domain persistent property into a transient one (providing specific setter and getter), and having a constructor using the map version and passing the variable there.

The original domain class was like this:

class MyDomain {
    String content
}

And I was creating an instance like:

def domain = new MyDomain( content: "sample content" )

Updated domain:

class MyDomain {
    static transients = [ 'content' ]

    byte[] compressed

    String getContent() {
        compressed ? uncompress( compressed ) : null
    }

    void setContent( String content ) {
        compressed = content ? compress( content ) : null 
    }
}

The constructor would not complain, but setContent is not called, and strangely instead of giving a validation error or an SQL error, it raises the error mentioned in this thread.

The solution is simple:

def domain = new MyDomain()
domain.content = "sample content"

That way the setter gets called.



回答10:

remove the methods hashCode and equals from your Entity . it's worked for me