Java Spring - RowMapper ResultSet - 整数/空值

时间:2015-10-21 17:53:57

标签: java spring jdbc jdbctemplate

我有一个Java SE 8 Spring 4.1.6-RELEASE应用程序,我正在实现org.springframework.jdbc.core.RowMapper<T>接口,我对java.sql.ResultSet传递的T mapRow(ResultSet rs, int rowNum)接口有一些疑问。方法。

当我检查ResultSet类时,我看到了一堆方法来获取列值:

╔══════════════╦═════════════════════╦════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ Return Type  ║       Method        ║                                                                   Return (javadoc, se 8)                                                                   ║
╠══════════════╬═════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ String       ║ getString           ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ boolean      ║ getBoolean          ║ the column value; if the value is SQL NULL, the value returned is false                                                                                    ║
║ byte         ║ getByte             ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ short        ║ getShort            ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ int          ║ getInt              ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ long         ║ getLong             ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ float        ║ getFloat            ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ double       ║ getDouble           ║ the column value; if the value is SQL NULL, the value returned is 0                                                                                        ║
║ BigDecimal   ║ getBigDecimal       ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ byte[]       ║ getBytes            ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ Date         ║ getDate             ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ Time         ║ getTime             ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ Timestamp    ║ getTimestamp        ║ the column value; if the value is SQL NULL, the value returned is null                                                                                     ║
║ InputStream  ║ getAsciiStream      ║ a Java input stream that delivers the database column value as a stream of one-byte ASCII characters; if the value is SQL NULL, the value returned is null ║
║ Reader       ║ getCharacterStream  ║ a java.io.Reader object that contains the column value; if the value is SQL NULL, the value returned is null in the Java programming language              ║
║ InputStream  ║ getBinaryStream     ║ a Java input stream that delivers the database column value as a stream of uninterpreted bytes; if the value is SQL NULL, the value returned is null       ║
║ <T> T        ║ getObject           ║ an instance of type holding the column value                                                                                                               ║
╚══════════════╩═════════════════════╩════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

通常的期望/做法是:

rs.getObject("COLUMN_NAME", Boolean.class);

rs.getObject("COLUMN_NAME", Byte.class);

rs.getObject("COLUMN_NAME", Short.class);

rs.getObject("COLUMN_NAME", Integer.class);

rs.getObject("COLUMN_NAME", Long.class);

等,对于所有原始类型?其他所有内容都会为null的实例返回SQL NULL

如果是这样的话,当有类型的Object方法存在时,为不同类型设置所有方法的重点是什么?

此外,每种方法的优点/缺点是什么?

  1. 使用getInt(String columnLabel)

    Integer resultingActionId = rs.getInt("RESULTING_ACTION_ID");
    if (rs.wasNull) {
        resultingActionId = null
    }

  2. 使用getObject(String columnLabel)并转换为Integer

    Integer resultingActionId = (Integer) rs.getObject("RESULTING_ACTION_ID");

  3. 使用getObject(String columnLabel, Class type)

    Integer resultingActionId = rs.getObject("RESULTING_ACTION_ID", Integer.class);

  4. 例如,我注意到org.springframework.jdbc.core.JdbcTemplate曾经有 queryForLong queryForInt 等方法来获取单个来自单行查询的值,并将它们全部替换为类型queryForObject方法。

    谢谢!

2 个答案:

答案 0 :(得分:1)

如果您查看java.sql.ResultSet,您可以看到您不需要如此明确。实际上,除非你有一个用于连接的typeMapper,它允许你使用getObject方法,否则它将不起作用(java.sql.ResultSet.getObject)。

我不知道它是否会对你有所帮助,但我设法找到了我自己的RowMapper,它非常适合我的需求。

private class ShabaUserMapper implements RowMapper<ShabaUser>
{
    @Override
    public ShabaUser mapRow( ResultSet rs, int rowNum ) throws SQLException
    {
        Collection<SimpleGrantedAuthority> roles = new ArrayList<SimpleGrantedAuthority>();

        String auths = rs.getString( "role" );

        roles.add( new SimpleGrantedAuthority( auths ) );

        ShabaUser user = new ShabaUser( rs.getString( "username" ), rs.getString( "password" ),
                rs.getBoolean( "enabled" ), rs.getString( "first_name" ),
                rs.getString( "last_name" ), rs.getString( "email" ),
                rs.getString( "date_joined" ), rs.getString( "last_online" ), true, true, true,
                roles );

        // Can be null!
        Integer awesomeness = rs.getInt( "awesomeness" );
        if ( rs.wasNull() )
        {
            awesomeness = null;
        }

        user.setAwesomeness( awesomeness );

        return user;
    }
}

private class ShabaUserListExtractor implements ResultSetExtractor<List<ShabaUser>>
{
    private final ShabaUserMapper rowMapper;

    private int                   rowsExpected;

    public ShabaUserListExtractor()
    {
        this( new ShabaUserMapper(), 0 );
    }

    public ShabaUserListExtractor( ShabaUserMapper rowMapper, int rowsExpected )
    {
        Assert.notNull( rowMapper, "RowMapper is required" );
        this.rowMapper = rowMapper;
        this.rowsExpected = rowsExpected;
    }

    @Override
    public List<ShabaUser> extractData( ResultSet rs ) throws SQLException
    {
        HashMap<String, ShabaUser> results = ( this.rowsExpected > 0
                                                                    ? new HashMap<String, ShabaUser>(
                                                                            rowsExpected )
                                                                    : new HashMap<String, ShabaUser>() );
        int rowNum = 0;
        while ( rs.next() )
        {
            ShabaUser user = rowMapper.mapRow( rs, rowNum++ );

            if ( results.containsKey( user.getUsername() ) )
            {
                ShabaUser inUser = results.get( user.getUsername() );
                ArrayList<GrantedAuthority> combinedAuthorities = new ArrayList<GrantedAuthority>();

                combinedAuthorities.addAll( inUser.getAuthorities() );
                combinedAuthorities.addAll( user.getAuthorities() );

                results.put( user.getUsername(),
                    createUserDetails( user.getUsername(), user, combinedAuthorities ) );
            } else
            {
                results.put( user.getUsername(), user );
            }
        }

        return new ArrayList<ShabaUser>( results.values() );
    }
}

我意识到这是很多代码,但希望你能看到这里完成了什么。实际的RowMapper实现实际上意味着容纳从行信息中提取对象的所有“脏工作”。

只要您的数据库设置正确并且您使其成为必需列上的NOT NULL,您将永远不会遇到拔出空行的问题。虽然我认为从ResultSet检查null响应并不会有什么坏处,但是如果列应该有值,你仍然只会抛出异常。

答案 1 :(得分:1)

TLDR

  • 如果不允许使用 resultSet.getXXX(String columnLabel),则使用 SQL NULL,或者您只关心获取原始类型(无对象、装箱或数组类型)
  • 如果值可以是 resultSet.getObject(String columnLabel, Class type),则使用 SQL NULL 但如果 type 是装箱类型,则不要取消装箱,例如Integer 因为原语不能 NullPointerException
  • 如果您运行的是 Java 6,请使用 null 进行转换

详情:

如果列不允许 resultSet.getObject(String columnLabel),或者您的代码严格需要或期望原始类型,即没有对象类型(这包括装箱类型,例如 SQL NULL、{{3 }} 例如 Integer) 然后使用特定的 get 方法:

byte[]

据我所知,这种方法的原因是方便,因为它可以优雅地处理 // Integer is 0 if value is SQL NULL int i = resultSet.getInt(column); // This is fine but Integer will never be null. This may or may not be what you want Integer j = resultSet.getInt(column); ,因为 primitive arrays which are actually objects 例如为 SQL NULL 返回 0。

如果列确实允许 int,或者您的代码需要对象类型(包括装箱类型或数组),则使用 SQL NULL 和类型的类,但不要在使用此方法时取消装箱类型:

resultSet.getObject

此方法对于受支持但没有特定 get 方法的其他 SQL 类型也很有用,即超出 JDBC 规范 primitives cannot be null 中指定的最小值:

// Integer will be null on SQL NULL but that's OK for boxed types
Integer i = resultSet.getObject(column, Integer.class);

// Throws NullPointerException on SQL NULL since primitives can't be null
int unbox = resultSet.getObject(column, Integer.class);

// Integer will be 0 since getInt returns 0 on SQL NULL
Integer autobox = resultSet.getInt(column);

但请注意,直到 Java 7 才添加此方法,因此如果您使用的是 Java 6,则需要对其进行强制转换:

// No getUUID method but it's supported by Postgres so no need for custom mapping
UUID uuid = resultSet.getObject(column, UUID.class);

所以除非你真的不知道或不关心返回类型,否则没有理由使用它,除非你喜欢它的风格,或者你需要在 Java 6 上运行。

最后,我的强烈建议是在使用(或拆箱)装箱类型时要谨慎,在使用 JDBC 时避免意外的 UUID uuid = (UUID) resultSet.getObject(column); 。要么避免在您的架构中使用 NullPointerException,例如如果可以,默认为 0,如果不能确定,则明确检查/转换。