SpringBoot项目预加载数据——ApplicationRunnerCommandLineRunnerInitializingBean @PostConstruct区别

Posted kakarotto-chen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot项目预加载数据——ApplicationRunnerCommandLineRunnerInitializingBean @PostConstruct区别相关的知识,希望对你有一定的参考价值。

0、参考、业务需求

1、方式

  • 实现 ApplicationRunner 接口
  • 实现 CommandLineRunner 接口
  • 实现 InitializingBean 接口
  • 使用 @PostConstruct 标签

2、@Order

  • 可以使用@Order注解或Ordered接口改变 ApplicationRunner 和 CommandLineRunner执行顺序
  • @Order 对 InitializingBean 和 @PostConstruct 不生效。

3、测试使用

  • ApplicationRunner
@Component
@Order(3)
public class App01 implements ApplicationRunner 

    @Override
    public void run(ApplicationArguments args) throws Exception 
        System.out.println("App01_执行了……@Order(3)");
    



@Component
@Order(2)
public class App02 implements ApplicationRunner 

    @Override
    public void run(ApplicationArguments args) throws Exception 
        System.out.println("App02_执行了……@Order(2)");
    
    

  • CommandLineRunner
@Component
@Order(1)
public class Com01 implements CommandLineRunner 

    @Override
    public void run(String... args) throws Exception 
        System.out.println("Com01_执行了……@Order(1)");
    



@Component
@Order(0)
public class Com02 implements CommandLineRunner 

    @Override
    public void run(String... args) throws Exception 
        System.out.println("Com02_执行了……@Order(0)");
    


  • InitializingBean
/** @Order没用
 * @author CC
 * @since 2023/5/17 0017
 */
@Component
@Order(7)
public class Ini01 implements InitializingBean 

    @Override
    public void afterPropertiesSet() throws Exception 
        System.out.println("Ini01_执行了……@Order(7)");
    



/** @Order没用
 * @author CC
 * @since 2023/5/17 0017
 */
@Component
@Order(4)
public class Ini02 implements InitializingBean 

    @Override
    public void afterPropertiesSet() throws Exception 
        System.out.println("Ini02_执行了……@Order(4)");
    


  • @PostConstruct
/** @Order没用
 * @author CC
 * @since 2023/5/17 0017
 */
@Component
@Order(6)
public class Pos01 

    @PostConstruct
    public void customizeName()
        System.out.println("Pos01_执行了……@Order(6)");
    
    


/** @Order没用
 * @author CC
 * @since 2023/5/17 0017
 */
@Component
@Order(5)
public class Pos02 

    @PostConstruct
    public void customizeName()
        System.out.println("Pos02_执行了……@Order(5)");
    
    

4、执行顺序、建议使用


  • 通过实现得出执行顺序
InitializingBean > @PostConstruct > ApplicationRunner > CommandLineRunner
  • 没有执行顺序要求,使用:@PostConstruct
  • 有执行顺序要求,使用:ApplicationRunner(推荐)或者CommandLineRunner

在单元测试之前从 .sql 预加载数据会引发语法错误

【中文标题】在单元测试之前从 .sql 预加载数据会引发语法错误【英文标题】:Pre loading data from .sql before unit testing throws syntax error 【发布时间】:2020-06-07 18:41:49 【问题描述】:

我正在使用 JPA 和 SpringJUnit4ClassRunner 对 Spring Boot 应用程序执行单元测试。为此,我创建了一个配置文件 -


@Configuration
@EnableJpaRepositories(basePackages = "base_package_name")
@EnableTransactionManagement
public class JPAConfig 

    @Bean
    public DataSource dataSource() 
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://localhost:5432/employeedb_test");
        dataSource.setUsername("postgres");
        dataSource.setPassword("postgres");

        return dataSource;
    

    private Properties hibernateProperties() 
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQL9Dialect");        
        properties.put("hibernate.hbm2ddl.auto", "create");
        properties.put("hibernate.show_sql", "true");
        properties.put("hibernate.format_sql", "false");
        return properties;
    

    @Bean(name="entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean()

        LocalContainerEntityManagerFactoryBean lcemfb
            = new LocalContainerEntityManagerFactoryBean();

        lcemfb.setDataSource(this.dataSource());
        lcemfb.setPackagesToScan(new String[] "Package_to_scan");

        HibernateJpaVendorAdapter va = new HibernateJpaVendorAdapter();
        lcemfb.setJpaVendorAdapter(va);

        lcemfb.setJpaProperties(this.hibernateProperties());

        lcemfb.afterPropertiesSet();

        return lcemfb;

    


    @Bean
    public PlatformTransactionManager transactionManager()

        JpaTransactionManager tm = new JpaTransactionManager();

        tm.setEntityManagerFactory(
            this.entityManagerFactoryBean().getObject() );

        return tm;

    

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation()
        return new PersistenceExceptionTranslationPostProcessor();
    



我的测试类看起来像这样 -


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=
        JPAConfig.class,
        TestConfig.class
        )
@SqlGroup(
    @Sql(executionPhase=ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:beforeTestRun.sql")
)
public class ExampleTest 
     ...


现在我想在运行任何测试之前将脚本文件 beforeTestRun.sql 中的数据插入到实体中。所以我在我的测试类(即ExampleTest)上方添加了以下注释 -

@SqlGroup(
    @Sql(executionPhase=ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:beforeTestRun.sql")
)

我的 beforeTestRun.sql 包含插入查询 -

INSERT INTO employee(emp_name)
    VALUES ('Jhon Dave');

我的模型类是 -


@Entity
@Table(name = "employee")
public class Employee 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="emp_id")
    Integer id;

    @Column(name="emp_name")
    String empName;


    public Integer getId() 
        return id;
    
    public void setId(Integer id) 
        this.id = id;
    

    public String getEmpName() 
        return empName;
    
    public void setEmpName(String empName) 
        this.empName = empName;
    




现在运行测试类后,它尝试从 .sql 文件插入,但即使语法正确,它也会抛出错误 -

org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #1 of class path resource [beforeTestRun.sql]: INSERT INTO employee(emp_name) VALUES ('Jhon Dave'); nested exception is org.postgresql.util.PSQLException: ERROR: syntax error at or near "INSERT"
  Position: 1
    at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:626)
    at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.populate(ResourceDatabasePopulator.java:254)
    at org.springframework.jdbc.datasource.init.DatabasePopulatorUtils.execute(DatabasePopulatorUtils.java:49)
    at org.springframework.jdbc.datasource.init.ResourceDatabasePopulator.execute(ResourceDatabasePopulator.java:269)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$4(SqlScriptsTestExecutionListener.java:263)
    at org.springframework.transaction.support.TransactionOperations.lambda$executeWithoutResult$0(TransactionOperations.java:68)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
    at org.springframework.transaction.support.TransactionOperations.executeWithoutResult(TransactionOperations.java:67)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:263)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.lambda$executeSqlScripts$0(SqlScriptsTestExecutionListener.java:185)
    at java.lang.Iterable.forEach(Unknown Source)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:185)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.executeSqlScripts(SqlScriptsTestExecutionListener.java:147)
    at org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener.beforeTestMethod(SqlScriptsTestExecutionListener.java:117)
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:289)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "INSERT"
  Position: 1
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2497)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2233)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:310)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:446)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:370)
    at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:311)
    at org.postgresql.jdbc.PgStatement.executeCachedSql(PgStatement.java:297)
    at org.postgresql.jdbc.PgStatement.executeWithFlags(PgStatement.java:274)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:269)
    at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:605)
    ... 35 more

你能建议我吗?

【问题讨论】:

您是否尝试过使用类似这样的架构限定的查询,INSERT INTO .employee (emp_name) VALUES ('Jhon Dave'); 仍然出现同样的错误 【参考方案1】:

测试数据库的更简单方法是使用带有自动配置的 h2 数据库。如果您将带有插入语句的“data.sql”和带有员工表模式的“schema.sql”文件放入测试文件夹的“resources”文件夹中,spring boot 将自动加载这两个脚本而不会出现问题,我我自己一直在使用它,它就像一个魅力。

这是一些代码:https://howtodoinjava.com/spring-boot2/h2-database-example/

【讨论】:

以上是关于SpringBoot项目预加载数据——ApplicationRunnerCommandLineRunnerInitializingBean @PostConstruct区别的主要内容,如果未能解决你的问题,请参考以下文章

基于spring的web项目启动时预加载数据到ServletContext

考虑 application.properties 的预加载数据的 Spring 数据最佳实践

加载预加载到 Core Data 中的 SQLite 数据库

如何在 vue 项目中预加载图片

SpringBoot简述springboot项目启动数据加载内存中的三种方法

前端项目分析:我是如何做图片优化的(预加载懒加载和延迟加载)