2014-02-09 154 views
3

我正在使用Servlet和JSP執行CRUD操作。以下類用於從服務器(Tomcat)維護的連接池中檢索連接。使用Servlet/JSP執行CRUD操作

public final class DatabaseConnection { 

    private static final DataSource dataSource; 

    static { 
     try { 
      Context initContext = new InitialContext(); 
      Context context = (Context) initContext.lookup("java:/comp/env"); 
      dataSource = (DataSource) context.lookup("jdbc/assignment_db"); 
     } catch (NamingException e) { 
      Logger.getLogger(DatabaseConnection.class.getName()).log(Level.SEVERE, null, e); 
      throw new ExceptionInInitializerError("DataSource not initialized."); 
     } 
    } 

    public static Connection getConnection() throws SQLException { 
     return dataSource.getConnection(); 
    } 
} 

以下類(DAO)中的方法執行CRUD操作。

public final class CountryDao { 

    public Long getCurrentRow(Long id) throws SQLException { 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 
     ResultSet resultSet = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("select rownum from (select @rownum:[email protected]+1 as rownum, tbl.country_id from country_tbl tbl, (select @rownum:=0)t order by tbl.country_id desc)t where country_id=?"); 
      preparedStatement.setLong(1, id); 
      resultSet = preparedStatement.executeQuery(); 
      return resultSet.next() ? resultSet.getLong("rownum") : 1; 
     } finally { 
      if (connection != null) {connection.close();} 
      if (resultSet != null) {resultSet.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 
    } 

    public Long rowCount() throws SQLException { 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 
     ResultSet resultSet = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("select count(*) as cnt from country_tbl"); 
      resultSet = preparedStatement.executeQuery(); 
      resultSet.next(); 
      return resultSet.getLong("cnt"); 
     } finally { 
      if (connection != null) {connection.close();} 
      if (resultSet != null) {resultSet.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 
    } 

    public List<CountryBean> getData(Long currentPage, Long pageSize) throws SQLException { 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 
     ResultSet resultSet = null; 
     List<CountryBean> countryBeans = new ArrayList<CountryBean>(); 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("select * from country_tbl order by country_id desc limit ?,?"); 

      //preparedStatement.setMaxRows(pageSize); 
      preparedStatement.setLong(1, currentPage); 
      preparedStatement.setLong(2, pageSize); 
      resultSet = preparedStatement.executeQuery(); 

      while (resultSet.next()) { 
       CountryBean countryBean = new CountryBean(); 
       countryBean.setCountryId(resultSet.getLong("country_id")); 
       countryBean.setCountryName(resultSet.getString("country_name")); 
       countryBean.setCountryCode(resultSet.getString("country_code")); 
       countryBeans.add(countryBean); 
      } 
     } finally { 
      if (connection != null) {connection.close();} 
      if (resultSet != null) {resultSet.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 

     return countryBeans; 
    } 

    public boolean delete(Long id) throws SQLException { 
     boolean status = false; 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("delete from country_tbl where country_id=?"); 
      preparedStatement.setLong(1, id); 

      if (preparedStatement.executeUpdate() == 1) { 
       status = true; 
      } 
     } finally { 
      if (connection != null) {connection.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 
     return status; 
    } 

    public boolean delete(Long[] ids) throws SQLException { 
     boolean status = false; 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      connection.setAutoCommit(false); 
      preparedStatement = connection.prepareStatement("delete from country_tbl where country_id=?"); 
      int len = ids.length; 

      for (int i = 0; i < len; i++) { 
       preparedStatement.setLong(1, ids[i]); 
       preparedStatement.addBatch(); 
      } 

      preparedStatement.executeBatch(); 
      connection.commit(); 
      status = true; 
     } finally { 
      if (connection != null) {connection.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 
     return status; 
    } 

    public boolean insert(String countryName, String countryCode) throws SQLException { 
     boolean status = false; 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("insert into country_tbl(country_name, country_code)values(?,?)"); 
      preparedStatement.setString(1, countryName); 
      preparedStatement.setString(2, countryCode); 
      preparedStatement.executeUpdate(); 
      status = true; 
     } finally { 
      if (connection != null) {connection.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 
     return status; 
    } 

    public boolean update(Long countryId, String countryName, String countryCode) throws SQLException { 
     boolean status = false; 
     Connection connection = null; 
     PreparedStatement preparedStatement = null; 

     try { 
      connection = DatabaseConnection.getConnection(); 
      preparedStatement = connection.prepareStatement("update country_tbl set country_name=?, country_code=? where country_id=?"); 
      preparedStatement.setString(1, countryName); 
      preparedStatement.setString(2, countryCode); 
      preparedStatement.setLong(3, countryId); 
      preparedStatement.executeUpdate(); 
      status = true; 
     } finally { 
      if (connection != null) {connection.close();} 
      if (preparedStatement != null) {preparedStatement.close();} 
     } 

     return status; 
    } 
} 

這些方法在執行驗證後從Servlet中被適當調用。反過來,Servlet與JSP(與JSTL/EL一起)進行交互。

只有一個問題。很遺憾,connection,preparedStatementresultSet都是局部變量到特定方法

我可以僅在一個地方聲明它們作爲類成員(實例變量)嗎?這樣做能夠保持一致的狀態嗎?

沒有必要把精力集中在覈心邏輯上。請不要只是說,最好使用MVC框架:)

+0

哎,我不明白您的問題...只是試試這個,HTTP:// publib .boulder.ibm.com/infocenter/db2luw/v8/index.jsp?topic =/com.ibm.db2.udb.doc/ad/tjvpsxqu.htm,http://docs.oracle.com/javase/7/ docs/api/java/sql/PreparedStatement.html – jmail

+0

我很想知道這一點。給出的代碼沒有問題。 – Tiny

回答

3

我只能在一個地方爲類成員(實例變量)聲明呢?

你可以這樣做,但該類將是線程安全的。調用者不能在多個線程中重複使用相同的實例,而不會干擾由不一致狀態引起的每種方法的行爲。以servlet爲調用者的情況下,這種方式不能在servlet的init()中只創建一次實例,並在doXxx()方法中多次重複使用該實例。您將被迫在threadlocal範圍內重新創建實例(因此,在doXxx()方法中)。這應該在課程'javadoc中清楚地記錄。但畢竟,設計一個線程安全的DAO類是沒有意義的。堅持當前的設計(或者,如果你不只是在四處閒逛,請切換到JPA;))。


燦這樣保持一致的狀態精確?

不!相反,它會不一致。您不能在多個查詢中共享同一個語句或結果集。每個查詢都應該有自己的語句和結果集。實例上的每個方法調用都會更改實例變量,導致其他仍在運行的方法調用在受損狀態下工作。共享連接是可能的,但這項工作通常已經由連接池完成。鑑於您使用的是數據源,您很可能已經擁有了一個。


這就是說,如果你不喜歡重複的代碼樣板但是真正要堅持好「OL JDBC API,看看Execute Around pattern/idiom和/或Java 7's automatic resource management (ARM)。有了這個,必須有可能創建一個具有一堆接口的助手DB類,並最終得到一個通用的抽象基類DAO類,其方法只需要一個SQL查詢和參數值(如果有的話)。

+0

只有一件事要確認。將'Connection'作爲實例變量(在代碼示例中的CountryDao中)是否安全?謝謝。 – Tiny

+1

不需要。作爲該方法的參數,您需要能夠在同一個事務中調用多個查詢(以便在一個查詢引發異常時,所有內容都會回滾)。 – BalusC

1

您不能將這些變量聲明爲實例變量。 ,因爲這可能會在某些情況下導致錯誤。

E.g -

如果在同一個對象上調用這個類的兩個方法。 一種方法需要兩分鐘才能執行,另一種方法需要5分鐘 在這種情況下,第一種方法會關閉連接,第二種方法會引發異常。 還有有很多其他的原因...

1

當然,只要您不在兩個或多個不同的線程中共享相同的對象,就可以使用連接,語句和結果集作爲成員變量。

就像在servlet中一樣,你將創建一個dao對象,所以每當servlet被命中時,一個新線程就會啓動,並且這個新線程將創建一個dao的新對象來執行某種方法。

只有在servlet中創建dao對象並將此相同對象傳遞給由您手動創建並執行兩種方法的兩個不同線程的情況下。在這種情況下,可能有一個線程正在讀取結果集中的所有記錄,但同時其他線程執行get查詢並嘗試重置結果集,這將導致異常。

@Manjeet第一個線程仍然讀取結果集中的數據,第二個線程剛剛關閉連接,這更有趣。但他忘了提及它只會在你創建兩個線程時纔會發生。如果你會在同一個線程中執行兩個方法,那麼它肯定會一個接一個地執行。因爲沒有其他方法可以同時執行兩個方法而不需要創建兩個線程。

但是,如果你的意圖是要刪除樣板代碼,那麼你有其他幾個方面。您可以創建一個名爲樣板類這樣

class Boilerplate{ 
    private Connection con; 
    private PreparedStatement ps; 

    public Boilerplate(String query){ 
    //initialize connection, ps and resultset 
    con=DatabaseConnection.getConnection(); 
    ps=connection.prepareStatement(query); 
    } 

    destroy void Boilerplate(){ 
    if(con!=null) 
     con.close() 
    if(ps!=null) 
     ps.close(); 
    } 
} 

然後得到它的一個實例做你的工作,並調用它的銷燬方法。分步在JSP中創建CRUD操作使用Servlet的

1

一般的東西:

由於知道的術語是很重要的。實例變量和類變量都是成員變量。它們都是成員變量,因爲它們都與特定的類相關聯。但是,實例變量和類變量之間存在差異。

實例變量

實例變量屬於一個類的實例。另一種說法是instance variables屬於一個對象,因爲對象是一個類的實例。每個對象都有自己的實例變量副本。下面是一個實例變量的聲明將是什麼樣子:

實例變量

class Taxes 
    { 
     int count; 
     /*...*/ 
    } 

類變量

class Taxes 
    { 
     static int count; 
     /*...*/ 
    } 
0
res.setContentType("text/html"); 

// Ask for a 16K byte response buffer; do not set the content length 
res.setBufferSize(16 * 1024); 

PrintWriter out = res.getWriter(); 
out.println("<HTML>"); 
out.println("<HEAD><TITLE>Hello World</TITLE></HEAD>"); 
out.println("<BODY>"); 
out.println("<BIG>Less than 16K of response body</BIG>"); 
out.println("</BODY></HTML>"); 
+0

請解釋它與問題的關係。 – Tiny

0

我用你的榜樣,並希望創建一個連接。但是當我使用junit來測試它時,它顯示出一個NoInitialContextException異常。我該如何解決它?下面是我的代碼。

public final class DataSourceUtil {      
    private static Context initCtx; 
    private static Context envCtx; 
    private static DataSource dataSource;     

    static { 
    try {            
     // Initial a datasource for pooled connections.  
     initCtx = new InitialContext();     
     envCtx = (Context) initCtx.lookup("java:/comp/env");  
     dataSource = (DataSource) envCtx.lookup("jdbc/ServletDB");             
    } catch (NamingException e) {      
     Logger.getLogger(DataSourceUtil.class.getName()).log(Level.SEVERE, null, e);        
     throw new ExceptionInInitializerError("DataSource not initialized."); 
    }             
}           

    public static Connection getConnection() throws SQLException { 
    return dataSource.getConnection(); 
    } 
} 

和測試代碼

public class ConnectionTest() { 
    @Test 
    public void connectionTest2() { 
    try { 

    Connection connection = DataSourceUtil.getConnection(); 

    } catch (Exception e) {e.printStackTrace();} 
} 

和錯誤消息:

Dec 23, 2015 9:06:46 PM com.wxz.server.dao.DataSourceUtil <clinit> 
SEVERE: null 
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial 
+0

這是一個不同的問題。如果你打開了一個新線程而不是詢問個人,也就是說只有我在哪裏,如果有必要,你可以提供這個問題的鏈接,你很可能會得到更好的幫助。使用JUnit/Mockito進行單元測試與實際開發環境完全不同,您可能需要執行不同的JNDI查找,而我完全不瞭解這些查找。 – Tiny

+0

好吧,我會嘗試創建一個新線程和一個問題。您的示例代碼是否適合您?我的意思是在靜態區域,它不能在我的代碼中初始化上下文實例。但是在類的構造函數中確實有效。謝謝你的幫助。 :) – Sam

+0

是的,在問這個問題的時候工作正常。通過靜態初始化程序中的JNDI查找來初始化DataSouce可能不得不使用所使用的測試工具,例如JUnit,這些工具只能由具有該工具經驗的人員指出並積極關注開發測試用例。 – Tiny