Java Spring - RowMapper ResultSet - Integer / null值

12
我有一个Java SE 8 Spring 4.1.6-RELEASE应用程序,我正在实现org.springframework.jdbc.core.RowMapper<T>接口,并且对其T mapRow(ResultSet rs, int rowNum)方法中传递的java.sql.ResultSet接口有一些疑问。
当我检查ResultSet类时,我看到了一堆获取列值的方法:
╔══════════════╦═════════════════════╦══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ 返回类型       ║ 方法                 ║                                                                 返回值 (javadoc, se 8)                                                                                    ║
╠══════════════╬═════════════════════╬════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║ String       ║ getString           ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ boolean      ║ getBoolean          ║ 列的值;如果该值为SQL NULL,则返回false                                                                                                           ║
║ byte         ║ getByte             ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ short        ║ getShort            ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ int          ║ getInt              ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ long         ║ getLong             ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ float        ║ getFloat            ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ double       ║ getDouble           ║ 列的值;如果该值为SQL NULL,则返回0                                                                                                               ║
║ BigDecimal   ║ getBigDecimal       ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ byte[]       ║ getBytes            ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ Date         ║ getDate             ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ Time         ║ getTime             ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ Timestamp    ║ getTimestamp        ║ 列的值;如果该值为SQL NULL,则返回null                                                                                                            ║
║ InputStream  ║ getAsciiStream      ║ 一个Java输入流,以一字节ASCII字符流形式提供数据库列值;如果该值为SQL NULL,则返回null                                                             ║
║ Reader       ║ getCharacterStream  ║ 一个java.io.Reader对象,包含列的值;如果该值为SQL NULL,则在Java编程语言中返回null                                                       ║
║ InputStream  ║ getBinaryStream     ║ 一个Java输入流,以未解释字节流形式提供数据库列值;如果该值为SQL NULL,则返回null                                                       ║
║ <T> T        ║ getObject           ║ 类型实例,保持列值                                                                                                                                     ║
╚══════════════╩═════════════════════╩══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝

一般期望/惯例是调用:

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);

所有基本类型都是这样吗?因为其他所有东西都会针对SQL NULL实例返回null

如果是这样的话,当有了类型化对象方法时,拥有所有不同类型方法的意义是什么?

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

  1. Using getInt(String columnLabel):

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

  2. Using getObject(String columnLabel) and casting to Integer:

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

  3. Using getObject(String columnLabel, Class type):

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

例如,我注意到org.springframework.jdbc.core.JdbcTemplate曾经有queryForLongqueryForInt等方法用于从单行查询中获取单个值,并将它们全部替换为一个类型化的queryForObject方法。
谢谢!
2个回答

3

如果你看一下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的空响应也无妨,但如果该列应该有一个值,你最终还是会抛出异常。


我有很多情况需要一个空的整数,主要是当我有一个可选的外键时,这是通过数据库中的可空整数类型来实现的。我在查看http://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html#getObject-java.lang.String-java.lang.Class-时发现,它说“检索此ResultSet对象当前行中指定列的值,并将其从列的SQL类型转换为请求的Java数据类型(如果支持转换)。如果不支持转换或者为类型指定了null,则抛出SQLException。”... - anonymous
我认为这意味着它将尝试从JDBC驱动程序结果进行类转换,对于将数字值强制转换为整数应该可以正常工作。 - anonymous
在这种情况下,我建议您至少尝试第一种解决方案。wasNull()方法是存在的,因此我认为这是最佳实践:Integer resultingActionId = rs.getInt("RESULTING_ACTION_ID"); if (rs.wasNull) { resultingActionId = null } - aaiezza
否则,您不希望它保持为0。这就是处理整数类型的SQL_NULL的方式。 - aaiezza
1
这是我在问题中举的一个例子,用于比较处理null的不同方式:从 ResultSet.getInt(String columnLabel) 中提取值并立即检查 ResultSet.wasNull()。我想知道从最佳实践/已知实践/效率/执行代码的角度来看,Integer awesomeness =(Integer)rs.getObject(“awesomeness”);Integer awesomeness = rs.getObject(“awesomeness”,Integer.class); 的区别是什么。 - anonymous
显示剩余2条评论

2

简述

  • 如果不允许SQL NULL,或者您只关心获取一个原始类型(没有对象、装箱或数组类型),请使用resultSet.getXXX(String columnLabel)
  • 如果值可能是SQL NULL,但类型是装箱类型(例如Integer),请勿取消装箱,否则可能会出现NullPointerException,因为原始类型不能为null,请使用resultSet.getObject(String columnLabel, Class type)
  • 如果您正在运行Java 6,请使用带有转换的resultSet.getObject(String columnLabel)

详细信息:

如果列不允许SQL NULL,或者您的代码严格需要或期望一个原始类型,即没有对象类型(包括装箱类型,例如Integer)、实际��是对象的原始数组(例如byte[]),那么请使用特定的获取方法:

// Integer is 0 if value is SQL NULL
int i = resultSet.getInt(column);

// This is fine but Integer will never be null e.g. Integer.valueOf(0) if SQL NULL
// This may or may not be what you want
Integer j = resultSet.getInt(column);

据我所知,使用这种方法的原因是它可以方便地处理SQL NULL,而在必须使用基元类型的情况下,基元类型不能为null,例如对于int返回0。
如果列允许SQL NULL,或者您的代码需要一个对象类型(包括装箱类型或数组),则使用resultSet.getObject和类型的类,但是在使用此方法时不要取消装箱装箱类型:
// 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);

这种方法对于其他SQL类型也很有用,尽管它们没有特定的获取方法,即超出了JDBC规范JSR-221中指定的最小值:

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

但请注意,此方法直到Java 7才被添加,因此如果您使用的是Java 6,则需要进行强制转换:
UUID uuid = (UUID) resultSet.getObject(column);

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

最后,我的强烈建议是,在使用(或取消装箱)装箱类型时要谨慎处理JDBC,以避免意外的NullPointerException。 要么避免在架构中使用SQL NULL,例如如果可以,请默认为0,否则请明确检查/转换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接