简体   繁体   English

Spring 启动测试 schema.sql/data.sql 违反约束错误

[英]Spring Boot Test schema.sql/data.sql contraint violation error

I have the following sql in a schema-h2.sql in my src/test/resources我的 src/test/resources 中的 schema-h2.sql 中有以下 sql

CREATE TABLE IF NOT EXISTS project.general_types(
  id SMALLINT PRIMARY KEY NOT NULL,
  name VARCHAR(15) NOT NULL);

My data.sql has the following我的 data.sql 具有以下内容

INSERT INTO project.general_types(id, name) VALUES 
   (0, 'Text'),
   (1, 'Binary');

application-test.yml应用测试.yml

spring:
  datasource:
    url: jdbc:h2:mem:testdb;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;DATABASE_TO_UPPER=false;
    username: sa
    password: 
    driver-class-name: org.h2.Driver
    initialization-mode: always
    platform: h2    
  jpa:
    generate-ddl: false
    defer-datasource-initialization: true
    show-sql: true
    open-in-view: false
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
        default_schema: project
        dialect: org.hibernate.dialect.H2Dialect  
        globally_quoted_identifiers: true

The entity that maps this.映射它的实体。

@Entity(name = "general_types")
public class GeneralTypesEntity {

  @Id
  private short id;
  @Basic
  private String name;

//getters/setters nor shown...
}

Test case测试用例

@SpringBootTest
@ActiveProfiles("test")
class GeneralTypesDbTest {

  @Autowired
  private final GeneralTypesRepository generalTypesRepository = null;

  @Test
  void test() {

    assertNotNull(generalTypesRepository);
    assertEquals(2, generalTypesRepository.count());
  }

}

With PostgreSQL in production this works (prebuilt db and all the repos, entities, etc...).在生产中使用 PostgreSQL 可以正常工作(预构建的数据库和所有回购协议、实体等...)。 I'm now trying to setup a Spring Boot test with h2.我现在正在尝试使用 h2 设置 Spring 启动测试。 If I run with just the schema.sql it builds fine and runs the test.如果我仅使用 schema.sql 运行,它可以正常构建并运行测试。

Adding the data.sql with the insert statement to create the default data set, Spring errors with the execution将data.sql与insert语句相加,创建默认数据集,执行Spring错误

Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PRIMARY KEY ON project.general_types(id) [0, 'Text']"; SQL statement:
INSERT INTO project.general_types(id, name) VALUES (0, 'Text'), (1, 'Binary') [23505-200]
   at org.h2.message.DbException.getJdbcSQLException(DbException.java:459) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.message.DbException.getJdbcSQLException(DbException.java:429) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.message.DbException.get(DbException.java:205) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.message.DbException.get(DbException.java:181) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.mvstore.db.MVPrimaryIndex.add(MVPrimaryIndex.java:127) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.mvstore.db.MVTable.addRow(MVTable.java:531) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.command.dml.Insert.insertRows(Insert.java:195) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.command.dml.Insert.update(Insert.java:151) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.command.CommandContainer.update(CommandContainer.java:198) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.command.Command.executeUpdate(Command.java:251) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:228) ~[h2-1.4.200.jar:1.4.200]
   at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:201) ~[h2-1.4.200.jar:1.4.200]
   at com.zaxxer.hikari.pool.ProxyStatement.execute(ProxyStatement.java:94) ~[HikariCP-4.0.3.jar:na]
   at com.zaxxer.hikari.pool.HikariProxyStatement.execute(HikariProxyStatement.java) ~[HikariCP-4.0.3.jar:na]
   at org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:261) ~[spring-jdbc-5.3.18.jar:5.3.18]
   ... 96 common frames omitted

I tried putting the Insert into the schema-h2.sql right after table creation and get the same error.我尝试在创建表后立即将 Insert 放入 schema-h2.sql 并得到相同的错误。 Any ideas how a constraint violation is being caused.关于如何导致约束违规的任何想法。 For a newly create table with no auto increment I'm unclear why an insert would be causing a constraint violation.对于没有自动递增的新创建的表,我不清楚为什么插入会导致违反约束。

my friend,我的朋友,

It would be good if you can throw the Entity class.如果你能抛出 Entity class 就好了。 But I had a similar error before, I solved my problem in the Entity class.但是我之前也遇到过类似的错误,我在 Entity class 中解决了我的问题。 If you are using @GeneratedValue, you should choose the right strategy.如果您使用@GeneratedValue,您应该选择正确的策略。 A reference: baeldung.com/hibernate-identifiers参考:baeldung.com/hibernate-identifiers

This is the code that solved the problem for me.这是为我解决问题的代码。 @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private Long id; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID") private Long id;

Also a recommendation is to make all sql queries in the data.sql file另外一个建议是在 data.sql 文件中进行所有 sql 查询

It would helpful to show your test class as it would appear your data script is being executed twice显示您的测试 class 会有所帮助,因为您的数据脚本似乎正在执行两次

As of this morning, it's now working and I have no idea why.截至今天早上,它现在正在工作,我不知道为什么。 I'll list out what was done and maybe someone can clarify why it would have started to work 12 hours later.我会列出所做的事情,也许有人可以澄清为什么它会在 12 小时后开始工作。

Since the data.sql was causing the duplicate key issue, I renamed it to data-disabled.yml.由于 data.sql 导致重复密钥问题,我将其重命名为 data-disabled.yml。 I then changed the test to using the @Sql( statement="" ) to insert an item and the test used the GeneralTypesRepository to findByName(string) (the method I was trying to test).然后我将测试更改为使用 @Sql( statement="" ) 插入项目,并且测试使用 GeneralTypesRepository 来 findByName(string) (我试图测试的方法)。 This ran successfully by building the H2 DB (schema.sql), used the @Sql to insert 1 item (0, "Text") and then checked the method could find the record for "Text".这通过构建 H2 DB (schema.sql) 成功运行,使用 @Sql 插入 1 项(0,“Text”),然后检查该方法是否可以找到“Text”的记录。 This passed.这过去了。 Great.伟大的。

Built out a few more tests for other repositories (about 8 in total) using this method (@Sql on each test method).使用此方法(每个测试方法上的@Sql)为其他存储库(总共大约 8 个)构建了更多测试。

This morning when I saw the request for more information, I created a new test file to copy redo my original test.今天早上,当我看到更多信息的请求时,我创建了一个新的测试文件来复制重做我原来的测试。 I renamed the data-disabled.sql back to data.sql.我将 data-disabled.sql 重命名为 data.sql。 Ran the test and it passed.运行测试,它通过了。 No exceptions and junit green.没有例外,junit 绿色。

I went back to the other test I created last night, removed all the @Sql lines, ran the test and they all passed.我回到昨晚创建的另一个测试,删除了所有@Sql 行,运行测试,它们都通过了。

No other changes were done (confirmed with git).没有进行其他更改(通过 git 确认)。 I don't know why it started working.我不知道为什么它开始工作。 This test is now working as I expected it to in the first place since the data.sql was the only place inserts were being done.这个测试现在正如我预期的那样工作,因为 data.sql 是唯一完成插入的地方。

Thanks to the few who pushed for more information.感谢推动更多信息的少数人。 Sorry I didn't have it in the first place.对不起,我一开始就没有。 And sorry this doesn't give a definitive answer to the original issue but again I was pretty sure having a data.sql file would only run once and do the inserts with the schema creating the db with no inserts.抱歉,这并没有对原始问题给出明确的答案,但我再次非常确定有一个 data.sql 文件只会运行一次,并使用创建数据库的架构进行插入,而无需插入。

Today I had a similar case of this failing and was able to figure out what caused it.今天我有一个类似的失败案例,并且能够找出导致它的原因。 Putting this here to help others in the future.把它放在这里是为了将来帮助其他人。

Basically I working tests and then after adding a @PostContruct my one DB test started failing.基本上我进行测试,然后在添加 @PostContruct 之后我的一个数据库测试开始失败。 The PostContruct method did a db call to get the most recent record as a cache. PostContruct 方法进行了数据库调用以获取最新记录作为缓存。

The issue here is that @PostContruct will be called during Spring App Context is starting up.这里的问题是 @PostContruct 将在 Spring App Context 启动期间被调用。 This cause the DB call to occur before the Spring Test gets a chance to use the test resource file to initialize the DB.这导致在 Spring 测试有机会使用测试资源文件初始化数据库之前发生数据库调用。 Changed to build the cache on first use instead and the problem is now gone.更改为在首次使用时构建缓存,现在问题消失了。 Another option could have been to change to the @EventListener(ApplicationReadyEvent.class) to have it start after the app context if fully loaded.另一种选择可能是更改为 @EventListener(ApplicationReadyEvent.class) 以使其在完全加载后在应用程序上下文之后启动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM