Jsp+Servlet+JDBC的使用復習


最近對JDBC進行了復習,對事物的理解,連接池的使用等部分都有一個復習,所以使用Servlet+JDBC完成了一個小Demo,在這里對這種底層的操作進行總結。框架的使用的確方便了我們的開發,但是底層的實現也不應該忘記

在這里還是用Web三層的分層結構,只不過是:表示層(Web層)使用Servlet,業務層還是使用Service(在這里,Service的作用並不明顯,只是調用Dao層的方法),持久層(Dao層)

我做的這個小項目使用的Jar有:c3p0-0.9.2.jar (連接池),mchange-commons-0.2.jar(連接池需要依賴), commons-beanutils-1.8.3.jar (簡化數據bean的封裝),commons-dbutils-1.4.jar (簡化JDBC),commons-logging-1.1.1.jar(日志) ,mysql-connector-java-5.1.28-bin.jar(數據庫驅動) ,jstl-1.2.jar(我在jsp中使用了JSTL標簽)

需要的配置文件有:c3p0-config.xml

額外的類有:JdbcUtils.java (配置連接池和事務), TxQueryRunner.java (處理線程的類,繼承於QueryRunner),CommonUtils.java (一個小工具類,提供獲得UUID和將map轉換為對應的JavaBean),BaseServlet.java(多個Servlet方便操作)

其實,在前面的復習文章中我已經將以上文件的源碼分享出來了,這里在粘一份了

c3p0-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
    <default-config> 
<!--記得換成項目使用的數據庫-->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/customers</property>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="user">root</property>
        <property name="password">root</property>
        
        <property name="acquireIncrement">3</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">2</property>
        <property name="maxPoolSize">10</property>
    </default-config>
</c3p0-config>

在使用c3p0是會自動加載這個c3p0-config.xml配置文件,也就是  new ComboPooledDataSource()  的時候,所以只要我們將這個配置文件放對位置(src下)就會可以了,無需自己去解析配置文件,c3p0內部已經做了這個工作

 

JdbcUtils.java:

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * 使用本類的方法,必須提供c3p0-copnfig.xml文件
 */
public class JdbcUtils {

    private static DataSource ds = new ComboPooledDataSource();
    
    /**
     * 它為null表示沒有事務
     * 它不為null表示有事務
     * 當開啟事務時,需要給它賦值
     * 當結束事務時,需要給它賦值為null
     * 並且在開啟事務時,讓dao的多個方法共享這個Connection
     */
    private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    
    public static DataSource getDataSource() {
        return ds;
    }
    
    /**
     * dao使用本方法來獲取連接
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        /*
         * 如果有事務,返回當前事務的con
         * 如果沒有事務,通過連接池返回新的con
         */
        Connection con = tl.get();//獲取當前線程的事務連接
        if(con != null) return con;
        return ds.getConnection();
    }
    
    /**
     * 開啟事務
     * @throws SQLException 
     */
    public static void beginTransaction() throws SQLException {
        Connection con = tl.get();//獲取當前線程的事務連接
        if(con != null) throw new SQLException("已經開啟了事務,不能重復開啟!");
        con = ds.getConnection();//給con賦值,表示開啟了事務
        con.setAutoCommit(false);//設置為手動提交
        tl.set(con);//把當前事務連接放到tl中
    }
    
    /**
     * 提交事務
     * @throws SQLException 
     */
    public static void commitTransaction() throws SQLException {
        Connection con = tl.get();//獲取當前線程的事務連接
        if(con == null) throw new SQLException("沒有事務不能提交!");
        con.commit();//提交事務
        con.close();//關閉連接
        con = null;//表示事務結束!
        tl.remove();
    }
    
    /**
     * 回滾事務
     * @throws SQLException 
     */
    public static void rollbackTransaction() throws SQLException {
        Connection con = tl.get();//獲取當前線程的事務連接
        if(con == null) throw new SQLException("沒有事務不能回滾!");
        con.rollback();
        con.close();
        con = null;
        tl.remove();
    }
    
    /**
     * 釋放Connection
     * @param con
     * @throws SQLException 
     */
    public static void releaseConnection(Connection connection) throws SQLException {
        Connection con = tl.get();//獲取當前線程的事務連接
        if(connection != con) {//如果參數連接,與當前事務連接不同,說明這個連接不是當前事務,可以關閉!
            if(connection != null &&!connection.isClosed()) {//如果參數連接沒有關閉,關閉之!
                connection.close();
            }
        }
    }
}

TxQueryRunner.java:

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;

public class TxQueryRunner extends QueryRunner {

    @Override
    public int[] batch(String sql, Object[][] params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int[] result = super.batch(con, sql, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
            throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.query(con, sql, rsh, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }
    
    @Override
    public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        T result = super.query(con, sql, rsh);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con, sql);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object param) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con, sql, param);
        JdbcUtils.releaseConnection(con);
        return result;
    }

    @Override
    public int update(String sql, Object... params) throws SQLException {
        Connection con = JdbcUtils.getConnection();
        int result = super.update(con, sql, params);
        JdbcUtils.releaseConnection(con);
        return result;
    }
}

CommonUtils.java:

import java.util.Map;
import java.util.UUID;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;

/**
 * 小小工具
 *
 */
public class CommonUtils {
    /**
     * 返回一個不重復的字符串
     * @return
     */
    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "").toUpperCase();
    }

    /**
     * 把map轉換成對象
     * @param map
     * @param clazz
     * @return
     * 
     * 把Map轉換成指定類型
     */
    @SuppressWarnings("rawtypes")
    public static <T> T toBean(Map map, Class<T> clazz) {
        try {
            /*
             * 1. 通過參數clazz創建實例
             * 2. 使用BeanUtils.populate把map的數據封閉到bean中
             */
            T bean = clazz.newInstance();
            ConvertUtils.register(new DateConverter(), java.util.Date.class);
            BeanUtils.populate(bean, map);
            return bean;
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
}

BaseServlet.java:

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * BaseServlet用來作為其它Servlet的父類
 * 
 * 
 *         一個類多個請求處理方法,每個請求處理方法的原型與service相同! 原型 = 返回值類型 + 方法名稱 + 參數列表
 */
@SuppressWarnings("serial")
public class BaseServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");//處理響應編碼
        request.setCharacterEncoding("UTF-8");
        
        /**
         * 1. 獲取method參數,它是用戶想調用的方法 2. 把方法名稱變成Method類的實例對象 3. 通過invoke()來調用這個方法
         */
        String methodName = request.getParameter("method");
        Method method = null;
        /**
         * 2. 通過方法名稱獲取Method對象
         */
        try {
            method = this.getClass().getMethod(methodName,
                    HttpServletRequest.class, HttpServletResponse.class);
        } catch (Exception e) {
            throw new RuntimeException("您要調用的方法:" + methodName + "它不存在!", e);
        }
        
        /**
         * 3. 通過method對象來調用它
         */
        try {
            String result = (String)method.invoke(this, request, response);
            if(result != null && !result.trim().isEmpty()) {//如果請求處理方法返回不為空
                int index = result.indexOf(":");//獲取第一個冒號的位置
                if(index == -1) {//如果沒有冒號,使用轉發
                    request.getRequestDispatcher(result).forward(request, response);
                } else {//如果存在冒號
                    String start = result.substring(0, index);//分割出前綴
                    String path = result.substring(index + 1);//分割出路徑
                    if(start.equals("f")) {//前綴為f表示轉發
                        request.getRequestDispatcher(path).forward(request, response);
                    } else if(start.equals("r")) {//前綴為r表示重定向
                        response.sendRedirect(request.getContextPath() + path);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

接下來我是從Servlet開始寫:

添加客戶,JavaBean是Customer.java , 里面就是一些屬性和set/get方式,這里不貼出了

    /**
     * 添加客戶
     * 
     */
    public String add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Customer customer = CommonUtils.toBean(request.getParameterMap(), Customer.class);
        customer.setCid(CommonUtils.uuid());
        customerService.add(customer);
        request.setAttribute("msg", "恭喜,添加客戶成功");
        return "f:/msg.jsp";
    }

客戶id使用的是UUID,從表單獲得數據是沒有id的,就需要我們在程序中指定,然后進到Service層,不要忘記在Web.xml中配置我們這個Servlet哦,BaseServlet不需要配置。

Service層做的工作很簡單,就是調用Dao的方法(這里還沒有使用事務):

    public void add(Customer c){
        customerDao.add(c);
    }

Dao層的添加方法:

    /**
     * 添加客戶
     * @param c
     */
    public void add(Customer c){
        try {
            String sql="insert into t_customer values(?,?,?,?,?,?,?)";
            Object[] params={c.getCid(),c.getCname(),c.getGender()
                    ,c.getBirthday(),c.getCellphone(),c.getEmail(),c.getDescription()};
            qr.update(sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

可以看到簡化后的Dao操作很簡單了,分三步,第一步:寫出SQL語句;第二步:設置參數,這里使用Object數組,因為類型不一致,所以使用Object;第三步:使用QueryRunner的update方法(增刪該,查詢使用query)值得注意的是用 TxQueryRunner 類構建我們的QueryRunner:

private QueryRunner qr=new TxQueryRunner();

再看看一個查詢的方法:

    /**
     * 查詢所有客戶
     * @return
     */
    public List<Customer> findAll() {
        try {
            String sql="select * from t_customer";
            return qr.query(sql, new BeanListHandler<Customer>(Customer.class));
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

這里使用的結果集處理器是BeanListHandler,可以根據結果集的不同使用不同的結果集處理器

注意:這里TxQueryRunner類只是重寫了query(String sql, ResultSetHandler<T> rsh, Object... params) query(String sql, ResultSetHandler<T> rsh)這兩個方法,前一個是帶查詢條件的,后一個不帶,帶條件的查詢使用前一個即可。QueryRunner類的其他query方法沒有重寫,所以不要用錯,如要使用記得給它們connection。

在頁面如何訪問

我們在表示層如何訪問這個Servlet呢?我們需要在提交的表單中添加一個隱藏的字段,名為method,其值為需要訪問的Servlet中的具體方法:

<form action="<c:url value='/customerServlet'/>" method="post">
    <!-- 向servlet傳遞一個名為method的參數,其值表示要調用servlet的哪一個方法 -->
    <input type="hidden" name="method" value="add"/>

 

現在整個工作方式類似於Struts,但是我們沒有采用類似Struts.xml一樣的配置文件,沒有很好的辦法來映射action與具體方法的關系,采用這種添加隱藏字段的方式只能是一個折中的方法,沒有配置文件那樣靈活。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM