【问题标题】:simpleJdbcTemplate. - insert and retrieve ID简单Jdbc模板。 - 插入和检索 ID
【发布时间】:2011-01-15 04:09:51
【问题描述】:

我正在使用 simpleJdbcTemplate 将数据放入数据库。

simpleJdbcTemplate.update("insert into TABLE values(default)");

我不想放任何数据,因为我不需要它来进行单元测试。

如何从插入的行中获取 id?我可以检索当前的序列值,但如果其他人会进行插入,那么我将获得下一个序列值。

有没有什么方法可以使用 simpleJdbcTemplate 插入一行并获取 id?更新方法重新调整插入的行数,我想拥有 id。感谢您的帮助。

【问题讨论】:

  • 感谢回答。是的,它是甲骨文。

标签: java unit-testing spring integration-testing jdbctemplate


【解决方案1】:

你找到答案了吗?如果没有,请尝试改用SimpleJdbcInsert。 例如:

SimpleJdbcInsert sji = new SimpleJdbcInsert(dataSource)
    .withTableName(TableName)
    .usingColumns(new String[]{your columns})
    .usingGeneratedKeyColumns(you auto-increment id colums);

然后检索

sji.executeAndReturnKey(args).longValue();

【讨论】:

    【解决方案2】:

    您需要手动处理序列以轻松获取 id,而无需将自己绑定到任何特定的 RDBMS 产品。

    这意味着您必须指定一个特定于部署的DataFieldMaxValueIncrementer bean 并将其注入到您的数据库处理类中,就像您很可能对DataSource 所做的那样。 bean 定义应该看起来像这样(这个例子是 PostgreSQL 的)

    <bean id="incrementer" class="org.springframework.jdbc.support.incrementer.PostgreSQLSequenceMaxValueIncrementer">
        <property name="dataSource" ref="dataSource" />
        <property name="incrementerName" value="seq_name" />
    </bean>
    

    然后,当你的类中有增量器时,你可以在代码中使用它来获取 id 值,有点像这样:

    public long saveBeanAndReturnId(Bean b) {
        long id = incrementer.nextLongValue();
        simpleJdbc.update("...");
        return id;
    }
    

    【讨论】:

      【解决方案3】:

      我不认为它像看起来那样艰难.. :-O

      你不要尝试类似的东西:

      int newID = simpleJdbcTemplate.queryForInt("INSERT INTO TABLE(Column_Names) 
                                                  values (default) 
                                                  RETURNING ID");
      

      现在 newID 将包含新插入的行 ID。

      干杯..!! :)

      【讨论】:

      • 我已经厌倦了这个并且得到了以下错误; “查询未返回任何结果。”可能是我做错了什么。
      【解决方案4】:

      使用 NamedParameterJdbcTemplate 你有一个密钥持有者。它抽象了 DBMS 密钥生成。 检查创建方法。

      package info.pello.spring.persistence;
      
      import org.springframework.jdbc.core.JdbcTemplate;
      import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
      import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
      import org.springframework.jdbc.support.GeneratedKeyHolder;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * 
       */
      
      /**
       * DAO for customer entity
       * @author Pello Xabier Altadill Izura
       * @greetz Blue Mug
       *
       */
      public class CustomerDAO {
      
          // I use both jdbcTemplate/namedParameterJdbcTemplate depending on needs
          private JdbcTemplate jdbcTemplate;
          private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
          private final static String CREATE_SQL = "insert into customer (name,address,email) values(:name,:address,:email)";
      
          /**
           * gets Customer data from DataBase
           * @param customerId
           * @return
           */
          public Customer read (int customerId) {
              Customer customer = null;
      
              return customer;
          } 
      
          /**
           * gets all Customer data from DataBase
           * @return list of customers
           */
          public List<Customer> readAll () {
      
              List<Customer> customerList = new ArrayList<Customer>();
      
              return customerList;
          } 
      
          /**
           * creates new Customer
           * @param newCustomer
           * @return
           */
          public int create (Customer newCustomer) {
              GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder();
      
              MapSqlParameterSource namedParameters = new MapSqlParameterSource();
              namedParameters.addValue("name", newCustomer.getName());
              namedParameters.addValue("address", newCustomer.getAddress());
              namedParameters.addValue("email", newCustomer.getEmail());
      
              namedParameterJdbcTemplate.update(CREATE_SQL,
                                  namedParameters,
                                  generatedKeyHolder);
      
              newCustomer.setId(generatedKeyHolder.getKey().intValue());
              return newCustomer.getId();
          }
      
          /**
           * updates customer information 
           * @param customer
           * @return
           */
          public int update (Customer customer) {
              int result = 0;
      
      
              return result;
          }
      
          /**
           * delete customer  
           * @param customerId
           * @return
           */
          public int delete (int customerId) {
      
              int result = 0;
      
      
              return result;
          }
      
          /**
           * @return the jdbcTemplate
           */
          public JdbcTemplate getJdbcTemplate() {
              return jdbcTemplate;
          }
      
          /**
           * @param jdbcTemplate the jdbcTemplate to set
           */
          public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
              this.jdbcTemplate = jdbcTemplate;
          }
      
          /**
           * @return the namedParameterJdbcTemplate
           */
          public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
              return namedParameterJdbcTemplate;
          }
      
          /**
           * @param namedParameterJdbcTemplate the namedParameterJdbcTemplate to set
           */
          public void setNamedParameterJdbcTemplate(
                  NamedParameterJdbcTemplate namedParameterJdbcTemplate) {
              this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
          }
      
      
      }
      

      【讨论】:

      • 对我来说,使用 KeyHolder 只返回 ROWID 而不是实际的序列值。所示代码产生异常:org.springframework.dao.DataRetrievalFailureException: The generated key is not of a supported numeric type. Unable to cast [oracle.sql.ROWID] to [java.lang.Number].
      【解决方案5】:

      您应该首先从相应的sequence 查询id,然后在您的插入语句中提供该id。就这么简单。

      此外,我们称之为集成测试,而不是单元测试。您可能想参考 this SO thread 以了解有关集成测试和 id 的想法。

      [评论后编辑]

      在这种情况下,摆脱那个触发器。并在创建insert 之前直接从sequence 中检索id

      好吧,你可以在桌子上触发一个SELECT... FOR UPDATE,然后抓住最后一个id,然后将其加1。如果你的id不是连续的,我猜不会是这样,你可以保存特定于 Oracle AFAIK 的 ROWID。然后使用它查询id。确实,它的所有工作都可以解决。

      注意: 我强烈建议您查看 Aaron Digulla 的帖子。看看这些是否足够。

      【讨论】:

      • id是数据库中trigger生成的。
      • trigger 一定是在某个地方深处使用了一些序列,我想。顺便说一句,这些是让测试变得棘手的事情。
      【解决方案6】:

      回答这个问题:您想通过测试实现什么目标?检查更新是否正常运行?每次都换新身份证?该表存在吗?

      根据答案,您必须修改您的测试。如果你只是想知道语句的语法是否正确,你不需要做任何事情,只需要运行语句(如果有错误导致测试失败,它会抛出异常)。

      如果你想确保每次都得到一个新的ID,你必须查询序列两次,并检查第二个值是否与第一个不同。

      如果您想检查是否插入了具有新唯一 ID 的行,只需运行插入并检查它是否返回 1。如果有效,您将知道主键(ID)没有被违反并且插入了一行。因此,“使用唯一 ID 添加”机制必须有效。

      [编辑] 无法测试将 ID 添加到新行的触发器,因为 Oracle 无法返回它刚刚创建的 ID。您可以读取序列,但不能保证nextval-1 会为您提供与触发器看到的相同的结果。

      您可以尝试select max(ID),但如果其他人在您运行查询之前插入另一行并提交它,则可能会失败(使用默认事务级别READ_COMMITTED)。

      因此,我强烈建议摆脱触发器并使用其他任何人都使用的标准两步(“获取新 ID”加上“插入新 ID”)算法。它会让你的测试更简单,更不脆弱。

      【讨论】:

      • 是的,这个问题是在乞求问题。 +1。我强烈推荐同样的。很好的建议。
      • 我有一个时间对象设计的实现(详见 Martin Folwer 网页)。我有一个连续对象和时间对象以及方法 dao.load(Continuum.class, continnumID)。此方法返回一个时间对象。有许多时间对象可以连接到一个连续对象。每个时间对象都有其所属的连续统一体。所以在我的测试中,我想首先使用插入语句将连续对象放入数据库。然后我想获取插入对象的 id 来加载时间对象。
      • 在这种情况下,摆脱那个触发器。并在进行插入之前先检索 id。
      • 我找到了其他解决方案。使用 SimpleJdbcInsert.executeAndReturnKey 方法。这对我有用。坦克每个人。
      【解决方案7】:

      simpleJdbcTemplate 已弃用,取而代之的是 NamedParameterJdbcTemplate。

      Pello X 有正确答案,但是他的提交太麻烦了,看不懂。 简化:

      如果您有一个名为 SAMPLE 的非常简单的表,其中有一个名为 NAME 的列和一个生成的名为 ID 类型为 bigint 的主键:

      MapSqlParameterSource namedParameters = new MapSqlParameterSource().addValue("name", name);
      
      KeyHolder keyHolder = new GeneratedKeyHolder();
      int numberOfAffectedRows = namedParameterJdbcTemplate.update("insert into SAMPLE(name) values(:name)", namedParameters, keyHolder);
      
      return numberOfAffectedRows == 1 ? keyHolder.getKey().longValue() : -1L;
      

      这将返回更新中唯一生成的键,如果超过 1 行受到影响,则返回 -1。

      请注意,由于只有 1 个生成的密钥,我并不关心列名。

      如果生成的密钥超过 1 个,请查看 http://docs.spring.io/spring/docs/3.2.7.RELEASE/javadoc-api/org/springframework/jdbc/support/KeyHolder.html#getKeys%28%29

      【讨论】:

        【解决方案8】:

        使用 Spring 的 JdbcTemplate,您可以使用 update 方法与 PreparedStatementCreatorGeneratedKeyholder 来保存新插入行的主键。

        public class SomeDao(){
           @Autowired
           private JdbcTemplate jdbcTemplate;
           //example of a insertion returning the primary key
           public long save(final String name){
               final KeyHolder holder = new GeneratedKeyHolder();//the newly generated key will be contained in this Object
               jdbcTemplate.update(new PreparedStatementCreator() {
              @Override
              public PreparedStatement createPreparedStatement(final Connection connection) throws SQLException {
                final PreparedStatement ps = connection.prepareStatement("INSERT INTO `names` (`name`) VALUES (?)",
                    Statement.RETURN_GENERATED_KEYS);
                ps.setString(1, name);
                return ps;
              }
            }, holder);
            return holder.getKey().longValue();//the primary key of the newly inserted row
           }
        }
        

        【讨论】:

          猜你喜欢
          • 2011-03-28
          • 2013-06-29
          • 2011-11-02
          • 2012-10-29
          • 2014-09-23
          • 2021-11-21
          • 1970-01-01
          • 2017-08-03
          • 2013-02-04
          相关资源
          最近更新 更多