org.hibernate.AssertionFailure: Unable to perform

2019-04-11 01:06发布

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,

10条回答
我欲成王,谁敢阻挡
2楼-- · 2019-04-11 01:44

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.

查看更多
爷、活的狠高调
3楼-- · 2019-04-11 01:46

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.

查看更多
forever°为你锁心
4楼-- · 2019-04-11 01:47

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.

查看更多
We Are One
5楼-- · 2019-04-11 01:49

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

查看更多
Anthone
6楼-- · 2019-04-11 01:57

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.

查看更多
Root(大扎)
7楼-- · 2019-04-11 02:00

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..

查看更多
登录 后发表回答