第二章不使用框架完成了自己的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那点事儿

架构探险——第二章(为web应用添加业务功能)的更多相关文章

  1. 读《架构探险——从零开始写Java Web框架》

    内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...

  2. 【EatBook】-NO.3.EatBook.3.JavaArchitecture.2.001-《架构探险:从零开始写Java Web框架》-

    1.0.0 Summary Tittle:[EatBook]-NO.3.EatBook.3.JavaArchitecture.2.001-<架构探险:从零开始写Java Web框架>- S ...

  3. 2017.12.12 架构探险-第一章-从一个简单的web应用开始

    参考来自:<架构探险>黄勇 著 1 使用IDEA搭建MAVEN项目 1.1 搭建java项目 (1)创建java项目 为了整个书籍的项目,我创建了一个工程,在这个工程里创建了每个章节的mo ...

  4. 架构探险——从零开始写Java Web框架》第二章照作

    沉下来慢慢看实现了. 越来越觉得可以和DJANGO作对比. package org.smart4j.chapter2.model; /** * Created by sahara on 2016/3/ ...

  5. 第二章、Web中使用shiro(实现登陆)

    虽然Apache Shiro的核心设计目标允许它用于保护任何基于JVM的应用程序(如命令行应用程序,服务器守护程序,Web应用程序等),但本指南将着重讨论最常见的用例:确保运行的Web应用程序一个Se ...

  6. 《架构探险——从零开始写Java Web框架》这书不错,能看懂的入门书

    这书适合我. 哈哈,结合 以前的知识点,勉强能看懂. 讲得细,还可以参照着弄出来. 希望能坚持 完成啦... 原来,JSTL就类似于DJANGO中的模板. 而servlet类中的res,req,玩了D ...

  7. 002-读书笔记-企业IT架构转型之道-阿里巴巴中台战略思想与架构实战-第二章 构建业务中台的基础-共享服务体系简介

    2.1.回归SOA的本质-服务重用 SOA理念的核心价值:松耦合的服务带来业务的复用,通过服务的编排助力业务的快速响应和创新. 现有模式多是烟囱式结合 ESB 企业总线打通不同系统间的交互. 2.2. ...

  8. 《深入理解linux内核架构》第二章 进程管理和调度

    2.1进程优先级 进程优先级 硬实时进程 软实时进程 抢占式多任务处理 2.2进程生命周期 用户太切换到核心态的办法 系统调用 中断 抢占调度模型优先级普通进程<系统调用<中断 普通进程可 ...

  9. 网络安全从入门到精通 (第二章-1) Web安全前端基础

    本文内容: 前端是什么? 前端代码 HTML CSS JS !!!醋成酒的小墨,促成就的小墨,小墨促成就,!!! 1,前端是什么? 网站一般用两部分组成,前端负责展示,后端负责处理请求. 2,前端代码 ...

随机推荐

  1. java解析properties文件

    在自动化测试过程中,经常会有一些公用的属性要配置,以便后面给脚本使用,我们可以选择xml, excel或者json格式来存贮这些数据,但其实java本身就提供了properties类来处理proper ...

  2. JavaScript专业规则12条

    学习JavaScript是困难的.它发展的如此之快,以至于在任何一个特定的时刻,你都不清楚自己是否“做错了”.有些时候,感觉像是坏的部分超过了好的部分.然而,讨论这些并没有意义,JavaScript正 ...

  3. 记录一次cefsharp1输入法在win7下异常解决定位

    最近几天都被基于cefSharp封装的浏览器控件搞疯了!对于cefSharp基本满足当前所做项目的需求,但是有一个问题一直困扰我,那就是系统中偶尔会出现输入法不能转换到中文.而且这个问题似乎没有什么规 ...

  4. 使用clipboard.js复制页面内容到剪切板

    最近在做一个的智能客服Web端浏览器应用,其中有一项需求是客户在获取系统返回的 答案后点击“复制答案”按钮将答案复制到系统剪切板.本以为这是一个小case,但是发现如果 要对各种主流浏览器都有良好的兼 ...

  5. <转>如何利用多核CPU来加速你的Linux命令 — awk, sed, bzip2, grep, wc等

    原文链接:http://www.vaikan.com/use-multiple-cpu-cores-with-your-linux-commands/ 你是否曾经有过要计算一个非常大的数据(几百GB) ...

  6. 【LeetCode】101 - Symmetric Tree

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  7. Xcode集成Google Test

    Xcode集成Google Test 1.下载源代码https://github.com/google/googletest 2.进入下载文件目录下googletest/make中运行make gte ...

  8. python添加windows域验证

    1.安装python-ldap https://pypi.python.org/pypi/python-ldap/ 在 Ubuntu/Debian 下安装 python-ldap 模块: $ sudo ...

  9. cocos2d-html5将js编译为jsc

    在d:\DevTool\cocos2d-x-2.2.2\cocos2d-x-2.2.2\tools\cocos2d-console\console 有 cocos2d_jscompile.py coc ...

  10. 了解shell

    1. shell 脚本文件第一行:    #!/bin/sh 或 #!/bin/bash "#!"  又称为纪数,在执行bash脚本的时候,内核会根据它来确定该用哪个程序来解释脚本 ...