为什么在查询对象时 Hibernate 重新更新会导致 Batch update 返回意外的行数?

Why Hibernate re-update when querying objects causes Batch update returned unexpected row count?

我正在编写一个测试 class 来测试我的 DAO (ProjectDao)。数据库中只有一个 table(对于项目)并且在持久性对象中没有关联。

这是我的基础测试class(针对@Before 和@After 方法):

@TransactionConfiguration
@Transactional
public class DaoTestBase extends AbstractTransactionalJUnit4SpringContextTests {

@Autowired
protected ProjectDao projectDao;

@Autowired
protected List<Project> dummyProjects;

@Before
public void initTestData() throws Exception {
    projectDao.deleteAll();

    for (Project project : dummyProjects) {
        projectDao.saveOrUpdate(project);
    }
}

@After
public void clearTestData() throws Exception {
    projectDao.deleteAll();
}
}

这里是实际的测试 class,有 2 种测试方法:

@ContextConfiguration(locations = {
    "classpath*:database/dummy-beans-test.xml",
    "classpath*:springcontext/*test.xml"})
public class ProjectDaoTest extends DaoTestBase {

private static final Comparator<Project> PROJECT_COMPARATOR = 
        new Comparator<Project>() {
    public int compare(Project o1, Project o2) {
        if (o1.getNumber() < o2.getNumber()) {
            return -1;
        } else if (o1.getNumber() == o2.getNumber()) {
            return 0;
        } else {
            return 1;
        }
    };
};

@Test
public void shouldQueryTheSameAsDummyObjects() throws Exception {
    List<Project> projects = projectDao.getAll();
    Assert.assertEquals(dummyProjects.size(), projects.size());

    Set<Project> preProjects = new TreeSet<Project>(PROJECT_COMPARATOR);
    preProjects.addAll(dummyProjects);

    for (Project project : projects) {
        if (preProjects.contains(project)) {
            preProjects.remove(project);
        }
    }

    Assert.assertEquals(0, preProjects.size());
}

@Test
public void deleteSomeProjectsShouldRemoveThem() throws Exception {
    List<Project> projects = projectDao.getAll();
    Assert.assertEquals(3, projects.size());

    for (Project toDeleteProject : projects) {
        projectDao.delete(toDeleteProject);

        List<Project> remainProjects = projectDao.getAll();
        for (Project project : remainProjects) {
            if (project.getNumber() == toDeleteProject.getNumber()) {
                Assert.assertTrue(false);
            }
        }
    }
}
}

问题是当我忽略上面的一种测试方法时,一切都测试正常但是当我测试它们时,它会导致:

Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1

检查日志,我看到在执行第一个测试时,Hibernate 执行这些 SQL:

Hibernate: 
delete 
from
    project_table
Hibernate: 
insert 
into
    project_table
    (pro_customer, pro_end_date, pro_name, pro_number, pro_start_date, pro_status) 
values
    (?, ?, ?, ?, ?, ?)
Hibernate: 
insert 
into
    project_table
    (pro_customer, pro_end_date, pro_name, pro_number, pro_start_date, pro_status) 
values
    (?, ?, ?, ?, ?, ?)
Hibernate: 
insert 
into
    project_table
    (pro_customer, pro_end_date, pro_name, pro_number, pro_start_date, pro_status) 
values
    (?, ?, ?, ?, ?, ?)
Hibernate: 
select
    this_.id as id0_0_,
    this_.pro_customer as pro2_0_0_,
    this_.pro_end_date as pro3_0_0_,
    this_.pro_name as pro4_0_0_,
    this_.pro_number as pro5_0_0_,
    this_.pro_start_date as pro6_0_0_,
    this_.pro_status as pro7_0_0_ 
from
    project_table this_
Hibernate: 
delete 
from
    project_table

并且在执行第二个测试时,他们会:

Hibernate: 
delete 
from
    project_table
Hibernate: 
update
    project_table 
set
    pro_customer=?,
    pro_end_date=?,
    pro_name=?,
    pro_number=?,
    pro_start_date=?,
    pro_status=? 
where
    id=?
Hibernate: 
update
    project_table 
set
    pro_customer=?,
    pro_end_date=?,
    pro_name=?,
    pro_number=?,
    pro_start_date=?,
    pro_status=? 
where
    id=?
Hibernate: 
update
    project_table 
set
    pro_customer=?,
    pro_end_date=?,
    pro_name=?,
    pro_number=?,
    pro_start_date=?,
    pro_status=? 
where
    id=?
Hibernate: 
delete 
from
    project_table

在每次测试中这些 Hibernate 操作之前和之后,我注意到 Hibernate 打开和关闭会话(并回滚事务)。所以我的问题是:

1/为什么在第二个测试的@Before方法中,Hibernate没有插入数据(他们在第一个方法中插入的数据被回滚了?!)。

2/为什么第二种测试方法我只getAll(使用条件)和Hibernate更新了数据?!

3/ 为什么Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1是这种情况?

在 DAO 的实现中,我从 SessionFactory 检索了当前会话,使用条件进行查询。例如。对于 getAll() 方法:

sessionFactory.getCurrentSession()
                .createCriteria(type)
                .list()

如果我理解正确,您的项目实体实际上是来自 Spring 上下文的 Spring bean。默认情况下,Spring 上下文在测试之间共享。

第一个测试使用 saveOrUpdate() 将这些实体附加到会话,从而为它们生成一个 ID。然后从数据库中删除它们。

第二个测试然后使用这些完全相同的项目实例作为参数调用 saveOrUpdate()。由于它们已经有一个 ID,Hibernate 认为它们是分离的项目实例,应该已经存在于数据库中,并使用更新查询来更新它们。但由于它们不再存在于数据库中,因此查询失败。

您不应在测试之间重复使用项目实体实例。在您的 @Before 方法中从头开始创建实例,而不是从 Spring 上下文中获取它们。