zoukankan      html  css  js  c++  java
  • 架构探险——第二章(为web应用添加业务功能)

    目录

    重点:

    第二章不使用框架完成了自己的Web应用。

    重点:

    服务层的完善优化过程,思路

    在看这一段的时候引起了无数次的共鸣。相信大家在开始接触Java Web的时候,都做过类似的封装和优化。

    第一版
    在Service的静态代码块中获取config.properties配置文件中与JDBC相关的配置项。在service的业务方法中获取数据库的连接,并进行数据库的操作,finally中关闭数据库。

    /**
     * 提供客户数据服务
     */
    public class CustomerServiceVersion {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);
    
        private static final String DRIVER;
        private static final String URL;
        private static final String USERNAME;
        private static final String PASSWORD;
        private static final String URL;
    
        static {
            Properties conf = PropsUtil.loadProps("config.properties");
            DRIVER = conf.getProperty("jdbc.driver");
            URL = conf.getProperty("jdbc.url");
            USERNAME = conf.getProperty("jdbc.username");
            PASSWORD = conf.getProperty("jdbc.password");
    
            try {
                Class.forName(DRIVER);
            }catch(ClassNotFoundException e){
                LOGGER.error("can not load jdbc driver",e);
            }
        }
    
        /**
         * 获取客户列表
         * @return
         */
        public List<Customer> getCustomerList(){
            Connection conn = null;
            try {
                List<Customer> customerList = new ArrayList<Customer>();
                String sql = "select * from customer";
                conn = DriverManager.getConnection(URL,USERNAME,PASSWORD);
                PreparedStatement stmt = conn.prepareStatement(sql);
                ResultSet rs = stmt.executeQuery();
                while (rs.next()){
                    Customer customer = new Customer();
                    customer.setId(rs.getLong("id"));
                    customer.setName(rs.getString("name"));
                    customer.setContact(rs.getString("contact"));
                    customer.setTelephone(rs.getString("telephone"));
                    customer.setEmail(rs.getString("email"));
                    customer.setRemark(rs.getString("remark"));
                    customerList.add(customer);
                }
                return  customerList;
            }catch (SQLException e){
                LOGGER.error("excute sql failure",e);
            }finally {
                if(conn != null){
                    try{
                        conn.close();
                    }catch (SQLException e){
                        LOGGER.error("close connection failure",e);
                    }
                }
            }
        }
    }

    存在的问题
    1.在service中读取config.properties文件,不合理,其他service使用的时候反复进行io。
    2.执行一条SQL语句需要编写一大堆代码,并且使用try...catch...finally结构,开发效率不高)

    第二版
    开发DatabaseHelper来读取配置文件,获取数据库连接,关闭数据库连接。(解决了第一个问题)
    使用Apache Common项目中的DbUtils类库,在DatabaseHelper中写SQL公共类,通过传参的形式,利用反射,执行对应的SQL。(解决了第二个问题)

    /**
     * 数据库助手类
     */
    public final class DatabaseHelper1 {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelperV.class);
    
        private static final QueryRunner QUERY_RUNNER = new QueryRunner();
    
        private static final String DRIVER;
        private static final String URL;
        private static final String USERNAME;
        private static final String PASSWORD;
        private static final String URL;
    
        static {
            Properties conf = PropsUtil.loadProps("config.properties");
            DRIVER = conf.getProperty("jdbc.driver");
            URL = conf.getProperty("jdbc.url");
            USERNAME = conf.getProperty("jdbc.username");
            PASSWORD = conf.getProperty("jdbc.password");
    
            try {
                Class.forName(DRIVER);
            }catch(ClassNotFoundException e){
                LOGGER.error("can not load jdbc driver",e);
            }
        }
    
        /**
         * 获取数据库连接
         */
        public static Connection getConnection() {
            Connection conn = null;
            try {
                conn = DriverManager.getConnection(URl,USERNAME,PASSWORD);
            } catch (SQLException e) {
                LOGGER.error("get connection failure", e);
            }
            return conn;
        }
    
        /**
         * 关闭数据库连接
         */
        public static void closeConnection(Connection conn){
            if(conn != null){
                try{
                    conn.close();
                }catch (SQLException e){
                    LOGGER.error("close connection failure",e);
                }
            }
        }
    
        /**
         * 查询实体列表
         */
        public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
            List<T> entityList;
            try {
                Connection conn = getConnection();
                entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
            } catch (SQLException e) {
                LOGGER.error("query entity list failure", e);
                throw new RuntimeException(e);
            }
            return entityList;
        }
    }

    存在的问题
    1.由于Connection是static变量,(静态对象可以节省频繁访问引起的频繁分配内存、释放内存、占用内存的性能开销)但是这样是线程不安全的。

    第三版
    使用ThreadLocal来存放本地线程变量。在每次获取Connection的时候,在ThreadLocal中查找,不存在,则新创建一个连接。

    /**
     * 数据库助手类
     */
    public final class DatabaseHelper2 {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelperV.class);
    
        private static final ThreadLocal<Connection> CONNECTION_HOLDER;
        
        private static final QueryRunner QUERY_RUNNER = new QueryRunner();
    
        private static final String DRIVER;
        private static final String URL;
        private static final String USERNAME;
        private static final String PASSWORD;
        private static final String URL;
    
        static {
            CONNECTION_HOLDER = new ThreadLocal<Connection>();
            
            Properties conf = PropsUtil.loadProps("config.properties");
            DRIVER = conf.getProperty("jdbc.driver");
            URL = conf.getProperty("jdbc.url");
            USERNAME = conf.getProperty("jdbc.username");
            PASSWORD = conf.getProperty("jdbc.password");
    
            try {
                Class.forName(DRIVER);
            }catch(ClassNotFoundException e){
                LOGGER.error("can not load jdbc driver",e);
            }
        }
    
        /**
         * 获取数据库连接
         */
        public static Connection getConnection() {
            Connection conn = CONNECTION_HOLDER.get();
            if (conn == null) {
                try {
                    conn = DATA_SOURCE.getConnection();
                } catch (SQLException e) {
                    LOGGER.error("get connection failure", e);
                    throw new RuntimeException(e);
                } finally {
                    CONNECTION_HOLDER.set(conn);
                }
            }
            return conn;
        }
    
        /**
         * 关闭数据库连接
         */
        public static void closeConnection(){
            Connection conn = CONNECTION_HOLDER.get();
            if(conn != null){
                try {
                    conn.close();
                }catch (SQLException e){
                    LOGGER.error("cose connection failure",e);
                    throw new RuntimeException(e);
                }finally {
                    CONNECTION_HOLDER.remove();
                }
            }
        }
    
        /**
         * 查询实体列表
         */
        public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
            List<T> entityList;
            try {
                Connection conn = getConnection();
                entityList = QUERY_RUNNER.query(conn, sql, new BeanListHandler<T>(entityClass), params);
            } catch (SQLException e) {
                LOGGER.error("query entity list failure", e);
                throw new RuntimeException(e);
            }
            return entityList;
        }
    }

    存在的问题:每次都需要创建一个Connection,然后进行数据库操作,然后关闭。

    第四版
    引入了DBCP数据库连接池

    /**
     * 数据库助手类
     */
    public final class DatabaseHelper {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);
    
        private static final ThreadLocal<Connection> CONNECTION_HOLDER;
    
        private static final QueryRunner QUERY_RUNNER;
    
        private static final BasicDataSource DATA_SOURCE;
    
        static {
            CONNECTION_HOLDER = new ThreadLocal<Connection>();
    
            QUERY_RUNNER = new QueryRunner();
    
            Properties conf = PropsUtil.loadProps("config.properties");
            String driver = conf.getProperty("jdbc.driver");
            String url = conf.getProperty("jdbc.url");
            String username = conf.getProperty("jdbc.username");
            String password = conf.getProperty("jdbc.password");
    
            DATA_SOURCE = new BasicDataSource();
            DATA_SOURCE.setDriverClassName(driver);
            DATA_SOURCE.setUrl(url);
            DATA_SOURCE.setUsername(username);
            DATA_SOURCE.setPassword(password);
        }
    
        /**
         * 获取数据库连接
         */
        public static Connection getConnection() {
            Connection conn = CONNECTION_HOLDER.get();
            if (conn == null) {
                try {
                    conn = DATA_SOURCE.getConnection();
                } catch (SQLException e) {
                    LOGGER.error("get connection failure", e);
                    throw new RuntimeException(e);
                } finally {
                    CONNECTION_HOLDER.set(conn);
                }
            }
            return conn;
        }
    
        /**
         * 执行查询语句
         */
        public List<Map<String, Object>> executeQuery(String sql, Object... params) {
            List<Map<String, Object>> result;
            try {
                Connection conn = getConnection();
                result = QUERY_RUNNER.query(conn, sql, new MapListHandler(), params);
            } catch (Exception e) {
                LOGGER.error("execute query failure", e);
                throw new RuntimeException(e);
            }
            return result;
        }
    }

    在这个过程中,黄老师已经教我们开发了一个轻量级的JDBC框架。
    最终代码

    干货分享:

    读取配置文件——Java读取Properties配置文件
    ThreadLocal的用法——传送门-ThreadLocal那点事儿

  • 相关阅读:
    将博客搬至CSDN
    smarty不渲染html页面
    开篇
    html的入门——从标签开始(1)
    java内部编码
    用base64Encoder进行编码 和base64Decoder解码
    序列化和反序列化
    HttpServletResponse类
    配置Tomcat服务器数据连接池
    SVN修改地址
  • 原文地址:https://www.cnblogs.com/xieyulin/p/7070151.html
Copyright © 2011-2022 走看看