在春天穿线

时间:2013-11-02 23:18:09

标签: multithreading spring hibernate session caching

我正在尝试在我的代码中进行一些优化,并希望生成一个我执行耗时操作的线程。在优化的实施过程中,我遇到了一个令我疯狂的问题。我简化了问题并为该特定问题创建了一个测试用例:(我正在使用SpringJUnit4ClassRunner,因此事务在testCRUD方法的开头正确启动)

有人可以帮我理解为什么线程中的foundParent为null吗?

private Semaphore sema = new Semaphore(0, false);
private long parentId;

@Test
public void testCRUD() {
    //create
    DBParent parent = null;
    {
        parent = new DBParent();
        parentDao.persist(parent);
        parentId = parent.getId();
        assertTrue(parentId > 0);

        parentDao.flush();
    }

    (new Thread(
        new Runnable() {
            public void run() 
            {
                System.out.println("Start adding childs !");
                DBParent foundParent = parentDao.findById(parentId);
                assertTrue(foundParent != null); //ASSERTION FAILS HERE !!!!

                System.out.println("Releasing semaphore !");
                sema.release();
                System.out.println("End adding childs !");
            }
    })).start();

    try {
        System.out.println("Acquiring semaphore !");
        sema.acquire();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

============================= EDITED ================= ================== 根据一条评论建议,我创建了一个生成线程的threadManager bean。这是threadManager的代码:

public class ThreadManager {
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void executeTask(String Name, Runnable task) {
        (new Thread(task, Name)).start();
    }
 }

然后在上一个测试中,我不是手动启动线程,而是将其发布到线程管理器中,如下所示:

@Autowired private ParentDao parentDao;
@Autowired private ThreadManager threadManager;

private Semaphore sema = new Semaphore(0, false);
private long parentId;

@Test
public void testCRUD() {
    //create
    DBParent parent = null;
    {
        parent = new DBParent();
        parentDao.persist(parent);
        parentId = parent.getId();
        assertTrue(parentId > 0);

        parentDao.flush();
    }

    threadManager.executeTask("BG processing...",
        new Runnable() {
            public void run() 
            {
                System.out.println("Start adding childs !");
                DBParent foundParent = parentDao.findById(parentId);
                assertTrue(foundParent != null); //ASSERTION FAILS HERE !!!!

                System.out.println("Releasing semaphore !");
                sema.release();
                System.out.println("End adding childs !");
            }
    });

    try {
        System.out.println("Acquiring semaphore !");
        sema.acquire();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

不幸的是,这也不起作用! : - (

2 个答案:

答案 0 :(得分:4)

事务上下文绑定到线程。因此,生成的线程中的代码不会在与初始线程中的代码相同的事务上下文中运行。因此,由于事务隔离(ACID中的I),生成的线程无法看到初始线程的事务在数据库中插入的内容。

答案 1 :(得分:0)

可以将Spring事务绑定到新线程,以运行事务& Hibernate / JPA访问它。但这必须是与其他线程不同的TX和JPA / HB会话。

OpenSessionInViewFilter的Spring代码是如何将Hibernate会话绑定到Spring的TX管理的合理示例。你可以把它剥离到相当少的代码。

请参阅:

  • org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
  • OpenSessionInViewFilter.doFilterInternal() - 这是它实际绑定它的地方
  • TransactionSynchronizationManager.bindResource()
  • TransactionSynchronizationManager.unbindResource()
  • TransactionSynchronizationManager.getResource()

在一个项目(IIRC)中,我将此功能包装到'ServerThreadHb'类中,以设置&在构造上保存先前的线程绑定 - 使用restore()块中的finally方法来恢复以前的绑定。

对于您发布的代码示例,在单独的线程上运行工作没有多大意义 - 因为您同步等待工作完成。但是我假设你打算删除那个约束&扩展该功能。