การตรวจสอบค่า null เป็นค่าจาก Java ResultSet


277

ใน Java ฉันพยายามทดสอบค่า Null จาก ResultSet ที่ซึ่งคอลัมน์กำลังถูกแปลงเป็นประเภทintดั้งเดิม

int iVal;
ResultSet rs = magicallyAppearingStmt.executeQuery(query);
if (rs.next()) {
  if (rs.getObject("ID_PARENT") != null && !rs.wasNull()) {
    iVal = rs.getInt("ID_PARENT");
  }
}

จากส่วนของโค้ดด้านบนมีวิธีที่ดีกว่าในการทำเช่นนี้และฉันคิดว่าการทดสอบที่สองคือการทดสอบแบบNull ()ซ้ำซ้อนหรือไม่

ให้ความรู้แก่เราและขอขอบคุณ


14
ฉันพบคำถามนี้เพราะฉันมีคอลัมน์ nullable ในฐานข้อมูลและเป็นตัวแทนโดย Integer ใน Java คุณอาจคิดว่าการมีคอลัมน์ตัวเลขเป็นโมฆะในฐานข้อมูลอาจเป็นเรื่องธรรมดามากพอที่ ResultSet API จะรองรับมันได้อย่างสวยงามกว่านี้เล็กน้อย
spaaarky21

ฉันไม่ได้โพสต์นี้เป็นคำตอบเพราะมันเป็นวงและห่างไกลจากสากล: วิธีการแก้ปัญหาปกติของฉันนี้คือการใส่IF(colName = NULL, 0, colName) AS colNameในSELECTคำสั่ง (โดยเฉพาะใน proc เก็บไว้) ในเชิงปรัชญาสิ่งนี้เกิดขึ้นได้ว่า DB ควรสอดคล้องกับแอพหรือในทางกลับกัน เนื่องจาก SQL จัดการ NULL ได้อย่างง่ายดายและผู้บริโภค SQL จำนวนมากไม่ได้ (เช่นjava.sql.ResultSet) ฉันเลือกที่จะจัดการกับฐานข้อมูลเมื่อเป็นไปได้ (แน่นอนว่าถือว่าเป็นโมฆะในเชิงแนวคิดและเป็นศูนย์จะเทียบเท่ากับวัตถุประสงค์ของคุณ)
s.co.tt

คำตอบ:


368

ค่าเริ่มต้นสำหรับResultSet.getIntเมื่อค่าฟิลด์NULLคือการส่งคืน0ซึ่งเป็นค่าเริ่มต้นสำหรับiValการประกาศของคุณ ในกรณีนี้การทดสอบของคุณซ้ำซ้อนอย่างสมบูรณ์

ถ้าคุณต้องการทำอะไรที่แตกต่างกันจริง ๆ ถ้าค่าของเขตข้อมูลเป็น NULL ฉันขอแนะนำ:

int iVal = 0;
ResultSet rs = magicallyAppearingStmt.executeQuery(query);
if (rs.next()) {
    iVal = rs.getInt("ID_PARENT");
    if (rs.wasNull()) {
        // handle NULL field value
    }
}

(แก้ไขเป็นความคิดเห็น @martin ด้านล่างรหัส OP ตามที่เขียนจะไม่รวบรวมเพราะiValไม่ได้เริ่มต้น)


2
ฉันเพิ่งพบคำสั่งเดียวกันในเอกสาร มันคุ้มค่าด้ายแยกต่างหากใน SO imho ( java.sun.com/j2se/1.4.2/docs/api/java/sql/… )
Roman

6
@ Roman - ดู javadoc สำหรับ getInt ใน ResultSet: "Return: ค่าคอลัมน์ถ้าค่าเป็น NULL ของ SQL ค่าที่ส่งคืนคือ 0"
Cowan

150
ความจริงก็คือไร้สาระแน่นอน getInt()ควรจะเป็นgetInteger()ซึ่งผลตอบแทนIntegerที่เป็นnullถ้าค่า DB nullคือ devs ทำให้สิ่งนี้ยุ่งมาก
ryvantage

7
@sactiw ตามตรรกะนี้ทุกอย่างบน java ควรได้รับการพัฒนาเพื่อหลีกเลี่ยง NPE ซึ่งไม่ใช่กรณี หลีกเลี่ยง NPE เป็นความรับผิดชอบของนักพัฒนาแอพไม่ใช่ภาษา API ภายใน
Mateus Viccari

10
OMG เหตุใดจึงไม่ส่งคืนค่า NULL 0และNULLเป็นสองสิ่งที่แตกต่างกันมาก
deFreitas

84

ทางออกอื่น:

public class DaoTools {
    static public Integer getInteger(ResultSet rs, String strColName) throws SQLException {
        int nValue = rs.getInt(strColName);
        return rs.wasNull() ? null : nValue;
    }
}

27

ฉันคิดว่ามันซ้ำซ้อน rs.getObject("ID_PARENT")ควรกลับIntegerวัตถุหรือถ้าค่าของคอลัมน์เป็นจริงnull NULLดังนั้นควรเป็นไปได้ที่จะทำสิ่งที่ชอบ:

if (rs.next()) {
  Integer idParent = (Integer) rs.getObject("ID_PARENT");
  if (idParent != null) {
    iVal = idParent; // works for Java 1.5+
  } else {
    // handle this case
  }      
}

5
อืมอย่างน้อยในกรณีของฉันปัญหานี้คือการโทรgetObjectไม่จำเป็นต้องคืนค่าจำนวนเต็มเนื่องจากลักษณะของคอลัมน์ประเภทใน oracle db ที่ฉันใช้ ("Number")
Matt Mc

ปัญหาเดียวกันของ Matt ... ด้วย MySQL และTypes.BIGINT(ที่ควรแมปกับ a Long) getObject()เมธอดส่งคืนค่า 0 แทนค่า null
xonya

2
สามารถทำได้rs.getObject("ID_PARENT", Integer.class)
Arlo

26

เพียงแค่ตรวจสอบว่าข้อมูลเป็นหรือไม่ได้ใช้null ResultSet#getObject()ทดแทน-1ด้วยค่าใดก็ได้ที่คุณต้องการ

int foo = resultSet.getObject("foo") != null ? resultSet.getInt("foo") : -1;

หรือถ้าคุณสามารถรับประกันได้ว่าคุณใช้สิทธิประเภทคอลัมน์ DB เพื่อให้ResultSet#getObject()มันส่งกลับInteger(จึงไม่Long, ShortหรือByte) แล้วคุณยังสามารถเพียง typecast Integerไปยัง

Integer foo = (Integer) resultSet.getObject("foo");

1
ไม่ แต่มันจะสร้างวัตถุจำนวนเต็มที่ไม่จำเป็นในกรณีที่ไม่ใช่ค่า null (และไดรเวอร์ BTW ส่วนใหญ่ JDBC จะไม่ตี db ในระหว่างการเรียกเมธอด ResultSet เลย ... โดยทั่วไปคุณจะไม่ได้รับ ResultSet กลับมาจนกว่าข้อมูลทั้งหมดจะมาถึงสาย)
EricS

ขึ้นอยู่กับขนาดการดึงข้อมูล ในไดรเวอร์ส่วนใหญ่ค่าเริ่มต้นคือ 10 แถวและเมื่อดึงการดึงข้อมูลแล้วพวกเขาจะถูกประมวลผล แต่การดึงข้อมูลครั้งต่อไปจะไม่ถูกดึงจนกว่าการประมวลผลจะเสร็จสิ้น
Kristo Aun

ฉันแปลกใจที่คำตอบของคุณไม่ใช่คำตอบที่ดีกว่า :-) ฉันลงคะแนนเพราะเป็นคำตอบที่เหมาะสมที่หลีกเลี่ยงวิธีที่ไม่ปลอดภัย wasNull () ที่ไม่ปลอดภัย สำหรับฉันมันเป็นเหตุผลเสริมที่จะหยุดใช้ Java ;-) และยังคงใช้ VB.Net ที่ RecordSet ได้แก้ไขปัญหาที่ง่ายนี้มานานกว่า 10 ปี!
schlebe

11

AFAIK คุณสามารถใช้

iVal = rs.getInt("ID_PARENT");
if (rs.wasNull()) {
  // do somthing interesting to handle this situation
}

แม้ว่ามันจะเป็นค่า NULL


5

เพียงแค่อัปเดตด้วย Java Generics

คุณสามารถสร้างวิธีการยูทิลิตี้เพื่อดึงค่าตัวเลือกของประเภท Java ใด ๆ จาก ResultSet ที่กำหนดก่อนหน้านี้หล่อ

น่าเสียดายที่ getObject (columnName, Class) จะไม่ส่งคืนค่า null แต่เป็นค่าเริ่มต้นสำหรับประเภท Java ที่กำหนดดังนั้นจึงต้องมีการโทร 2 สาย

public <T> T getOptionalValue(final ResultSet rs, final String columnName, final Class<T> clazz) throws SQLException {
    final T value = rs.getObject(columnName, clazz);
    return rs.wasNull() ? null : value;
}

ในตัวอย่างนี้รหัสของคุณอาจมีลักษณะดังนี้:

final Integer columnValue = getOptionalValue(rs, Integer.class);
if (columnValue == null) {
    //null handling
} else {
    //use int value of columnValue with autoboxing
}

ยินดีที่จะรับข้อเสนอแนะ


2

คุณสามารถเรียกวิธีนี้ได้โดยใช้ resultSet และชื่อคอลัมน์ที่มีประเภท Number มันจะส่งกลับค่าจำนวนเต็มหรือเป็นโมฆะ จะไม่มีศูนย์ส่งกลับสำหรับค่าว่างในฐานข้อมูล

private Integer getIntWithNullCheck(ResultSet rset, String columnName) {
    try {
        Integer value = rset.getInt(columnName);
        return rset.wasNull() ? null : value;
    } catch (Exception e) {
        return null;
    }
}

คุณช่วยอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการแก้ปัญหาได้ไหม?
Sterling Archer

คุณสามารถเรียกวิธีนี้ได้โดยใช้ resultSet และชื่อคอลัมน์ที่มีประเภท Number มันจะส่งกลับค่าจำนวนเต็มหรือเป็นโมฆะ จะไม่มีศูนย์ส่งกลับสำหรับค่าว่างในฐานข้อมูล
amine kriaa

ยอดเยี่ยม แก้ไขที่เป็นคำตอบของคุณ (และลบความคิดเห็นหลังจากการแก้ไข) และคุณมีคำตอบที่ดี :)
สเตอร์ลิงอาร์เชอ

1

ด้วย java 8 คุณสามารถทำได้:

Long nVal = Optional.ofNullable(resultSet.getBigDecimal("col_name"))
                    .map(BigDecimal::longValue).orElse(null));

ในกรณีดังกล่าวคุณต้องแน่ใจว่า nVal จะเป็นโมฆะ (และไม่เป็นศูนย์) หากค่า SQL เป็น NULL


2
แต่ใช้ไม่ได้กับresultSet.getInt("col_name")
rapt

1
ด้วยแหล่งข้อมูล MSSQL ดูเหมือนว่าจะไม่ทำงาน ต้องมีการตรวจสอบเพิ่มเติมของif (rs.wasNull())
alltej

1

เพื่อความสะดวกคุณสามารถสร้างคลาส wrapper รอบ ResultSet ที่ส่งคืนค่า Null โดยResultSetปกติจะไม่

public final class ResultSetWrapper {

    private final ResultSet rs;

    public ResultSetWrapper(ResultSet rs) {
        this.rs = rs;
    }

    public ResultSet getResultSet() {
        return rs;
    }

    public Boolean getBoolean(String label) throws SQLException {
        final boolean b = rs.getBoolean(label);
        if (rs.wasNull()) {
            return null;
        }
        return b;
    }

    public Byte getByte(String label) throws SQLException {
        final byte b = rs.getByte(label);
        if (rs.wasNull()) {
            return null;
        }
        return b;
    }

    // ...

}

-6

อีกวิธีที่ดีในการตรวจสอบถ้าคุณมีการควบคุม SQL คือการเพิ่มค่าเริ่มต้นในแบบสอบถามสำหรับคอลัมน์ int ของคุณเอง จากนั้นตรวจสอบค่านั้น

เช่นสำหรับฐานข้อมูล Oracle ให้ใช้ NVL

SELECT NVL(ID_PARENT, -999) FROM TABLE_NAME;

จากนั้นตรวจสอบ

if (rs.getInt('ID_PARENT') != -999)
{
}

ของหลักสูตรนี้ยังอยู่ภายใต้การสันนิษฐานว่ามีค่าที่ปกติจะไม่พบในคอลัมน์


12
ฉันโหวตคำตอบนี้เนื่องจากมีโอกาสมากที่จะทำให้เกิดปัญหากับผู้คนจำนวนมาก คอลัมน์ int หากกำหนดเป็น nullable มีชุดของค่าที่ประกอบด้วยตัวเลขบวกศูนย์ตัวเลขติดลบและ NULL ณ เวลาใดก็ตามเราสามารถแทรกแถวข้อมูลที่ถูกต้องซึ่งมีหมายเลขเวทย์มนตร์นี้และสิ่งที่ฉับพลันทั้งหมดจะไม่ดี มันเป็นพื้นการใช้งานของรูปแบบการป้องกันเวทมนตร์หมายเลข อย่าทำอย่างนี้
Matthias Hryniszak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.