【问题标题】:Spring - execute query before application shutdown (tests)Spring - 在应用程序关闭之前执行查询(测试)
【发布时间】:2019-08-15 23:38:47
【问题描述】:

我正在运行 SpringBoot 2.1,并使用 Sprind Data JPA/Hibernate 作为持久层。在应用程序关闭之前,在我的测试中,我遇到了成功运行查询的问题。

详情:

在应用程序上下文启动期间,我正在通过 JPA 执行查询(假设此查询转换为遵循 SQL "insert into mytable('mycolumn') values ('abc'))。

现在我需要在应用程序关闭之前执行另一个查询。对于给定的示例,这将是 "update mytable set mycolumn = 'xyz' where mycolumn = 'abc'

我设法通过在我的配置类上使用 @PreDestroy 来执行查询

@Configuration
MyConfig {

   @Autowired
   private MyTransactionalService myService;

   @PreDestroy
   public void doQuery() {
      mySerivce.runMyQuery(); 

  }
}

mySerivce.runMyQuery() 委托给myRepository(即 Spring Data JPA 存储库)调用更新查询:

MyRepository extends JpaRepository(String, Something) {

   @Modifying
   @Query("UPDATE myEntity e SET e.myColumn = 'xyz' WHERE e.myColumn = 'abc")' 
   void runMyQuery();
}

使用@PreDestroy 注释的方法会执行,但是当查询由 H2(在我的 spring 测试中运行的内存数据库)执行时,它会抛出异常,说明该表不存在。

问题是该表之前肯定存在,因为我能够在应用程序启动期间对该表执行 INSERT(请参阅帖子的开头)。

我的猜测是 shudtown 进程正在进行中,所以内存数据库被清除了......因此没有表。

是否有办法确保在与数据库的连接仍然正常且尚未删除表的情况下执行查询(在应用程序上下文关闭时)?

【问题讨论】:

  • 为什么需要在数据库销毁之前更新表?
  • 在测试中这被破坏了。在实时系统中,数据库将是持久的。对于真正的数据库(SQL 服务器),我真的需要它......但我希望能够在测试中使用 in-mem db 测试这种行为

标签: spring spring-boot spring-data-jpa shutdown-hook application-shutdown


【解决方案1】:

@Predestroy 按预期工作,只需将 @PreDestroy 注释放在 Application 类中的某个方法上。我在这里创建了一个示例。为了快速测试,我使用 sql 文件来初始化我的数据库,正如here 所描述的那样,您也可以使用它的服务。当我关闭应用程序时,数据库会根据需要进行更新。请尝试:

依赖于:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zpavel</groupId>
    <artifactId>test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
            <version>1.4.199</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
        </dependency>
    </dependencies>
</project>

型号:

@Entity
@Data
public class Foo {
    @Id
    private Long id;
    private String bar;
}

存储库:

public interface FooRepository extends JpaRepository<Foo, Long> {
}

src/main/resources/schema.sql:

DROP TABLE IF EXISTS foo;

CREATE TABLE foo (
  id INT AUTO_INCREMENT  PRIMARY KEY,
  bar VARCHAR(250) NOT NULL
);

src/main/resources/data.sql:

INSERT INTO foo (bar) VALUES ('baz');

src/main/resources/application.properties:

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.datasource.url=jdbc:mariadb://localhost:3306/test?useSSL=false
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.initialization-mode=always

Application.java

@SpringBootApplication
public class Application {  
    @Autowired
    private FooRepository fooRepository;

    // keep main method here

    @PreDestroy
    private void shutdown() {
        fooRepository.deleteAll();
    }
}

【讨论】:

  • 您能否将代码 sn-p 发布为“只需在应用程序类中的某个方法上添加 @PreDestroy 注释”。您粘贴了很多代码,但没有粘贴回答问题的代码。谢谢。
  • 对。忘了它。我会完成我的答案。
  • 对你有用吗?(我放了Application类的代码)
  • 您的示例不使用 imem 数据库,而是使用 MariaDb。使用持久数据库没有问题。另一件事是,在我的情况下,由于模块依赖项的组织方式,不可能在 Application 类中注入 FooService(即 Application 看不到 FooRepository)
【解决方案2】:

我设法通过使用DB_CLOSE_ON_EXIT=FALSE"; init 参数克服了这个问题

String url = "jdbc:h2:~/test;DB_CLOSE_ON_EXIT=FALSE";

在这种情况下,H2 不会杀死数据库并在关闭过程中保持其可用。

【讨论】:

    猜你喜欢
    • 2019-04-08
    • 2019-12-27
    • 1970-01-01
    • 2016-10-13
    • 2020-08-20
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 2019-07-24
    相关资源
    最近更新 更多