2016-10-21 21 views
1


java.sql.SQLException中的錯誤:事先準備好的聲明已經完成
當我打電話同preparedStatement時第二次恰好。我用一種方法來調用它。源碼的PreparedStatement敲定

這裏是數據庫中的Java類(相關部分),創建一個收費

//create the charge table. (ps is PreparedStatement) 
try{ 
    statement.executeUpdate("CREATE TABLE charge(username TEXT NOT NULL, date DATETIME DEFAULT CURRENT_TIMESTAMP, charge REAL, PRIMARY KEY(username, date));"); 
} catch (SQLException ex) { 
    System.out.println("charge table creation failed. exception" + ex); 
} 

方法:

public void createCharge(String username, double amount){ 
    try { 
     System.out.println(username + amount); 

     ps = connection.prepareStatement("INSERT INTO charge VALUES(?, ?, ?);"); 
     ps.setString(1, username); 
     ps.setDate(2, DateConvert.toSQLDate(Date.valueOf(LocalDate.MIN))); 
     ps.setDouble(3, amount); 
     ps.executeUpdate(); 
     ps.clearParameters(); 

     System.out.println("Complete"); 

    } catch (SQLException ex) { 
     Logger.getLogger(MontsRentalDatabase.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

這在創建本班:

public void createCharge(String username, double amount){ 
    try { 
     System.out.println(username + amount); 

     ps = connection.prepareStatement("INSERT INTO charge VALUES(?, ?, ?);"); 
     ps.setString(1, username); 
     ps.setDate(2, DateConvert.toSQLDate(Date.valueOf(LocalDate.MIN))); 
     ps.setDouble(3, amount); 
     ps.executeUpdate(); //Line 170 
     ps.clearParameters(); 

     System.out.println("Complete"); 

    } catch (SQLException ex) { 
     Logger.getLogger(MontsRentalDatabase.class.getName()).log(Level.SEVERE, null, ex); 
    } 
} 

將普通日期轉換爲sqldate的類: public class Dat eConvert {

public static java.sql.Date toSQLDate(java.util.Date date){ 
    return new java.sql.Date(date.getTime()); 
} 

public static java.util.Date toJavaDate(java.sql.Date date){ 
    return new java.util.Date(date.getTime()); 
} 
} 

錯誤是在創建費用的行170,這是ps.executeUpdate運行時。第一次運行成功,第二次運行失敗。 日誌:

450100.0 
Complete 
450150.0 
SEVERE: null 
java.sql.SQLException: The prepared statement has been finalized 
    at org.sqlite.core.NativeDB.throwex(NativeDB.java:429) 
    at org.sqlite.core.NativeDB.reset(Native Method) 
    at org.sqlite.core.DB.executeUpdate(DB.java:878) 
    at org.sqlite.jdbc3.JDBC3PreparedStatement.executeUpdate(JDBC3PreparedStatement.java:99) 
at server.RentalDatabase.createCharge(RentalDatabase.java:170) 

感謝您的幫助, Ĵ

+1

如果您已經顯示了所有代碼,而不僅僅是您認爲相關的部分,這將有所幫助。 – Roman

+0

對不起,我要上傳,只需要回家以獲得穩定的數據連接。 – John

回答

6

我相信這是SQLite JDBC驅動程序版本3.14.2.1中的一個錯誤。

在我看來,你正在得到一個唯一的約束衝突異常,但SQLite JDBC驅動程序正試圖報告這個異常,而其他一些異常卻被拋出。

如果我嘗試多次插入相同的數據,例如,我能夠使用sqlite-jdbc版本3.14.2.1來重現您的異常,例如,通過重新運行你的代碼。我降級SQLite的JDBC驅動程序3.8.11.2,我運行代碼後,得到了以下異常:

java.sql.SQLException: [SQLITE_CONSTRAINT] Abort due to constraint violation (UNIQUE constraint failed: charge.username, charge.date) 
    at org.sqlite.core.DB.newSQLException(DB.java:890) 
    at org.sqlite.core.DB.newSQLException(DB.java:901) 
    at org.sqlite.core.DB.execute(DB.java:810) 
    at org.sqlite.core.DB.executeUpdate(DB.java:847) 
    at org.sqlite.jdbc3.JDBC3PreparedStatement.executeUpdate(JDBC3PreparedStatement.java:86) 
    at com.example.MontsRentalDatabase.createCharge(MontsRentalDatabase.java:40) 
    at com.example.Main.main(Main.java:17) 

當然,重新運行該程序時,我試圖插入數據是在表已經。所以預計會有一個獨特的約束違規。

我然後加入行

 statement.execute("DROP TABLE IF EXISTS charge"); 

createChargeTable(),創建表的線之上。然後代碼使用任一版本的SQLite JDBC驅動程序成功運行多次。

此錯誤現在已在sqlite-jdbc版本3.15.1中修復,因此修復程序因此升級到此版本或更高版本。

+0

sqlite-jdbc的問題跟蹤器中可能存在的相關問題:https://github.com/xerial/sqlite-jdbc/issues/74 –

+0

謝謝盧克,我將嘗試降級驅動程序,將在一對夫婦中回覆給你幾小時。 – John

+0

感謝您的幫助,這是由於創建兩個具有相同主鍵的費用,​​將會改變它。 (一旦我將Sqlite-jdbc降級到你的版本,就可以看到它)。 – John

-2

您創建一個準備好的聲明後,你應該關閉它,而不是再次使用它。我在代碼中沒有看到它,但是你說:「當我再次調用同一個預先準備好的語句時會發生這種情況。」 你的「PS」變量應該是局部變量,聲明在try-與資源塊如:

try (PreparedStatement ps = connection.prepareStatement("INSERT INTO charge VALUES(?, ?, ?);")) { 
     ps.setString(1, username); 
     ps.setDate(2, DateConvert.toSQLDate(Date.valueOf(LocalDate.MIN))); 
     ps.setDouble(3, amount); 
     ps.executeUpdate(); //Line 170 
     ps.clearParameters(); 
     System.out.println("Complete"); 
    } 

這樣其關閉後無法使用。

+0

反對:「在您創建準備好的聲明之後,您應該關閉它,而不是再次使用它。」 。這是錯誤的。來自PreparedStatement JavaDoc:「然後可以使用此對象多次高效地執行此語句。」 – Paulo

+0

我完全同意他們可以(也應該)按照你的建議使用,但是在他的代碼中,他每次都在重新創建它們,所以他沒有利用這一點。因此,使用局部變量會更好,我認爲這可以解決他的問題。 –

+0

可能它會使代碼工作。但這不是重點。他試圖理解他的代碼有什麼問題。你沒有說出來。 – Paulo