The persisted data in hibernate session becomes stale and sometimes duplicate ID gets generated by hibernate

痴心易碎 提交于 2020-01-04 02:43:48

问题


I am using hibernate for my application. Sometimes hibernate generates duplicate ID and the strategy i have used is increment which does max(ID) from table and adds one. I have no clue why it happens and it happens all of sudden and after that all the subsequent requests for that table insert operations fail. the workaround for this is to restart the tomact server.

here are the details of application

1. 3 applications deployed on liferay-tomcat.each has its own abstraction-impls.jar.This JAR contains classes for interacting with oracle using Hibernate. I could suspect this is the one possible case where lead this issue.

2. all the 3 applications has hibernate related JAR's

I have made the following test to confirm whether the issue lead because of multithread, 1) created a 3 threads, where each thread runs a for loop for 20 times to insert into same table> i did not find any issue with this. everything went well.

Here is the sample code for above.

public class ThreadTest implements Runnable{
@Override
public void run() {
    // TODO Auto-generated method stub
    for(int i=0;i<20;i++){
        UserManagement userManagement = new UserManagementImpl(-1);
        Context context = new Context();
        context.setUserId("testUser");
        context.getUserContextMap().put("password", "shiva123");
        try{
        int sessionId = userManagement.login(context);
        System.out.println("thread<<"+Thread.currentThread().getName() + ">>    "+sessionId);
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

public static void main(String[] args) {
    Thread t1 = new Thread(new ThreadTest());
    Thread t2 = new Thread(new ThreadTest());
    Thread t3 = new Thread(new ThreadTest());
    t1.setName("t1");
    t2.setName("t2");
    t3.setName("t3");
    t1.start();
    t2.start();
    t3.start();
}

}

How to get correct ID is the question or how to avoid generated duplicate IDs?

Here is the code which is giving problem to me.

The following is the hibernate mapping class.

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;



@javax.persistence.Entity
@Table(name="entities",
uniqueConstraints = {@UniqueConstraint(columnNames={"id"})})
@org.hibernate.annotations.GenericGenerator(
    name = "test-increment-strategy",
    strategy = "increment")
public class EntityImpl extends Entity {

/**
 * Serial Version UID for this class
 */
private static final long serialVersionUID = -9214466697134157251L;
@Override
public String toString() {
    return "EntityImpl [entityId=" + entityId + ", entityName="
            + entityName + ", entityType=" + entityType
            + ", parentEntityId=" + parentEntityId + ", entityHierarchy="
            + entityHierarchy + "]";
}



private int entityId;
private String entityName;
private String entityType;
private int parentEntityId;
private String entityHierarchy;


@Id
    @GeneratedValue(generator = "test-increment-strategy")
@Column(name = "ID", nullable = false, updatable = false)
public int getEntityId() {
    return entityId;
}
/** Sets the Entity ID for this instance */
public void setEntityId(int entityId) {
    this.entityId = entityId;
}
/** Retrieves the name of this Entity instance */
@Column(name = "entity_name", nullable = false)
public String getEntityName() {
    return entityName;
}
/** Sets the name of this Entity instance */
public void setEntityName(String entityName) {
    this.entityName = entityName;
}
/** Retrieves the type of this Entity instance */
@Column(name = "entity_type", nullable = false, updatable = false)
public String getEntityType() {
    return entityType;
}
/** Sets the type of this Entity instance */
public void setEntityType(String entityType) {
    this.entityType = entityType;
}
/** Retrieves the Parent Entity ID for this instance */
@Column(name = "parent_id")
public int getParentEntityId() {
    return parentEntityId;
}
/** Sets the Parent Entity ID for this instance */
public void setParentEntityId(int parentEntityId) {
    this.parentEntityId = parentEntityId;
}

@Column(name = "ENTITY_HIERARCHY", nullable = false, updatable = false)
public String getEntityHierarchy() {
    return entityHierarchy;
}


public void setEntityHierarchy(String entityHierarchy) {
    this.entityHierarchy = entityHierarchy;
}

}

The below is the method to save the entity

private boolean save (Serializable object, Session session) throws EntityNotFoundException, InternalSystemException {
    logger.debug("save() - start"); 
    Transaction tx = null;
    boolean successful = false;
    boolean localInit = false;
    try {
        if (session == null) {
            session = createSession();
            tx = session.beginTransaction();
            //session.save(object);
            localInit = true;
        }
        session.save(object);
        logger.debug("**********************************");
        logger.debug("object: "+object);
        logger.debug("**********************************");
        if (localInit == true) {
            tx.commit();
        }
        successful = true;
    } catch(HibernateException ex) {
        logger.error("error in saving entity "+ ex);
        if (localInit == true && tx !=null)
            tx.rollback();
        ex.printStackTrace();
        throw new InternalSystemException("error in saving entity "+ ex);
    } catch(Exception ex) {
        logger.error("error in saving entity "+ex);
        if (localInit == true && tx !=null)
            tx.rollback();
        new InternalSystemException("error in saving entity "+ ex);
    }finally {
        if (localInit && session != null) {
            session.flush();
            session.clear();
            session.close();
        }
    }
    if (logger.isDebugEnabled()) {
        logger.debug("save() - end"); 
    }
    return successful;
}

The error will not occur always, but once it happens, the same will be continued and the tested oracle environment is Oracle RACK.


回答1:


The increment strategy should not be used in such a situation. It consists in loading the max value in a counter in memory, and then incrementing it each time a new ID is asked for. Since you have 3 applicarions deployed, you have 3 such counters, each initialized to the same initial value, and they will thus return the same IDs.

This is obviously documented:

increment

generates identifiers of type long, short or int that are unique only when no other process is inserting data into the same table. Do not use in a cluster.

(emphasis NOT mine)

A sequence generator would not exhibit the same problem. If it does, then either you're missing something in the configuration, or one of the three apps continues using an increment strategy, or you're inserting IDs from the outside, or the initial value of the sequence in the database is wrong. Without seeing the code, the max value of the ID, and the definition of the sequence, it's hard to say.



来源:https://stackoverflow.com/questions/14021112/the-persisted-data-in-hibernate-session-becomes-stale-and-sometimes-duplicate-id

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!