【问题标题】:How can I configure Spring Boot, Spring JPA, Spring Test, and Hibernate to create, use, and drop a given PostgreSQL schema?如何配置 Spring Boot、Spring JPA、Spring Test 和 Hibernate 来创建、使用和删除给定的 PostgreSQL 模式?
【发布时间】:2016-01-31 06:03:29
【问题描述】:

如何配置 Spring BootSpring Data JPASpring TestHibernate 以创建、使用和删除给定的 PostgreSQL 架构以用于保存和检索对象的单元测试?

在测试开始之前,Spring Test 应该为测试创建数据库模式。每个测试方法都应该在单个事务中运行,完成后,测试方法应该回滚所有数据库操作。在所有测试方法结束时,测试应该删除架构。

在其当前形式中,AccountRepositoryTest 通过,但在架构 public 中创建表 account,而不是在新架构 springboot 中创建表 account

配置:

  • Spring Boot 1.3.0.BUILD-SNAPSHOT
  • Spring Data JPA 1.9.0.RELEASE
  • 春季测试 4.2.3.BUILD-SNAPSHOT
  • 休眠 4.3.11.Final
  • PostgreSQL 9.5.4

AccountRepositoryTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class AccountRepositoryTest {
    final static Logger logger = LoggerFactory.getLogger(AccountRepositoryTest.class);

    @Autowired
    AccountRepository accountRepository;

    @Test
    public void testSaveAccount() {
        final Account newAccount = accountRepository.save(new Account("123", "Derek Mahar", 500.00));
        final Account readAccount = accountRepository.findOne(newAccount.getId());
        logger.info("New account UUID={}", newAccount.getId());
        assertEquals(newAccount.getBalance(), readAccount.getBalance(), 0.001);
        assertEquals(newAccount.getNumber(), readAccount.getNumber());
        assertEquals(newAccount.getOwner(), readAccount.getOwner());
    }

    @Test
    public void testFindByNumber() {
        final Account newAccount = accountRepository.save(new Account("456", "Steve Balmer", 500.00));
        final Account readAccount = accountRepository.findByNumber(newAccount.getNumber());
        logger.info("New account UUID={}", newAccount.getId());
        assertEquals(newAccount.getBalance(), readAccount.getBalance(), 0.001);
        assertEquals(newAccount.getNumber(), readAccount.getNumber());
        assertEquals(newAccount.getOwner(), readAccount.getOwner());
    }

    @Test
    public void testFindByOwner() {
        final Account newAccount = accountRepository.save(new Account("789", "Bill Gates", 500.00));
        final Account readAccount = accountRepository.findByNumber(newAccount.getNumber());
        logger.info("New account UUID={}", newAccount.getId());
        assertEquals(newAccount.getBalance(), readAccount.getBalance(), 0.001);
        assertEquals(newAccount.getNumber(), readAccount.getNumber());
        assertEquals(newAccount.getOwner(), readAccount.getOwner());
    }
}

AccountRepository.java

public interface AccountRepository extends CrudRepository<Account, UUID> {
    Account findByNumber(String number);
    Account findByOwner(String owner);
}

Account.java

@Entity
public class Account implements Serializable {
    @Column(nullable = false)
    private double balance;

    @Id
    @GeneratedValue(generator = "uuid2")
    @GenericGenerator(name = "uuid2", strategy = "uuid2")
    @Column
    private UUID id;

    @Column(nullable = false)
    private String number;

    @Column(nullable = false)
    private String owner;

    public Account(String number, String owner, double balance) {
        this.number = number;
        this.owner = owner;
        this.balance = balance;
    }

    public Account() {
    }

    public double getBalance() {
        return balance;
    }

    public UUID getId() {
        return id;
    }

    public String getNumber() {
        return number;
    }

    public String getOwner() {
        return owner;
    }
}

application.properties

spring.datasource.url=jdbc:postgresql:springboot
spring.datasource.username=springboot
spring.datasource.password=springboot
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.schema=springboot
spring.datasource.initialize=true

spring.jpa.database=springboot
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

【问题讨论】:

  • 我了解到,将注解 @Transactional(参见 docs.spring.io/spring/docs/current/spring-framework-reference/…)应用于测试类会指示 Spring 在事务中运行每个测试方法并回滚测试执行的所有数据库操作。但是,我还没有确定如何配置 Spring 来创建一个单独的模式来包含测试创建的数据库工件。
  • CREATE SCHEMA springboot 添加到schema.sql(请参阅docs.spring.io/spring-boot/docs/current/reference/html/…)创建了架构springboot,但在单元测试完成时并未删除架构。因此,测试在下次运行时失败,因为架构 springboot 仍然存在。测试完成后如何强制 Spring 删除模式?
  • 理想情况下,我希望将CREATE SCHEMA 包含在与测试相同的事务中,以便在测试结束时回滚期间丢弃整个架构。

标签: java hibernate postgresql spring-boot spring-data-jpa


【解决方案1】:

最好的办法是使用 spring.jpa.hibernate.ddl-auto Spring Boot 属性。将其包含在application-test.properties(或application-test.yaml,视情况而定)中,并将其值设置为create-drop(例如spring.jpa.hibernate.ddl-auto=create-drop。更多详细信息请参见Spring Boot documentation

但是,我建议使用内存数据库来运行测试,因为您不会冒将测试指向实际数据库模式的风险,测试会运行得更快,并且您将能够运行持续集成构建在 Travis-CI 或 Shippable 等第三方系统上。

【讨论】:

  • application.properties 中已经拥有属性spring.jpa.hibernate.ddl-auto(请参阅问题)。
  • 在使用 PostgreSQL 之前,我使用 HSQLDB 和 MySQL 成功地“解决”了这个问题。正如您所提到的,当使用内存数据库 HSQLDB 时,在测试结束时,Spring Boot 和/或 Spring Data JPA 会自动删除数据库模式,包括模式。我使用 MySQL 观察到类似的行为。不过,PostgreSQL 的行为并非如此。
  • 我又看了几遍你的帖子,发现你的配置是正确的。我没有看到我拥有的应用程序存在这个问题。我经常在 Heroku 上使用 Postgres,并且在使用自定义模式的测试中根本没有看到这个问题。不幸的是,我只使用内存数据库进行开发和测试,所以我无法在本地验证您的配置。一旦我有时间,我会用我的 Heroku 应用程序尝试一下你的示例代码。
  • 当我提交“真实”测试时,我可能还会使用内存数据库 HSQLDB,除非它不支持 UUID 列类型。
  • 刚刚确认我的测试方法在使用 HSQLDB 而不是 PostgreSQL 时通过了,因此 HSQLDB 确实可以使用 UUID 列类型和生成器。
猜你喜欢
  • 2018-06-02
  • 1970-01-01
  • 2020-03-16
  • 2017-03-28
  • 2020-04-17
  • 2018-09-17
  • 2019-11-01
  • 2016-05-21
  • 2014-02-09
相关资源
最近更新 更多