粤嵌打卡第42天(实现DBCP数据库连接池工具类和mvc分层开发web流程操作)

今天我们来聊聊数据库连接,mvc分层实现web应用的那一套,方便日后连接数据库时快速复制哦!!!

DBCP数据库连接池操作如下:

1、导入驱动jar包(dbcp连接池jar包通常依赖于pool包一起使用)

原理:dbcp包会产生许多个连接对象,这些对象供pool包进行统一管理。

2、创建数据库工具类:DBUtils.java

工具类优点:

  • 1、提高性能

告别传统方式每次连接数据库都要进行创建并关闭,严重影响性能,使用数据库连接池DBCP技术实现连接池中存放多个连接对象供使用。当不需要连接时,将连接对象存放到池中即可,提高性能!

  • 2、避免多线程访问连接对象混乱问题

将连接保存在ThreadLocal类中,相当于map结构,它是将当前线程对象作为key,保存连接conn对象,避免多线程访问业务层和dao层导致使用的conn连接对象不一致问题。先从threadLocal中获取连接,如果连接为null,从连接池获取连接,设置到threadLocal中。

  • 3、将提交和回滚事务统一处理

先从ThreadLocal类中获取连接,如果连接不为null,提交或者回滚事务,然后关闭连接,清理当前线程所绑定的连接。

  • 4、方便关闭连接

关闭除过conn连接对象的其他对象statement、resultset等。

DBCP核心设置代码如下:

	// 创建数据库连接池
		ds = new BasicDataSource();
		// 设置连接信息
		ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
		ds.setUrl(URL);
		ds.setUsername(USERNAME);
		ds.setPassword(PASSWORD);

		// 设置连接池信息
		// 最大空闲连接数
		ds.setMaxIdle(30);
		// 最小空闲连接数
		ds.setMinIdle(2);
		// 设置初始连接数
		ds.setInitialSize(2);
		// 创建连接时最大等待时间
		ds.setMaxWaitMillis(4000);// 毫秒
		// 从数据源中拿到的连接,关闭其自动提交的事务
		ds.setDefaultAutoCommit(false);

完整代码如下:

package com.yueqian.store.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;

public class DBUtils {
	private static final String URL = "jdbc:mysql://127.0.0.1:3306/store?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false";
	private static final String USERNAME = "root";
	private static final String PASSWORD = "root";
	private static final BasicDataSource ds;
	static {
		// 加载驱动
		try {
			// 方式一
			DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
			// 方式二 根据传入字符串形式的类名加载该类 (加载类时调用静态代码块加载驱动)
			// Class.forName("com.mysql.cj.jdbc.Driver()");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 创建数据库连接池
		ds = new BasicDataSource();
		// 设置连接信息
		ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
		ds.setUrl(URL);
		ds.setUsername(USERNAME);
		ds.setPassword(PASSWORD);

		// 设置连接池信息
		// 最大空闲连接数
		ds.setMaxIdle(30);
		// 最小空闲连接数
		ds.setMinIdle(2);
		// 设置初始连接数
		ds.setInitialSize(2);
		// 创建连接时最大等待时间
		ds.setMaxWaitMillis(4000);// 毫秒
		// 从数据源中拿到的连接,关闭其自动提交的事务
		ds.setDefaultAutoCommit(false);
	}

	// 定义连接保存在ThreadLocal类中,将当前线程对象作为key,保存连接conn对象,避免多线程访问业务层和dao层导致使用的conn连接不一致问题
	// service里绑定每个线程对象的连接,调用的dao层获取该处理线程对象的连接
	private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();

	/**
	 * 连接数据库
	 * 
	 * @return
	 */
	public static Connection getConnection() {
		//先从threadLocal中获得连接,threadLocal类似map存放,ThreadLocal中是以键为当前线程对象,值为conn连接存放的。
		Connection conn = threadLocal.get();
		//如果连接为null,从连接池中获取连接
		if (conn == null) {
			try {
				conn = ds.getConnection();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//将得到的连接设置到threadLocal中
			threadLocal.set(conn);
		}
		return conn;
	}

	/**
	 * 关闭连接
	 * 
	 * @param rs
	 * @param stmt
	 * @param conn
	 */
	public static void close(ResultSet rs, Statement stmt) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (stmt != null) {
					try {
						stmt.close();
					} catch (SQLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	// 提交事务
	public static void commit() {
		Connection conn = threadLocal.get();
		if(conn != null){
			try {
				conn.commit();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				//关闭连接
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//清理当前线程所绑定的连接
				threadLocal.remove();
			}
		}
	}

	// 回滚事务
	public static void rollback() {
		Connection conn = threadLocal.get();
		if(conn != null){
			try {
				conn.rollback();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				//关闭连接
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//清理当前线程所绑定的连接
				threadLocal.remove();
			}
		}
	}
}

3、业务层

使用同一个连接对象,调用多个Dao层处理。所有事务处理完成之后,统一操作成功,调用DBUtils.commit()方法提交事务,一个失败,全部回滚。

package com.yueqian.store.service;

import java.util.List;

import com.yueqian.store.common.DBUtils;
import com.yueqian.store.dao.ProductTypeDao;
import com.yueqian.store.domain.ProductType;

/**
 * 商品类别业务层
 * 
 * @author LinChi
 *
 */
public class ProductTypeService {
	private ProductTypeDao typeDao = new ProductTypeDao();

	/**
	 * 查询所有商品类目
	 * 
	 * @return
	 */
	public List<ProductType> findAllProType() {
		List<ProductType> list = null;
		try {
			//调用多个Dao对象处理,最后统一事务提交
			list = typeDao.findAllProType();
			//使用同一个连接对象,执行后续的DAO方法
			//提交事务
			DBUtils.commit();
		} catch (Exception e) {
			//执行失败回滚事务
			DBUtils.rollback();
		}
		return list;
	}

}

4、dao层

dao层直接调用DBUtils.getConnection()方法获得连接,将dao层所有出现的异常向上抛到业务层,业务层统一处理,最后统一提交并回滚事务,dao层不处理异常,一直声明抛出即可,

package com.yueqian.store.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.yueqian.store.common.DBUtils;
import com.yueqian.store.domain.ProductType;

public class ProductTypeDao {
	/**
	 * 查询所有商品类目表
	 * @return
	 * @throws SQLException  将dao层所有出现的异常向上抛到业务层,业务层统一处理,最后统一提交并回滚事务
	 */
	public List<ProductType> findAllProType()throws SQLException{
		Connection conn = null;
		PreparedStatement pstm = null;
		List<ProductType> list = new ArrayList<ProductType>();
		ResultSet rs = null;
		ProductType proType = null;
		conn = DBUtils.getConnection();
		String sql = "SELECT p.product_type_id,p.name FROM product_types p";
		try {
			pstm = conn.prepareStatement(sql);
			rs = pstm.executeQuery();
			while(rs.next()) {
				proType = new ProductType();
				proType.setTypeId(rs.getInt(1));
				proType.setTypeName(rs.getString(2));
				list.add(proType);
			}
		} finally {
			DBUtils.close(rs, pstm);
		}
		return list;
	}
}

5、controller层

调用业务层,处理相应的逻辑

  • BaseServlet.java

这是个通过反射机制,将前端传入String的参数类型转换为对应的类型。其他servlet集成该类即可方便转换类型。参数如下(前端name名称,请求对象req,要转化类型的字节码Integer.class)

package com.yueqian.store.controller;

import java.sql.Date;
import java.text.SimpleDateFormat;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;

public class BaseServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;
	private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

	// 获取请求的参数,根据请求的参数转化为特定的类型
	public <T> T paseParamter(String parseName, HttpServletRequest req, Class<T> clz) {
		// 获取请求参数
		String parmValue = req.getParameter(parseName);
		// 定义返回类型的对象
		T result = null;
		// clz类型 Integer.class String.class Double.class,Float.class,Date.class
		try {
			if (parmValue != null || parmValue.length() > 0) {
				// 因为Float类型没有String参数的构造方法,所以只能传入long参数的构造方法
				if (clz == Date.class) {
					// 将paramValue转换成long类型的值
					long longValue = sdf.parse(parmValue).getTime();
					// 创建指定类型的String参数构造方法
					result = clz.getDeclaredConstructor(long.class).newInstance(longValue);
				} else {
					// 将paramValue作为指定类型构造方法的参数
					result = clz.getDeclaredConstructor(String.class).newInstance(parmValue);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

}

  • ProductServlet

具体使用如下:

package com.yueqian.store.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.yueqian.store.dao.ProductDAO;
import com.yueqian.store.dao.ProductTypeDao;
import com.yueqian.store.domain.ProductInfo;
import com.yueqian.store.domain.ProductType;
import com.yueqian.store.service.ProductService;
import com.yueqian.store.service.ProductTypeService;

public class ProductServlet extends BaseServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private ProductService productService = new ProductService();
	private ProductTypeService typeService = new ProductTypeService();

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String uri = req.getRequestURI();
		if (uri.indexOf("/prd/list") >= 0) {
			// 商品列表
			List<ProductInfo> findAllProduct = productService.findAllProduct();
			// 设置列表到请求页面
			req.setAttribute("findAllProduct", findAllProduct);
			// 请求转发到目标页面
			req.getRequestDispatcher("/product/product_list.jsp").forward(req, resp);
		} else if (uri.indexOf("/prd/add") >= 0) {
			// 查看所有类目
			List<ProductType> findAllProType = typeService.findAllProType();
			// 存放到请求域中转发到其他页面
			req.setAttribute("findAllProType", findAllProType);
			req.getRequestDispatcher("/product/product_add.jsp").forward(req, resp);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("utf-8");
		String uri = req.getRequestURI();
		if (uri.indexOf("/prd/add") > 0) {
			// 获取请求的参数
			Integer typeId = super.paseParamter("typeId", req, Integer.class);
			String productName = super.paseParamter("productName", req, String.class);
			String desc = super.paseParamter("desc", req, String.class);
			Float price = super.paseParamter("price", req, Float.class);
			System.out.println("productName:"+productName+"-------------------------");
			System.out.println("price:"+price+"-------------------------");
			//输入校验
			//数据库中商品名称不能为null,如果为null,将返回消息和用户填写的其他数据
			if(productName == null || productName.equals("")) {
				req.setAttribute("msg", "商品名称不能为空!");
				req.setAttribute("typeId", typeId);
				req.setAttribute("desc", desc);
				req.setAttribute("price", price);
				req.setAttribute("findAllProType", this.typeService.findAllProType());
				//请求转发到添加页面
				req.getRequestDispatcher("/product/product_add.jsp").forward(req, resp);
				return;
			}
			// 添加新商品
			ProductInfo info = new ProductInfo();
			if (typeId != null && typeId > 0) {
				info.setProductTypeId(typeId);
			}
			info.setProductName(productName);
			info.setDesc(desc);
			info.setPrice(price);
			int count = productService.saveInfo(info);
			System.out.println(info.getProductName()+"========================");
			// 重定向到添加请求url (此处不能使用请求转发,因为表单刷新会发送第二次请求,导致添加两次商品)
			resp.sendRedirect(
					req.getContextPath() + "/prd/add?count=" + count + "&from=add&proId=" + info.getProductId());
		}
	}
}

6、view层

将servlet传入的数据,通过jsp展示到页面给用户

<%@page import="com.yueqian.store.domain.ProductType"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>商品添加页面</title>
<script type="text/javascript">
	window.onload = function() {
		var returnNode = document.getElementById('returnBtn');
		returnNode.onclick = function() {
			location.href = "<%=request.getContextPath()%>/main.jsp";
		}
	}
</script>
</head>

<body>
	<%
		List<ProductType> typeList = (List<ProductType>) request.getAttribute("findAllProType");
		//获取传递的url参数
		String count = request.getParameter("count");
		String from = request.getParameter("from");
		String proId = request.getParameter("proId");
	%>
	<button id="returnBtn" type="button">返回主页</button>
	<form action='<%=request.getContextPath()%>/prd/add' method="post">
		商品类别:<select name="typeId">
			<option value="0">请选择商品类别</option>
			<%
				if (typeList != null && typeList.size() > 0) {
					for (ProductType types : typeList) {
			%>
			<option value='<%=types.getTypeId()%>'<%=request.getAttribute("findAllProType").equals(types.getTypeId())? "selected=selected":"selected=0"%>><%=types.getTypeName()%></option>
			<%
				}
				}
			%>
		</select><br /> 
		商品名称:<input type="text" name="productName" maxlength="30" /><br />
		商品描述:
		<textarea rows="7" cols="30" name="desc"><% Object desc = request.getParameter("desc"); if(desc != null) out.print(desc);%></textarea>
		<br /> 商品价格:<input type="text" name="price" maxlength="10" <% Object price = request.getParameter("price"); if(price != null) out.print(price);%>/><br />
		 <input type="submit" value="添加">
	</form>
	<%
		if (count != null && from != null) {
			int intCount = Integer.parseInt(count);
			String f = ("add".equals(from) ? "录入" : "");
			if (intCount > 0) {
				out.print(f + "了" + count + "件商品,刚刚录入的商品编号为:" + proId);
			}else{
				out.print(f+"失败!");
			}
		}
		Object msg = request.getAttribute("msg");
		if(msg!=null){
			out.print(msg);
		}
	%>
</body>
</html>

好了,该项目的部分代码如上所示,这节你将学到如何优化数据库连接、mvc分层创建web项目流程
、反射机制转换前端传递的类型。


get到的小伙伴双击666,点赞并关注吧,小红心走起来!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值