JdbcTemplate queryForInt / Long เลิกใช้แล้วใน Spring 3.2.2 ควรแทนที่ด้วยอะไร


105

เมธอด queryforInt / queryforLong ใน JdbcTemplate เลิกใช้แล้วใน Spring 3.2 ฉันไม่สามารถหาสาเหตุหรือสิ่งที่ถือเป็นแนวทางปฏิบัติที่ดีที่สุดในการแทนที่โค้ดที่มีอยู่โดยใช้วิธีการเหล่านี้

วิธีการทั่วไป:

int rowCount = jscoreJdbcTemplate.queryForInt(
    "SELECT count(*) FROM _player WHERE nameKey = ? AND teamClub = ?",
    playerNameKey.toUpperCase(),
    teamNameKey.toUpperCase()
);

ตกลงวิธีการข้างต้นจะต้องเขียนใหม่ดังนี้:

Object[] params = new Object[] { 
   playerNameKey.toUpperCase(), 
   teamNameKey.toUpperCase()
};
int rowCount = jscoreJdbcTemplate.queryForObject(
    "SELECT count(*) FROM _player WHERE nameKey = ? AND teamClub = ?",
    params, Integer.class);

เห็นได้ชัดว่าการเลิกใช้งานนี้ทำให้คลาส JdbcTemplate ง่ายขึ้น (หรือไม่) QueryForInt เป็นวิธีที่สะดวกเสมอ (ฉันเดา) และมีมานานแล้ว ทำไมถึงถูกลบออก รหัสมีความซับซ้อนมากขึ้นเป็นผลให้


รายละเอียดนี้เลิกใช้วิธีการ: static.springsource.org/spring/docs/current/javadoc-api/…
Dan MacBean

คุณพูดถูกฉันไม่รู้ว่าทำไมแหล่งที่มาของฉันไม่มี@Deprecated
Sotirios Delimanolis

อัปเดตเวอร์ชัน Spring เป็น 3.2.2 - ดูเหมือนว่าจะเลิกใช้งานครั้งแรกที่นี่
Dan MacBean

ฉันอัปเกรดโค้ดเบสที่มีอยู่จาก 3.1 เป็น 3.2.2 และวิธีการเหล่านี้ถูกนำไปใช้ทั่วทุกที่ ต้องเข้าใจสาเหตุและวิธีการอัปเดตโค้ด
Dan MacBean

โปรดทราบว่า queryForObject อาจส่งคืนnull(ไม่ใช่กรณีในตัวอย่างของคุณ) ฉันไม่พบวิธีอื่นนอกจากทำซ้ำตอนนี้รหัสตรวจสอบ null จาก queryForInt / Long
hochraldo

คำตอบ:


110

สิ่งที่ฉันคิดก็คือมีคนตระหนักว่าเมธอด queryForInt / Long มีความหมายที่สับสนนั่นคือจากซอร์สโค้ด JdbcTemplate คุณสามารถเห็นการใช้งานปัจจุบัน:

@Deprecated
public int queryForInt(String sql, Object... args) throws DataAccessException {
    Number number = queryForObject(sql, args, Integer.class);
    return (number != null ? number.intValue() : 0);
}

ซึ่งอาจทำให้คุณคิดว่าหากชุดผลลัพธ์ว่างเปล่ามันจะส่งคืน 0 อย่างไรก็ตามมันมีข้อยกเว้น:

org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

ดังนั้นการใช้งานต่อไปนี้จึงเทียบเท่ากับการใช้งานปัจจุบัน:

@Deprecated
public int queryForInt(String sql, Object... args) throws DataAccessException {
    return queryForObject(sql, args, Integer.class);
}

จากนั้นโค้ดที่ไม่เลิกใช้งานตอนนี้จะต้องถูกแทนที่ด้วยน่าเกลียด:

    queryForObject(sql, new Object { arg1, arg2, ...}, Integer.class);

หรือสิ่งนี้ (ดีกว่า):

    queryForObject(sql, Integer.class, arg1, arg2, ...);

12
นั่นไม่เป็นความจริง ข้อมูลโค้ดที่สามไม่เท่ากับการนำไปใช้งาน! เนื่องจากมี NPE ซ่อนอยู่พร้อมกับการแกะกล่องอัตโนมัติ หากการสืบค้นของคุณส่งคืนผลลัพธ์ แต่เป็นค่าว่างรหัสก่อนหน้านี้จะส่งคืน 0 แทนค่าว่าง - เพื่อให้เกิดพฤติกรรมก่อนหน้านี้อย่างถูกต้องผลลัพธ์จำนวนเต็ม = queryForObject (sql, args, Integer.class); ผลตอบแทน == null? 0: ผล;
MetroidFan2002

@ MetroidFan2002: การสังเกตของคุณเป็นเรื่องจริง! อย่างไรก็ตามจากมุมมองของการออกแบบ API หากแบบสอบถามส่งคืนค่า NULL เพียงค่าเดียวฉันเชื่อว่าจะดีกว่าที่จะส่งคืนตามที่เป็นอยู่แทนที่จะระบุว่า (เป็น queryForInt do) NULL จะเทียบเท่ากับ 0 มันคืองาน ของผู้ใช้ API เพื่อประเมินเงื่อนไขประเภทนั้น
Gabriel Belingueres

ปัญหาคือถ้าและเมื่อผู้ใช้ได้รับ NPE ที่นั่นเว้นแต่พวกเขาจะตั้งค่าบางอย่างอย่างชัดเจนในสภาพแวดล้อมของพวกเขา (ตัวอย่างเช่น Eclipse มีตัวเลือกในการเน้นการใช้งาน autoboxing) NPE ในบรรทัดนั้นจะดูเหมือนอินสแตนซ์ JDBCOperations เป็นโมฆะ ก่อนหน้านี้จะส่งคืนศูนย์ไปแล้ว ตอนนี้ทำไมคุณถึงใช้สิ่งนี้ในแบบสอบถามที่คืนค่า null ฉันไม่รู้ (โดยพื้นฐานแล้วเกิดจากการที่ n00bs ทำสิ่งนี้ซึ่งจะทำได้) แต่การกำจัดสิ่งเหล่านี้ออกไปไม่ใช่การย้าย IMO ที่ดี
MetroidFan2002

ฉันพบว่าสาเหตุที่เป็นไปได้นั้นเป็นเพราะความไม่ถูกต้อง ฉันมีค่าแบบยาว 10,000000233174211 ที่ถูกส่งคืนโดย queryForLong (String) แต่กลับส่งคืน 10,000000233174212 แทนเช่น +1 ฉันดูในรหัสและมันแปลง Double เป็น Long ดังนั้นอาจมีปัญหากับการแปลง
mrswadge

เมื่อนึกถึงความคิดเห็นของฉันด้านบนอีกเล็กน้อยประเภทข้อมูลสำหรับคอลัมน์คือหมายเลข (19,0) ดังนั้นอาจเป็นสาเหตุที่ทำให้ double เข้ามามีบทบาท ฉันแก้ไขปัญหาโดยใช้ queryForObject (sql, Long.class) แต่อย่างใด
mrswadge

35

ฉันเห็นด้วยกับผู้โพสต์ต้นฉบับว่าการเลิกใช้คิวรีวิธีอำนวยความสะดวก (sql) เป็นสิ่งที่ไม่สะดวก

ฉันได้พัฒนาแอปโดยใช้ Spring 3.1 และเพิ่งอัปเดตเป็นเวอร์ชัน Spring ล่าสุด (3.2.3) และสังเกตเห็นว่าแอปนั้นเลิกใช้แล้ว

โชคดีที่มันเป็นการเปลี่ยนแปลงหนึ่งบรรทัดสำหรับฉัน:

return jdbcTemplate.queryForLong(sql);  // deprecated in Spring 3.2.x

ถูกเปลี่ยนเป็น

return jdbcTemplate.queryForObject(sql, Long.class);

และการทดสอบหน่วยสองสามข้อดูเหมือนจะบ่งชี้ว่าการเปลี่ยนแปลงข้างต้นใช้ได้ผล


จุดดี. มันจะทำงานได้ดีโดยไม่ต้องมีวงเล็บด้วย :)
SGB


13

การเปลี่ยนรหัสดังกล่าว:

long num = jdbcTemplate.queryForLong(sql);

ด้วยรหัสนี้:

long num = jdbcTemplate.queryForObject(sql, Long.class);

เป็นสิ่งที่อันตรายมากเพราะถ้าคอลัมน์มีค่า null queryForObject return null และอย่างที่เรารู้ว่าประเภทดั้งเดิมไม่สามารถเป็น null ได้และคุณจะมี NullPointerException คอมไพเลอร์ไม่ได้เตือนคุณเกี่ยวกับเรื่องนี้ คุณจะทราบเกี่ยวกับข้อผิดพลาดนี้เมื่อรันไทม์ ข้อผิดพลาดเดียวกันคุณจะมีหากคุณมีวิธีที่ส่งคืนประเภทดั้งเดิม:

public long getValue(String sql) {
    return = jdbcTemplate.queryForObject(sql, Long.class);
}

แบบสอบถามเมธอดที่เลิกใช้แล้วใน JdbcTemplate ใน Spring 3.2.2 มีเนื้อความต่อไปนี้:

@Deprecated
public long queryForLong(String sql) throws DataAccessException {
    Number number = queryForObject(sql, Long.class);
    return (number != null ? number.longValue() : 0);
}

คุณจะเห็นก่อนที่มันจะส่งคืนค่าดั้งเดิมมีการตรวจสอบว่านี่ไม่ใช่โมฆะและถ้ามันเป็นโมฆะพวกเขาจะคืนค่า 0 โดยวิธีการ - ควรเป็น 0L


3
2 เซนต์: คอมไพเลอร์อาจเตือนคุณเกี่ยวกับเรื่องนี้หากคุณเปิดใช้งานคำเตือนการทำกล่องอัตโนมัติ
keiki

ฉันไม่รู้เรื่องนั้น ขอบคุณเพื่อน :)
Marcin Kapusta

2

JdbcTemplate#queryForIntส่งคืน 0 ถ้าค่าคอลัมน์เป็น SQL NULL หรือ 0 ไม่มีวิธีใดที่จะแยกแยะกรณีหนึ่งออกจากอีกกรณีหนึ่ง ฉันคิดว่านี่เป็นสาเหตุหลักที่ทำให้วิธีนี้เลิกใช้งาน BTW ResultSet#getIntทำงานในลักษณะเดียวกัน ResultSet#wasNullแม้ว่าเราสามารถแยกแยะความแตกต่างระหว่างทั้งสองกรณี


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.