簡體   English   中英

Java和SQL:返回null或拋出異常?

[英]Java and SQL : return null or throw exception?

這是另一個有爭議的主題,但這次我只搜索簡單而有記錄的答案。 場景:

我們假設以下方法:

  public static Hashtable<Long, Dog> getSomeDogs(String colName, String colValue) { Hashtable<Long, Dog> result = new Hashtable<Long, Dog>(); StringBuffer sql = null; Dog dog = null; ResultSet rs = null; try { sql = new StringBuffer(); sql.append("SELECT * FROM ").append("dogs_table"); sql.append(" WHERE ").append(colName).append("='"); sql.append(colValue).append("'"); rs = executeQuery(sql.toString()); while (rs.next()) { dog= new Dog(); //...initialize the dog from the current resultSet row result.put(new Long(dog.getId()), dog); } } catch (Exception e) { createErrorMsg(e); result = null; //i wonder.... } finally { closeResultSet(rs); //this method tests for null rs and other stuff when closing the rs. } return result; } 

問題:

1.你有什么方法可以改善這種歸還某些狗的技術?

2. rs.next()將為null ResultSet返回false,或者將生成一個異常,如下所示:

String str = null; 的System.out.println(str.toString());

3.如果在從ResultSet的當前行初始化dog對象時發生了一些不好的事情,例如:連接失敗,不兼容的值已經傳遞給dog屬性設置器等等,該怎么辦? 我現在可能在哈希表中有10個元素,或者沒有(第一行)。 下一步操作是什么:a)返回空哈希表; b)返回結果哈希表,它在這個階段的方式; c)拋出異常:這里的異常類型是什么?

4.我想你們都會同意這一點:沒有什么不好的事情發生,審訊中沒有行,將返回空值。 但是,@ThorbjørnRavnAndersen 在這里說我應該返回一個NullObject而不是null值。 我不知道那是什么。

5.我注意到人們和一群人說應該將應用程序分成幾層或某種層次。 考慮到上面的例子,這里有哪些層,除了我能想到的這些層:

Layer1 :: Database層,執行操作:此方法。

Layer2 :: ??? :構建新Dog對象的一些層:我的Dog對象。

Layer3 ::? :我打算用狗的集合做一些事情的層:主要是GUI層,或用戶界面的子層。

在應用程序流程之后,如果在第一層中發生異常,最好的想法是什么呢? 我的想法:捕獲異常,記錄異常,返回一些值。 這是最好的做法嗎?

Manny感謝您的回答,我期待着看到其他人對這些問題的看法。

我會避免以下情況

   sql.append("SELECT * FROM ").append("dogs_table");
   sql.append(" WHERE ").append(colName).append("='");
                        sql.append(colValue).append("'");

而是使用PreparedStatement及其相關的參數setter方法( setString() )等。這將防止colValue值有引號和SQL注入攻擊(或更一般地, colValue形成一些SQL語法)的問題。

如果集合只是空的,我永遠不會返回null。 這看起來非常違反直覺,從客戶的角度來看完全出乎意料。

我不建議在錯誤條件下返回null,因為您的客戶端必須明確檢查這個(並且可能會忘記)。 如果需要,我會返回一個空集合(這可能類似於你的注釋,一個空對象),或者更可能拋出異常(取決於環境和嚴重性)。 該例外是有用的,因為它將攜帶與遇到的錯誤有關的一些信息。 Null告訴你什么。

如果在構建Dog對象時遇到問題,您應該怎么做? 我認為這取決於您希望應用程序的強大和彈性。 返回Dog的一個子集是一個問題,還是完全是災難性的,你需要報告這個? 這是一個應用程序要求(我必須滿足過去的任何一種情況 - 盡力而為全有或全無 )。

幾點意見。 我使用的是HashMap而不是舊的Hashtable (對於所有訪問都是同步的,更重要的是,不是一個合適的Collection - 如果你有一個Collection你可以將它傳遞給任何其他期望任何 Collection方法),並且StringBuilder通過StringBuffer出於類似的原因。 不是一個大問題,但值得了解。

你問五個問題

1.你有什么方法可以改善這種歸還某些狗的技術?

實際上有幾個。

  • 你的方法是靜態的 - 這並不可怕,但會導致你使用另一個靜態的“executeQuery”,這對我來說有點像辛格爾頓...
  • “Dogs”這個類違反了OO的命名習慣 - 復數名詞不能成為好的類名,除非該類的一個實例擁有一系列東西 - 而且似乎Dogs實際上是“Dog”。
  • HashTable幾乎全部棄用。 HashMap或ConcurrentHashMap提供更好的性能。
  • 我看不出有理由用多個追加創建查詢的第一部分 - 它不錯,但它的可讀性低於它可能的,所以sql.append(“SELECT * FROM dogs_table WHERE”); 如果您只是要對所選列(*)和表名(dogs_table)進行硬編碼,那么這將是一個更明智的開端。

2. rs.next()將為null ResultSet返回false,否則將生成異常

這似乎不是一個問題,但是,只要沒有任何行要處理,rs.next()就會返回false。

3.如果在從ResultSet的當前行初始化dog對象時發生了一些不好的事情

如果“發生了一些不好的事情”,你接下來要做的就取決於你和你的設計。 有寬容的方法(返回你可以的所有行)和不寬容(拋出異常)。 我傾向於傾向於“無情”的方法,因為使用“寬容”的方法,用戶不會知道你沒有返回所有存在的行 - 只是你在錯誤之前得到的所有行。 但是可能存在寬容方法的情況。

4.我想你們都會同意這一點:沒有什么不好的事情發生,審訊中沒有行,將返回空值。

這不是一個明顯正確答案的東西。 首先,它不是所寫方法中發生的事情。 它將返回一個空的HashTable(這是“null對象”的意思)。 其次,在“未找到結果”的情況下,null並不總是答案。

我見過null,但我也看到了一個空結果變量。 我聲稱它們都是正確的方法,但我更喜歡空的結果變量。 但是,最好保持一致,所以選擇一種返回“無結果”並堅持下去的方法。

5.我注意到人們和一群人說應該將應用程序分成幾層或某種層次。

這比其他人更難回答,而沒有看到你的應用程序的其余部分。

空對象模式是一種設計模式,您總是返回一個對象以避免NPE:s和代碼中的任何空檢查。 在你的情況下,這意味着不是返回null ,而是返回一個空的Hashtable<Long, Dogs>

原因是,因為它是一個集合而你的其他代碼會這樣訪問它,所以如果你返回一個空集合它就不會分解; 它不會被迭代,它不會包含任何令人驚訝的東西,它不會導致NPE:s被拋出等等。

確切地說,Null對象是類/接口的特殊實現,它絕對沒有任何作用,因此沒有任何類型的副作用。 由於它不使用null性質會使你的代碼更清晰,因為當你知道你總是從你的方法調用中得到一個對象, 無論在方法內部發生什么,你甚至不必檢查空值,也不需要代碼對他們作出反應! 因為Null Object沒有做任何事情,你甚至可以將它們作為單身只是躺在那里,從而通過這樣做來節省內存。

不要通過連接字符串來構建SQL查詢,就像你正在做的那樣:

sql = new StringBuffer();
sql.append("SELECT * FROM ").append("dogs_table");
sql.append(" WHERE ").append(colName).append("='");
sql.append(colValue).append("'");

這使得您的代碼容易受到眾所周知的安全攻擊, SQL注入攻擊。 而不是這樣做,使用PreparedStatement並通過調用相應的set...()方法來設置參數。 注意,您只能使用它來設置列 ,您不能使用它來動態構造列 ,就像您正在做的那樣。 例:

PreparedStatement ps = connection.prepareStatement("SELECT * FROM dogs_table WHERE MYCOL=?");
ps.setString(1, colValue);

rs = ps.executeQuery();

如果使用PreparedStatement ,JDBC驅動程序將自動處理轉義colValue中可能存在的某些字符,以便SQL注入攻擊不再起作用。

如果發生錯誤,則發生異常。 如果沒有數據,則返回空集合,而不是null。 (另外,通常你應該返回更通用的'Map',而不是具體的實現),

通過使用Spring-JDBC而不是普通的JDBC,可以顯着減少樣板JDBC代碼的數量。 這是使用Spring-JDBC重寫的相同方法

public static Hashtable<Long, Dogs> getSomeDogs(String colName, String colValue) {

    StringBuffer sql = new StringBuffer();
    sql.append("SELECT * FROM ").append("dogs_table");
    sql.append(" WHERE ").append(colName).append("='");
    sql.append(colValue).append("'");

    Hashtable<Long, Dogs> result = new Hashtable<Long, Dogs>();

    RowMapper mapper = new RowMapper() {

        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
            Dogs dog = new Dogs();
            //...initialize the dog from the current resultSet row
            result.put(new Long(dog.getId()), dog);
        }
    };
    (Hashtable<Long, Dogs>) jdbcTemplate.queryForObject(sql, mapper);
}

Spring負責:

  1. 迭代ResultSet
  2. 關閉ResultSet
  3. 一致地處理異常

正如其他人所提到的,你真的應該使用PreparedStatement來構造SQL而不是String(或StringBuffer)。 如果由於某種原因你不能這樣做,你可以通過構建像這樣的SQL來提高查詢的可讀性:

    String sql = 
        "SELECT * FROM dogs_table " +
        "WHERE " + "colName" + " = '" + colValue + "'";

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM