【问题标题】:DbUnit does not update postgresql sequences on insertDbUnit 不会在插入时更新 postgresql 序列
【发布时间】:2014-04-30 07:50:56
【问题描述】:

我正在使用 DbUnit 在 postgreSql 数据库上运行一些测试。为了能够运行我的测试,我通过在每次测试之前重新填充数据库表,运行一个干净的插入,使数据库进入一个众所周知的状态。因此我使用下面的 FlatXmlDataSet 定义(与附加的 SQL 架构比较)。

但是,如果我运行 testCreateAvatar() 测试用例,我会因为状态码不匹配而出现异常,这是由于 sql 插入失败导致的,因为已经存在主键 ( id 字段)。查看我的数据库告诉我,测试数据集的插入不会更新相应的 *avatars_id_seq* 和 *users_id_seq* 序列表,它们用于生成 id 字段(postgresql 生成自动增量值的机制)。

这意味着,如果我在 FlatXmlDataSet 定义中定义静态 ID,则不会更新自动增量值。所以我的问题是如何改变这种行为或自己设置自动增量值(使用 DbUnit)。

头像创建测试用例

@Test
public void testCreateAvatar() throws Exception {
    // Set up the request url.
    final HttpPost request = new HttpPost(
            "http://localhost:9095/rest/avatars");

    // Setup the JSON blob, ...
    JSONObject jsonAvatar = new JSONObject();
    jsonAvatar.put("imageUrl", "images/dussel.jpg");

    // ... add it to the post request ...
    StringEntity input = new StringEntity(jsonAvatar.toString());
    input.setContentType("application/json");
    request.setEntity(input);

    // ... and execute the request.
    final HttpResponse response = HttpClientBuilder.create().build()
            .execute(request);

    // Verify the result.
    assertThat(response.getStatusLine().getStatusCode(),
            equalTo(HttpStatus.SC_CREATED));

    // Fetch dussel duck from the database ...
    Avatar dussel = getServiceObjDao().queryForFirst(
                getServiceObjDao().queryBuilder().where()
         .eq("image_url", "images/dussel.jpg")
         .prepare());

    // ... and verify that the object was created correctly.
    assertThat(dussel, notNullValue());
    assertThat("images/dussel.jpg", equalTo(dussel.getImageUrl()));
}

DbUnit 数据集

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
   <!-- Avatars -->
   <avatars 
      id="1" 
      image_url="images/donald.jpg" />
   <avatars 
      id="2" 
      image_url="images/daisy.jpg" />

   <!-- Users -->
   <users 
      id = "1"
      name = "Donald Duck"
      email = "donald.duck@entenhausen.de"
      password = "quack" />
   <users 
      id = "2"
      name = "Daisy Duck"
      email = "daisy.duck@entenhausen.de"
      password = "flower" />
</dataset>

用户和头像表架构

CREATE TABLE avatars (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP,
   image_url VARCHAR(200),
   UNIQUE (image_url)
);

CREATE TABLE users (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP,
   name VARCHAR(160) NOT NULL,
   email VARCHAR (355) UNIQUE NOT NULL,
   password VARCHAR(30) NOT NULL,
   avatar_id BIGINT,
   UNIQUE (name),
   CONSTRAINT user_avatar_id FOREIGN KEY (avatar_id)
      REFERENCES avatars (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

【问题讨论】:

    标签: sql database postgresql auto-increment dbunit


    【解决方案1】:

    您可以使用setval 设置序列的值,例如

    SELECT SETVAL('sequence_name', 1000);
    

    其中 sequence_name 是序列的名称,在 psql 中使用表上的 /dt 可见,1000 是您要设置的值。您可能希望将其设置为表中 Id 的最大值。

    我真的不知道如何让 DbUnit 发出这个 SQL。

    【讨论】:

    • 感谢您的回答,这可能会有所帮助。但是,我不会接受这个答案,因为我希望得到一个基于 DbUnit 的解决方案。但是您得到了 +1 的帮助。 ;-D
    • 添加了一个简单的解决方案,它不使用 DbUnit,但适用于所有自动生成的序列。希望,这可以帮助某人。但是,我仍然更喜欢基于 DbUnit 的解决方案。
    【解决方案2】:

    下面的函数查找数据库中的所有序列,从序列名称中提取对应表的名称,最后根据对应表中的最大id值更新序列的当前值。由于还没有更好的解决方案,这似乎是要走的路。希望,这对某人有帮助。

    基于harmic的建议的简单解决方案

    @Before
    public void resetSequence() {
        Connection conn = null;
        try {
            // Establish a database connection.
            conn = DriverManager.getConnection(
                    this.props.getProperty("database.jdbc.connectionURL"),
                    this.props.getProperty("database.jdbc.username"), 
                    this.props.getProperty("database.jdbc.password"));
    
            // Select all sequence names ...
            Statement seqStmt = conn.createStatement();
            ResultSet rs = seqStmt.executeQuery("SELECT c.relname FROM pg_class c WHERE c.relkind = 'S';");
    
            // ... and update the sequence to match max(id)+1.
            while (rs.next()) {
                String sequence = rs.getString("relname");
                String table = sequence.substring(0, sequence.length()-7);
                Statement updStmt = conn.createStatement();
                updStmt.executeQuery("SELECT SETVAL('" + sequence + "', (SELECT MAX(id)+1 FROM '" + table + "'));");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException e) {
            }
        }
    }
    

    【讨论】:

    • "SELECT SETVAL('" + sequence + "', (SELECT MAX(id)+1 FROM " + table + "));"最好用引号将表名括起来以防止错误: "SELECT SETVAL('" + relation + "', (SELECT MAX(id)+1 FROM \"" + table + "\"));"
    • @YakovlevDenis 相应地更新了我的答案。但是使用单引号以获得更好的可读性。
    猜你喜欢
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 2012-08-05
    • 2016-02-23
    • 2020-12-17
    • 2014-10-16
    • 2012-03-19
    • 2021-01-06
    相关资源
    最近更新 更多