【问题标题】:Inserting row into multiple table with same sequence number in java在java中将行插入具有相同序列号的多个表中
【发布时间】:2022-01-10 14:48:21
【问题描述】:

我必须通过java代码将数据一一插入到两个表departmentemployee中。每张表都有一个公共列dept_id,它是department表中的primary key和employee表中的foreign key,引用department表的dept_id列。在部门表中的 dept_id 上定义了一个序列 dept_sequence

现在,我目前将数据插入这两个表的方法如下,

我分别使用 dept_sequence.nextval 和 dept_sequence.currval 作为 dept_id 列将数据插入到这两个表中。

Map<String, Object> deptData = ImmutableMap.builder()
        .put("DEPT_NAME", "TEXTILE")
        .put("LOCATION", "PARIS")
        .build();
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, DEPT_NAME, LOCATION) 
                          VALUES(dept_sequence.nextval, :DEPT_NAME, :LOCATION)";
namedParameterJdbcTemplate.update(insertToDeptSql , deptData);

Map<String, Object> empData = ImmutableMap.builder()
        .put("EMP_NAME", "John")
        .put("AGE", 15)
        .build();
String insertToEmpSql = "INSERT INTO EMPLOYEE(EMP_ID, DEPT_ID, EMP_NAME, AGE) 
                         VALUES(emp_sequence.nextval, dept_sequence.currval, :EMP_NAME, :AGE)";
namedParameterJdbcTemplate.update(insertToEmpSql, empData);

当一次只有一个事务时,它工作得非常好。这两个表都有正确的 dept_id 值,但它在多事务环境中中断。员工表没有收到相同的 dept_sequence 值,该值插入到部门表中以进行一项事务。在将记录插入员工表之前,dept_sequence 值会通过不同的事务(将新记录插入部门表)增加,这可能发生在不同的系统中,并且员工表会收到一些增加的序列值。

我们如何实现这一点,使同一事务的两个表中的 dept_id 值保持相同。

注意:实际数据模型不同,员工和部门仅用于示例目的,因此不要建议对模型和主键、外键约束进行任何更改,因为我不允许对实际模型做任何事情。

【问题讨论】:

  • 只标记您使用的数据库。
  • @forpas 使用 oracle...进行了更正

标签: java sql oracle spring-boot


【解决方案1】:
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, EMP_ID, EMP_NAME, DEPT_NAME, LOCATION) 
                          VALUES(dept_sequence.nextval, emp_sequence.currval, :EMP_NAME, :DEPT_NAME, :LOCATION)"

如果您的主键约束在(dept_id, emp_id) 上,那么理论上,您可以运行单独的 SQL 查询来获取 dept_sequence.nextval,然后将相同的值传递给每个插入。但这是一种非常...非常规的序列用法,我认为还有更简单的方法。

我建议重新评估数据模型。

您的department 表没有存储部门。它存储员工与部门的关系。如果员工 A 和员工 B 在同一个部门,则您没有 2 个部门。

我建议您在employee 表上放置一个dept_id 列,然后在department 表上删除与员工相关的列。

你会得到这样的结果:

Map<String, Object> deptData = ImmutableMap.builder()
        .put("DEPT_NAME", "MECH")
        .put("LOCATION", "PARIS")
        .build();
String insertToDeptSql = "INSERT INTO DEPARTMENT(DEPT_ID, DEPT_NAME, LOCATION) 
                          VALUES(dept_sequence.nextval, :DEPT_NAME, :LOCATION)";

jdbcTemplate.update(connection -> {
    PreparedStatement ps = connection
      .prepareStatement(INSERT_MESSAGE_SQL);
      ps.setString(1, message);
      return ps;
    }, keyHolder);
}
long departmentId = keyHolder.getKey().longValue();

Map<String, Object> empData = ImmutableMap.builder()
        .put("EMP_NAME", "John")
        .put("AGE", 15)
        .put("DEPARTMENT", departmentId)
        .build();
String insertToEmpSql = "INSERT INTO EMPLOYEE(EMP_ID, EMP_NAME, AGE, DEPARTMENT) 
                         VALUES(emp_sequence.nextval, :EMP_NAME, :AGE, :DEPARTMENT)";
jdbcTemplate.update(insertToEmpSql, empData);

您可以为部门的每个员工重复最后一部分并重复使用departmentId

【讨论】:

    【解决方案2】:

    正如 Brandon 所说,您的数据模型不是很好。但是要回答您实际上要问的问题,基本上,“我如何捕获刚刚插入的 id 值?”您在第一次插入时使用 RETURNING INTO 子句:

    INSERT INTO EMPLOYEE(EMP_ID, EMP_NAME, AGE) 
    VALUES(emp_sequence.nextval, :EMP_NAME, :AGE)
    RETURNING EMP_ID INTO :x
    

    在 PL/SQL 中,这非常简单。要通过 JDBC 执行此操作,它的捕获方式略有不同,尽管 getGeneratedKeys()。见Oracle's RETURNING INTO usage in Java (JDBC, Prepared Statement)

    【讨论】:

      【解决方案3】:

      我们处理的类似情况如下

      1. 将“dept_sequence”的下一个序列值获取到变量中,例如 X
      2. 在父表和子表插入语句中使用 X。确保两个插入都在单个事务范围内

      这将是干净且易于管理的并行执行。

      【讨论】:

        猜你喜欢
        • 2020-03-11
        • 2011-11-25
        • 1970-01-01
        • 2019-05-25
        • 2022-08-03
        • 1970-01-01
        • 2014-11-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多