使用hibernate通过多个线程保存多个实体

时间:2012-08-27 15:00:59

标签: java database multithreading hibernate

我想通过多个线程使用hibernate保存新实体。

OBS:我无法在客户端上使用批处理。

所以我做了这个结构:

+-----------------+
|+------------------+
||+------------------+     +-----------------------------------------------+
|||                  |     | Each thread do these steps:                   |
||| Multiple Threads |     |  - get EntityManagerFactory                   |
|||                  |---> |  - create Entity Manager                      |
||| Creating new     |     |  - begin new transaction                      |
+||    Entities on   |     |  - create new entity (with autoincrement id)  |
 +|        Hibernate |     |  - persist                                    |
  +------------------+     |  - commit the transaction                     |
                           |  - close the entity manager                   |
                           +-----------------------------------------------+

实体:

@Entity
@Table(name="EN_TEST")
public class EnTest {
    private long id;

    private String name;

    public EnTest() {
    }

    @Id
    @GeneratedValue(generator="increment")
    @GenericGenerator(name="increment", strategy = "increment")
    @Column(name = "ID")
    public long getId() {
        return id;
    }

    @SuppressWarnings("unused")
    private void setId(long id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Column(length = 20)
    public String getName() {
        return name;
    }
}

java stuff(DBHandler):

// just to get the session factory
public class DBHandler {
    private static DBHandler dbHandler;

    private static EntityManager entityManager;

    public static DBHandler get() {
        if (dbHandler == null)  {
            dbHandler = new DBHandler();
        }

        return dbHandler;
    }

    private DBHandler() {
        try {
            this.sessionFactory = Persistence.createEntityManagerFactory("a_persistence_unit");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private EntityManagerFactory sessionFactory;

    public EntityManagerFactory getSessionFactory() {
        return sessionFactory;
    }
}

java stuff(创建过程):

public class CreateTest {

    // this is accessed by multiple threads to create new entities
    public static void crateNewEntityTest(String name) {
        EntityManagerFactory factory = DBHandler.get().getSessionFactory();

        EntityManager entityManager = factory.createEntityManager();

        entityManager.getTransaction().begin();

        EnTest newEnTest = new EnTest();
        newEnTest.setName(name);

        entityManager.persist(newEnTest);

        entityManager.getTransaction().commit();
        entityManager.close();
    }

       // The creation of threads and saving, this is just an example:
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread() {
                public void run() {
                    CreateTest.createNewEntityTest("Name_" + i);
                };
            }.start();
        }
    }
}

错误:

Caused by: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1389)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1317)
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:81)
    ... 2 more
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:268)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:76)
    ... 2 more
Caused by: java.sql.BatchUpdateException: Duplicate entry '3' for key 'PRIMARY'
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1666)
    at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1082)
    at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
    at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
    ... 10 more

hibernate抱怨我的实体自动增量!

我应该采取什么策略?

我必须实现线程池或其他东西来处理多线程实体保存吗?

这个线程问题不是一个休眠任务吗?

2 个答案:

答案 0 :(得分:2)

我找不到明显的错误。

建议:

  1. 文档说“不要在群集环境中使用increment”。这似乎不是你的情况,但要确保你只运行一个进程。同一进程中的几个线程应该没问题。

  2. 使用其他生成器。对于MySQL,identity是一个很好的候选者,因为数据库会给你一个ID。请注意,您还需要为列提供正确的类型。

答案 1 :(得分:0)

使用身份(或序列取决于DB术语)或GUID或您独特的自然商业身份。