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

时间:2010-11-26 13:22:38

标签: java hibernate integration-testing

我想在一个集成测试与另一个集成测试之间截断所有数据库表。使用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 个答案:

答案 0 :(得分:3)

我猜你可能不会使用Spring。如果你这样做,Spring's transactional test support将是理想的。

简而言之:Spring会在每个测试用例之前自动启动一个事务,并在测试用例之后自动回滚,从而为您留下一个空的(或至少不变的)数据库。

也许你可以模仿这种机制:

使用@Before方法打开一个事务,然后使用@After方法回滚。

答案 1 :(得分:0)

你在内存mysql http://dev.mysql.com/downloads/connector/mxj/看了吗?每次测试后都不可能回滚吗?我相信你可以像这样配置它。

答案 2 :(得分:0)

您可以使用SchemaExport删除并重新创建Hibernate架构,尽管这看起来非常严厉。回滚交易听起来更好。

答案 3 :(得分:0)

您可以使用内存数据库并在测试之间删除完整的数据库。

如果没有那么多但是很长的测试,你可以使用这种方式。

但请注意,每个数据库的行为都与其他数据库略有不同。因此,在某些情况下,使用内存数据库(例如HyperSQL)的行为与普通数据库完全不同 - 因此它不是正确的集成测试。

答案 4 :(得分:0)

我正是为此目的而写了一个积分器。基本上,我们进入了会话工厂创建流程,遍历了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);