SimpleJdbcInsert使用executeAndReturnKeyHolder方法返回主键时需要注意的一个地方

原文出处: http://www.javatang.com/archives/2010/08/09/3212438.html
作者: Jet Mah from Java堂
声明: 可以非商业性任意转载, 转载时请务必以超链接形式标明文章原始出处、作者信息及此声明!

 

Spring的SimpleJdbcInsert发挥了Simple风格,与SimpleJdbcTemplate同属于Simple体系。该类为向数据库中插入数据提供了一个非常快捷的方式,另外它还提供了一套用于返回插入数据的主键的方法:executeAndReturnKeyHolder、executeAndReturnKey。

 

查看API的时候可以看到executeAndReturnKey这个方法的返回类型是Number类型,当时我就再想如果主键的类型是String类型呢,比如UUID。后来看到还有一个executeAndReturnKeyHolder方法,返回的是一个KeyHolder对象,可以通过keyHolder#getKeys()获取主键的值,另外还有一个getKeyList()方法用于复合主键的情况,这里先撇开不说。

 

看完API之后那就可以动手了,代码如下:

  1. // jdbcInsert是SimpleJdbcInsert对象
  2. Map<String, Object> data = Maps.newHashMap();
  3. data.put("id", "t0001");
  4. data.put("name", "Tom");
  5. data.put("age", 24);
  6. KeyHolder keyHolder = jdbcInsert.withTableName("t_tablename")
  7.         .usingColumns("id", "name", "age")
  8.         .usingGeneratedKeyColumns("id")
  9.         .executeAndReturnKeyHolder(data);
  10. // 下面主要是对keyHoder进行分析
  11. if(keyHolder == null) {
  12.     return null;
  13. }
  14. Map<String, Object> keys = keyHolder.getKeys();
  15. if(keys == null || keys.size() == 0 || keys.values().size() == 0) {
  16.     return null;
  17. }
  18. Object key = keys.values().toArray()[0];
  19. if(key == null || !(key instanceof Serializable)) {
  20.     return null;
  21. }
  22. if(key instanceof Number) {
  23.     Long k = (Long)key;
  24.     return (idType == int.class || idType == Integer.class) ?
  25.             k.intValue() : k;
  26. } else if(key instanceof String) {
  27.     return (String)key;
  28. } else {
  29.     return (Serializable)key;
  30. } // end of if(key instanceof Number)

 

如果主键id的类型是int或long上面的代码没有任何问题,但是如果是自定义的UUID等String类型则问题出现了,提示下面的错误:

org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL []; SQL state [HY000]; error code [1364]; Field ‘id’ doesn’t have a default value; nested exception is java.sql.SQLException: Field ‘id’ doesn’t have a default value

有些奇怪了吧?明明id的值已经传入了,但是错误的提示应该是没有传入id的值。进入到Spring的源代码中,发现代码里面有一些debug信息,于是在log4j中将debug打开:

  1. log4j.logger.org.springframework.jdbc.core=debug

 

从打印出来的信息中看,Spring自动生成的Insert语句中竟然没有id字段!!!继续最终源代码,先在org.springframework.jdbc.core.simple.AbstractJdbcInsert类中找到protected void compileInternal()方法,在代码前加上一个debug信息:

  1. protected void compileInternal() {
  2.     logger.debug("getGeneratedKeyNames: " + getColumnNames());
  3.     tableMetaDataContext.processMetaData(getJdbcTemplate().getDataSource(), getColumnNames(), getGeneratedKeyNames());
  4.     ...
  5. }

 

这个时候打印的列表中有id字段,继续最终,最后终于在TableMetaDataContext#createInsertString(java.lang.String[])方法里面找到,关键的代码片段如下:

  1. for (String columnName : this.getTableColumns()) {
  2.             // 这里将SimpleJdbcInsert#usingGeneratedKeyColumns方法中所设置的字段去除了
  3.             if (!keys.contains(columnName.toUpperCase())) {
  4.                 columnCount++;
  5.                 if (columnCount > 1) {
  6.                     insertStatement.append(", ");
  7.                 }
  8.                 insertStatement.append(columnName);
  9.             }
  10.         }

 

看到这里,我也猛然恍然大悟了。既然在INSERT INTO语句中设置了UUID的值,那这里就不需要再使用KeyHolder进行返回了,直接获取就是了。这也是为什么KeyHolder中的getKey()方法的返回类型是Number的原因了,因为通常来说需要Spring返回的就是插入数据库中的自增类型的主键值。

你可能感兴趣的:(spring,sql,log4j,jdbc)