hibernate会话中的持久化数据变得陈旧,有时会通过休眠生成重复ID [英] The persisted data in hibernate session becomes stale and sometimes duplicate ID gets generated by hibernate

查看:117
本文介绍了hibernate会话中的持久化数据变得陈旧,有时会通过休眠生成重复ID的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为我的应用程序使用hibernate。有时侯hibernate会生成重复的ID,我使用的策略是增量,它会从表中增加一个max(ID)。我不知道为什么会发生这种情况,并且突然发生,并且之后对该表插入操作的所有后续请求都失败。解决方法是重新启动tomact服务器。



这里是应用程序的详细信息

1。部署在liferay-tomcat.each上的3个应用程序具有自己的抽象impls.jar。此JAR包含使用Hibernate与Oracle进行交互的类。我可以怀疑这是引发这个问题的一种可能的情况。

2。所有3个应用程序都与hibernate相关JAR's



我做了以下测试以确认是否由于多线程,
1)导致的问题创建了3个线程,其中每个线程运行一个for循环20次插入到同一个表>我没有发现任何问题。一切顺利。



以下是上述示例代码。

  public class ThreadTest implements Runnable {
@Override
public void run(){
// TODO自动生成方法存根
(int i = 0; i< 20; i ++){
UserManagement userManagement = new UserManagementImpl(-1);
上下文context = new Context();
context.setUserId(testUser);
context.getUserContextMap()。put(password,shiva123);
尝试{
int sessionId = userManagement.login(context);
System.out.println(thread<+ Thread.currentThread()。getName()+>>+ sessionId);
} catch(Exception e){
e.printStackTrace();




$ b public static void main(String [] args){
Thread t1 = new Thread(new ThreadTest ());
线程t2 =新线程(new ThreadTest());
线程t3 =新线程(new ThreadTest());
t1.setName(t1);
t2.setName(t2);
t3.setName(t3);
t1.start();
t2.start();
t3.start();
}

}

如何获得正确的ID是问题或如何避免生成的重复ID?



这是给我带来问题的代码。 / p>

以下是hibernate映射类。

  import javax.persistence 。柱; 
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实体{

/ **
*此类的串行版本UID
* /
private static final long serialVersionUID = -9214466697134157251L;
@Override
public String toString(){
returnEntityImpl [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;
}
/ **设置此实例的实体ID * /
public void setEntityId(int entityId){
this.entityId = entityId;

/ **检索此实体实例的名称* /
@Column(name =entity_name,nullable = false)
public String getEntityName(){
返回entityName;
}
/ **设置此实体实例的名称* /
public void setEntityName(String entityName){
this.entityName = entityName;
}
/ **检索此实体实例的类型* /
@Column(name =entity_type,nullable = false,updatable = false)
public String getEntityType ){
return entityType;
}
/ **设置此实体实例的类型* /
public void setEntityType(String entityType){
this.entityType = entityType;

**检索此实例的父实体ID * /
@Column(name =parent_id)
public int getParentEntityId(){
return parentEntityId;
}
/ **设置此实例的父实体ID * /
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;
}

}

下面是保存实体的方法

  private boolean save(Serializable object,Session session)throws EntityNotFoundException,InternalSystemException {
logger.debug(save() - start);
Transaction tx = null;
布尔成功= false;
boolean localInit = false;
尝试{
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();
}
成功= true;
} catch(HibernateException ex){
logger.error(保存实体出错+ ex);
if(localInit == true&& tx!= null)
tx.rollback();
ex.printStackTrace();
抛出新的InternalSystemException(保存实体时出错+ ex);
} catch(Exception ex){
logger.error(save in entity+ ex);
if(localInit == true&& tx!= null)
tx.rollback();
new InternalSystemException(保存实体出错+ ex);
} finally {
if(localInit&& session!= null){
session.flush();
session.clear();
session.close(); ();
}
}
if(logger.isDebugEnabled()){
logger.debug(save() - end);
}
返回成功;
}

错误不会总是发生,但一旦发生,同样会继续和经过测试的Oracle环境是Oracle RACK。

解决方案

在这种情况下不应该使用增量策略。它包括将最大值加载到内存中的计数器中,然后在每次询问新的ID时递增。由于您已部署了3个应用程序,因此您有3个此类计数器,每个计数器初始化为相同的初始值,因此它们将返回相同的ID。



显然有文件记录


increment

生成long,short或int类型的唯一标识符,只有在没有其他进程正在插入数据时才是唯一的进入同一张桌子。
不在群集中使用


(强调不是我的)



序列发生器不会出现同样的问题。如果确实如此,那么要么是在配置中丢失了某些内容,要么是三个应用程序中的一个继续使用增量策略,或者从外部插入ID,或者序列在数据库中的初始值是错误的。没有看到代码,ID的最大值和序列的定义,这很难说。


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.

解决方案

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.

这篇关于hibernate会话中的持久化数据变得陈旧,有时会通过休眠生成重复ID的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆