架构探险——第二章(为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那点事儿
架构探险——第二章(为web应用添加业务功能)的更多相关文章
- 读《架构探险——从零开始写Java Web框架》
内容提要 <架构探险--从零开始写Java Web框架>首先从一个简单的 Web 应用开始,让读者学会如何使用 IDEA.Maven.Git 等开发工具搭建 Java Web 应用:接着通 ...
- 【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 ...
- 2017.12.12 架构探险-第一章-从一个简单的web应用开始
参考来自:<架构探险>黄勇 著 1 使用IDEA搭建MAVEN项目 1.1 搭建java项目 (1)创建java项目 为了整个书籍的项目,我创建了一个工程,在这个工程里创建了每个章节的mo ...
- 架构探险——从零开始写Java Web框架》第二章照作
沉下来慢慢看实现了. 越来越觉得可以和DJANGO作对比. package org.smart4j.chapter2.model; /** * Created by sahara on 2016/3/ ...
- 第二章、Web中使用shiro(实现登陆)
虽然Apache Shiro的核心设计目标允许它用于保护任何基于JVM的应用程序(如命令行应用程序,服务器守护程序,Web应用程序等),但本指南将着重讨论最常见的用例:确保运行的Web应用程序一个Se ...
- 《架构探险——从零开始写Java Web框架》这书不错,能看懂的入门书
这书适合我. 哈哈,结合 以前的知识点,勉强能看懂. 讲得细,还可以参照着弄出来. 希望能坚持 完成啦... 原来,JSTL就类似于DJANGO中的模板. 而servlet类中的res,req,玩了D ...
- 002-读书笔记-企业IT架构转型之道-阿里巴巴中台战略思想与架构实战-第二章 构建业务中台的基础-共享服务体系简介
2.1.回归SOA的本质-服务重用 SOA理念的核心价值:松耦合的服务带来业务的复用,通过服务的编排助力业务的快速响应和创新. 现有模式多是烟囱式结合 ESB 企业总线打通不同系统间的交互. 2.2. ...
- 《深入理解linux内核架构》第二章 进程管理和调度
2.1进程优先级 进程优先级 硬实时进程 软实时进程 抢占式多任务处理 2.2进程生命周期 用户太切换到核心态的办法 系统调用 中断 抢占调度模型优先级普通进程<系统调用<中断 普通进程可 ...
- 网络安全从入门到精通 (第二章-1) Web安全前端基础
本文内容: 前端是什么? 前端代码 HTML CSS JS !!!醋成酒的小墨,促成就的小墨,小墨促成就,!!! 1,前端是什么? 网站一般用两部分组成,前端负责展示,后端负责处理请求. 2,前端代码 ...
随机推荐
- Java之--Java语言基础组成(关键字、标识符、注释、常量和变量、运算符)
Java语言基础组成-关键字.标识符.注释.常量和变量.运算符 Java语言由8个模块构成,分别为:1.关键字:2.标识符(包名.类名.接口名.常量名.变量名等):3.注释:4.常量和变量:5.运算符 ...
- static_cast .xml
pre{ line-height:1; color:#1e1e1e; background-color:#d2d2d2; font-size:16px;}.sysFunc{color:#627cf6; ...
- LeetCode Database: Rank Scores
Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ra ...
- Python:映像、集合
一.字典 字典(dictionary)是Python中唯一的“映射”类型,映射这个概念在高中就学过:一个函数f将键(key, 定义域)映射到值(value, 值域).这样的函数在字典中可以称为哈希(H ...
- MoveTo和MoveBy
cc.MoveTo是“移动到这里",而cc.MoveBy则是“相对于之前点再移动”,通俗一点就是说这里需要两个坐标pos1(x1,y1),pos2(x2,y2). 如果是cc.MoveTo的 ...
- 第三百零八至三百二十天 how can I 坚持
十三天..2月4号至2月16号,好快,假期还没开始就结束了.一一回忆下. 2月4号,腊月二十六,最后一天上班,没多大事,好像是玩了一天,东月回家,貌似路上好折腾,晚上D401,和她聊了一路,也聊了好多 ...
- Django 的 CSRF 保护机制(转)
add by zhj:假设用户登录了网站A,而在网站B中有一个CSRF攻击标签,点击这个标签就会访问网站A,如果前端数据(包括sessionid)都放在本地存储的话, 当在网站B点击CSRF攻击标签时 ...
- Javascript事件处理进阶
这篇文章是我在看乌龟书<编写可维护的Javascript>发现的一篇写的非常好的章节,在这里我并不会教大家什么是绑定事件等比较基础的事.有兴趣了解DOM事件的同学们,可以去w3cschoo ...
- C++11右值引用
[C++11右值引用] 1.什么是左值?什么是右值? 左值是表达式结束后依然存在的对象:右值是表达式结束时就不再存在的对象. 2.std::move的作用是什么? std::move用于把任意类型转化 ...
- 支付结果回调v7核心,投保确认接口..
<?xml version="1.0" encoding="GBK"?> <PACKET type="REQUEST"&g ...