使用Hibernate截断所有表的最佳方法是什么?

6

我希望能在集成测试之间截断所有数据库表。使用Hibernate,最好的方法是什么?

目前,我正在执行以下操作:

public void cleanDatabase() {
        doWithSession(new Action1<Session>() {
            @Override
            public void doSomething(Session session) {
                SQLQuery query = session.createSQLQuery("truncate table stuff");
                // todo - generify this to all tables
                query.executeUpdate();
            }
        });

(doWithSession是一个小包装器,用于创建和关闭会话)。我可以使用反射迭代所有映射的对象...我想知道是否已经有人解决了这个问题。

5个回答

4
我猜你可能不使用Spring。如果使用,Spring的事务测试支持 将是理想的选择。
简而言之:Spring会在每个测试用例之前自动启动事务,然后在测试用例之后自动回滚事务,以留下一个空数据库(或至少是未更改的数据库)。
也许你可以模仿这种机制:
@Before方法中开启事务,在@After方法中回滚事务。

但是...但是...我想在数据库上断言一些东西。为此,我需要实际提交东西到它。 - ripper234
3
@ripper234 只要在交易范围内进行,你就可以完美地断言一些东西。 - Qwerky
@Qwerky - 嗯,听起来是一个有趣的方法。我会试一试,谢谢。 - ripper234

1
我为此编写了一个整合器。基本上,我们会在会话工厂创建流程中挂钩,迭代Hibernate找到的表映射,然后对每个表执行TRUNCATE TABLE xxx操作。由于我们无法截断带有外键约束的表,因此在截断操作之前禁用外键检查,然后重新启用。
static final class TruncatorIntegrator implements org.hibernate.integrator.spi.Integrator {

    @Override
    public void integrate(Metadata metadata,
                          SessionFactoryImplementor sessionFactory,
                          SessionFactoryServiceRegistry serviceRegistry) {
        try (Session session = sessionFactory.openSession()) {
            session.doWork(connection -> {
                try (PreparedStatement preparedStatement = connection.prepareStatement("SET FOREIGN_KEY_CHECKS = 0;")) {
                    preparedStatement.executeUpdate();
                    System.out.printf("Disabled foreign key checks%n");
                } catch (SQLException e) {
                    System.err.printf("Cannot disable foreign key checks: %s: %s%n", e, e.getCause());
                }

                metadata.collectTableMappings().forEach(table -> {
                    String tableName = table.getQuotedName();
                    try (PreparedStatement preparedStatement = connection.prepareStatement("TRUNCATE TABLE " + tableName)) {
                        preparedStatement.executeUpdate();
                        System.out.printf("Truncated table: %s%n", tableName);
                    } catch (SQLException e) {
                        System.err.printf("Couldn't truncate table %s: %s: %s%n", tableName, e, e.getCause());
                    }
                });

                try (PreparedStatement preparedStatement = connection.prepareStatement("SET FOREIGN_KEY_CHECKS = 1;")) {
                    preparedStatement.executeUpdate();
                    System.out.printf("Enabled foreign key checks%n");
                } catch (SQLException e) {
                    System.err.printf("Cannot enable foreign key checks: %s: %s%n", e, e.getCause());
                }
            });
        }
    }

    @Override
    public void disintegrate(SessionFactoryImplementor sessionFactory,
                             SessionFactoryServiceRegistry serviceRegistry) {
    }
}
用法:我们必须在会话工厂创建流程中使用这个Integrator,并且每个测试都需要创建一个新的会话工厂。
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder().applyIntegrator(new TruncatorIntegrator()).build();
StandardServiceRegistry registry = new StandardServiceRegistryBuilder(bootstrapServiceRegistry).build();
SessionFactory sessionFactory = new Configuration().buildSessionFactory(registry);

0

0

你可以使用 SchemaExport放弃并重新创建Hibernate模式,尽管这似乎是很过度的方式。回滚事务听起来更好。


0
您可以使用内存数据库,在测试之间删除整个数据库。
如果您的测试不是很多但很长,可以使用这种方法。
但请注意,每个数据库的行为都与其他数据库略有不同。因此,在某些情况下,使用内存数据库(例如HyperSQL)的行为不会完全像您的普通数据库-因此它不是正确的集成测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接