zoukankan      html  css  js  c++  java
  • 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与具体方法的关系,采用这种添加隐藏字段的方式只能是一个折中的方法,没有配置文件那样灵活。

  • 相关阅读:
    jquery跨域3
    juery的跨域请求2
    jquery的跨域请求
    synchronized与Lock的区别
    springboot之启动原理解析及源码阅读
    java中Number类理解
    springboot中配置文件application.properties的理解
    restTemplate设置访问超时
    BigDecimal.setScale 处理java小数点
    NIO之FileChannel类的理解和使用
  • 原文地址:https://www.cnblogs.com/lz2017/p/7055591.html
Copyright © 2011-2022 走看看