您的位置:首页 > 运维架构 > 网站架构

架构探险——第二章(为web应用添加业务功能)

2015-09-08 16:33 465 查看
第二章不使用框架完成了自己的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那点事儿
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: