持续学习&持续更新中…
学习态度:守破离
引入DAO_JDBC封装
编写一个小例子(数据库+Servlet+JSP)
准备工作
1 添加jar包到项目
将mysql的驱动和JSP的taglibsjar包引入项目
2 建表 & 初始化表数据
CREATE TABLE customer(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age INT,
height DOUBLE
);
INSERT INTO customer(name, age, height) VALUES('张三', 10, 1.8);
INSERT INTO customer(name, age, height) VALUES('李四', 20, 1.2);
INSERT INTO customer(name, age, height) VALUES('王五', 30, 1.4);
INSERT INTO customer(name, age, height) VALUES('赵六', 14, 2.3);
INSERT INTO customer(name, age, height) VALUES('田七', 13, 1.8);
INSERT INTO customer(name, age, height) VALUES('王婆', 50, 3.2);
INSERT INTO customer(name, age, height) VALUES('李逵', 40, 0.8);
INSERT INTO customer(name, age, height) VALUES('宋江', 50, 1.9);
编写Java代码
Customer Bean
public class Customer {
private Integer id;
private String name;
private Integer age;
private Double height;
public Customer(Integer id, String name, Integer age, Double height) {
this.id = id;
this.name = name;
this.age = age;
this.height = height;
}
public Customer(String name, Integer age, Double height) {
this.name = name;
this.age = age;
this.height = height;
}
public Customer() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getHeight() {
return height;
}
public void setHeight(Double height) {
this.height = height;
}
}
ListServlet
@WebServlet("/list")
public class ListServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
final String mysqlDriver = "com.mysql.jdbc.Driver";
final String url = "jdbc:mysql://localhost:3306/my_db";
final String username = "root";
final String password = "root";
final String sql = "SELECT id, name, age, height FROM user";
try {
Class.forName(mysqlDriver);
} catch (ClassNotFoundException e) {
System.err.println("MySQL数据库驱动加载失败!");
return;
}
try (
final Connection connection = DriverManager.getConnection(url, username, password);
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
final ResultSet resultSet = preparedStatement.executeQuery()
) {
List<Customer> customers = new ArrayList<>(20);
while (resultSet.next()) {
final Integer id = resultSet.getInt("id");
final String name = resultSet.getString("name");
final Integer age = resultSet.getInt("age");
final Double height = resultSet.getDouble("height");
customers.add(new Customer(id, name, age, height));
}
req.setAttribute("customers", customers);
req.getRequestDispatcher("pages/list.jsp").forward(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
AddCustomerServlet
@WebServlet("/addcustomer")
public class AddCustomerServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String customerName = req.getParameter("customer_name");
int customerAge = 0;
double customerHeight = 0.0;
try {
customerAge = Integer.parseInt(req.getParameter("customer_age"));
customerHeight = Double.parseDouble(req.getParameter("customer_height"));
} catch (Exception e) {
resp.sendRedirect("/seven_crms_jdbc/pages/addcustomer.html");
System.out.println("用户输入错误");
return;
}
final String mysqlDriver = "com.mysql.jdbc.Driver";
final String url = "jdbc:mysql://localhost:3306/my_db";
final String username = "root";
final String password = "root";
final String sql = "INSERT INTO user(name, age, height) VALUES(?, ?, ?)";
try {
Class.forName(mysqlDriver);
} catch (ClassNotFoundException e) {
System.err.println("MySQL数据库驱动加载失败!");
return;
}
try (
final Connection connection = DriverManager.getConnection(url, username, password);
final PreparedStatement preparedStatement = connection.prepareStatement(sql)
) {
preparedStatement.setString(1, customerName);
preparedStatement.setInt(2, customerAge);
preparedStatement.setDouble(3, customerHeight);
if (1 == preparedStatement.executeUpdate()) {
resp.sendRedirect("/seven_crms_jdbc/list");
System.out.println("插入数据成功");
} else {
resp.sendRedirect("/seven_crms_jdbc/pages/addcustomer.html");
System.out.println("插入数据失败 执行sql错误");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>jsp list</title>
<style>
table, tr, th, td {
border: 1px solid black;
text-align: center;
}
th, td {
display: inline-block;
width: 200px;
}
</style>
</head>
<body>
<div>
<a href="pages/addcustomer.html">添加用户</a>
</div>
<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>身高</th>
</tr>
<c:forEach items="${customers}" var="customer">
<tr>
<td>${customer.name}</td>
<td>${customer.age}</td>
<td>${customer.height}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
addcustomer.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>add customer</title>
</head>
<body>
<form action="/seven_crms_jdbc/addcustomer" method="post">
<div>姓名<input type="text" name="customer_name"></div>
<div>年龄<input type="number" name="customer_age"></div>
<div>身高<input type="number" name="customer_height"></div>
<button type="submit">保存</button>
</form>
</body>
</html>
该例子存在的一些问题
-
现在一个请求就对应一个Servlet,Servlet的功能过于单一:ListServlet负责从数据库中获取数据并展示,AddCustomerServlet负责添加数据到数据库。那如果再想要一个删除数据操作呢?再添加一个DeleteServlet?
-
还有一个问题,现在浏览器中输入http://host:port/list,这个list是谁的list?Customer的?Company的?能不能考虑实现一个CustomerServlet,让该Servlet操作对customer表的增删改查?实现一个CompanyServlet,让该Servlet操作对company表的增删改查?
-
要知道,Servlet只是负责解析客户端请求的,负责解析客户端想干嘛的。数据库访问代码不应该写在Servlet中。
-
项目中存在大量重复、繁琐代码。
-
…
扫描二维码关注公众号,回复: 13194499 查看本文章
这些问题该如何解决?
解决该项目问题
① 引入配置文件
什么是配置文件
具体实现
1 创建db.properties文件
李明杰老师讲的是在项目中的src目录下新建该文件。
但是在IDEA2021.1.1中,直接在src中创建db.properties加载不出来,需要在自动生成的resources目录下创建db.properties文件。
2 往properties中写入配置信息
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/my_db
mysql_username=root
mysql_password=root
3 加载配置文件
/*
这些常量不应该写死。
因为到时候需要将项目中的.java文件编译为.class文件部署到服务器上
如果将这些常量写死的话,到时候如果MySQL服务器的用户名或者密码发生改变的话,
只能将该项目修改后再次编译为.class文件,然后再次部署到服务器上,这样会很麻烦。
*/
private static String MYSQL_DRIVER;
private static String MYSQL_URL;
private static String MYSQL_USERNAME;
private static String MYSQL_PASSWORD;
static {
Properties properties = new Properties();
try (InputStream is = Dbs.class.getClassLoader().getResourceAsStream("db.properties")) {
properties.load(is);
MYSQL_DRIVER = properties.getProperty("mysql_driver");
MYSQL_URL = properties.getProperty("mysql_url");
MYSQL_USERNAME = properties.getProperty("mysql_username");
MYSQL_PASSWORD = properties.getProperty("mysql_password");
} catch (Exception e) {
e.printStackTrace();
}
}
② 引入Dbs
什么是Dbs
Dbs就是一个数据库工具类,里面封装了一些常用的操作数据库的方法。
实现Dbs
public final class Dbs {
private static String MYSQL_DRIVER;
private static String MYSQL_URL;
private static String MYSQL_USERNAME;
private static String MYSQL_PASSWORD;
static {
Properties properties = new Properties();
try (InputStream is = Dbs.class.getClassLoader().getResourceAsStream("db.properties")) {
properties.load(is);
MYSQL_DRIVER = properties.getProperty("mysql_driver");
MYSQL_URL = properties.getProperty("mysql_url");
MYSQL_USERNAME = properties.getProperty("mysql_username");
MYSQL_PASSWORD = properties.getProperty("mysql_password");
} catch (Exception e) {
e.printStackTrace();
}
try {
Class.forName(MYSQL_DRIVER);
} catch (ClassNotFoundException e) {
System.err.println("MySQL数据库驱动加载失败!");
}
}
/**
* 执行 DML、DDL语句
*
* @param sql
* @return 是否执行成功
*/
public static int update(final String sql, Object... args) {
try (
final Connection connection = DriverManager.getConnection(MYSQL_URL, MYSQL_USERNAME, MYSQL_PASSWORD);
final PreparedStatement preparedStatement = connection.prepareStatement(sql)
) {
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
return preparedStatement.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public interface RowMapper<T> {
T generateBean(ResultSet resultSet, int row) throws SQLException;
}
/**
* 执行 DQL语句
*
* @param rowMapper 让用户自己创建Bean对象
* @param sql sql语句
* @param args sql语句中的参数
* @param <T> Bean类型
* @return 查询出的结果
*/
public static <T> List<T> query(RowMapper<T> rowMapper, String sql, Object... args) {
if (null == rowMapper) return null;
ResultSet resultSet = null;
try (
final Connection connection = DriverManager.getConnection(MYSQL_URL, MYSQL_USERNAME, MYSQL_PASSWORD);
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
) {
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
resultSet = preparedStatement.executeQuery();
List<T> list = new ArrayList<>();
// 表中的数据 行是从0开始的,列是从1开始的
int row = 0; // 告诉当前正在操纵第几行的数据
while (resultSet.next()) {
list.add(rowMapper.generateBean(resultSet, row++));
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != resultSet) resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
③ 引入DAO
什么是DAO
DAO:Data(base) Acces Object
DAO是专门用来操作数据的。
DAO的命名规范
数据库中一张表就要对应一个JavaBean以及对应一个Dao。
例如数据库下有个customer表,那么就应该有个Customer Bean和CustomerDao与之对应。
实现CustomerDao
public class CustomerDao {
// public List<Customer> customers() {
// final String sql = "SELECT id, name, age, height FROM customer";
//
// return Dbs.query((resultSet, row) -> {
// System.out.println("表中的第" + row + "行记录");
//
// final Integer id = resultSet.getInt("id");
// final String name = resultSet.getString("name");
// final Integer age = resultSet.getInt("age");
// final Double height = resultSet.getDouble("height");
// return new Customer(id, name, age, height);
// }, sql);
// }
public List<Customer> list() {
final String sql = "SELECT id, name, age, height FROM customer";
return Dbs.query(new Dbs.RowMapper<Customer>() {
@Override
public Customer generateBean(ResultSet resultSet) throws Exception {
return new Customer(
resultSet.getInt("id"),
resultSet.getString("name"),
resultSet.getInt("age"),
resultSet.getDouble("height")
);
}
}, sql);
}
public boolean saveCustomer(Customer customer) {
final String sql = "INSERT INTO customer(name, age, height) VALUES(?, ?, ?)";
return Dbs.update(sql, customer.getName(), customer.getAge(), customer.getHeight()) == 1;
}
}
④ 创建CustomerSerlvet
-
将list请求映射到该Servlet的list方法
-
将addcustomer请求映射到该Servlet的addCustomer方法
CustomerServlet的实现:
// @WebServlet("/list*"),此时服务器处于根目录(/ 对应 webapp目录)
// @WebServlet("/customer/list") 此时服务器处于根目录下的customer目录(/customer 对应 webapp/customer目录)
@WebServlet("/customer/*")
public class CustomerServlet extends HttpServlet {
private final CustomerDao dao = new CustomerDao();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
final String requestURI = req.getRequestURI();
final String[] split = requestURI.split("/");
final String methodName = split[split.length - 1];
try {
Method method;
switch (methodName) {
case "list":
case "addcustomer":
method = getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, req, resp);
break;
default:
method = getClass().getMethod("error", String.class, HttpServletRequest.class, HttpServletResponse.class);
method.invoke(this, "找不到你找的页面", req, resp);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// if (requestURI.endsWith("list")) {
// list(req, resp);
// } else if (requestURI.endsWith("addcustomer")) {
// addCustomer(req, resp);
// } else {
// error(req, resp, "请求页面不存在");
// }
// }
// 展示用户数据
public void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
List<Customer> list = dao.customers();
req.setAttribute("customers", list);
req.setAttribute("customerSize", list.size());
req.getRequestDispatcher("../pages/list.jsp").forward(req, resp);
}
// 添加用户数据到数据库
public void addcustomer(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
final String customerName = req.getParameter("customer_name");
final String customerAge = req.getParameter("customer_age");
final String customerHeight = req.getParameter("customer_height");
if (
null != customerName && !"".equals(customerName) &&
null != customerAge && !"".equals(customerAge) &&
null != customerHeight && !"".equals(customerHeight)
) {
dao.saveCustomer(new Customer(
customerName, Integer.valueOf(customerAge), Double.valueOf(customerHeight)
));
resp.sendRedirect("/seven_crms_jdbc/customer/list");
} else {
error("用户信息填写错误", req, resp);
}
}
// 跳转至出错页面
public void error(String error, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("error", error);
req.getRequestDispatcher("../pages/error.jsp").forward(req, resp);
}
}
调整项目之后的项目结构图
一些细节和建议
-
从数据库中查询数据时,最好不要写
SELECT * FROM TABLE_XX
,而是换成查询具体的字段SELECT name, age, height FROM TABLE_XX
。 -
表中的数据,行一般是从0开始的,列一般是从1开始的,自动增长的id也是从1开始的。
-
list.jsp中的a标签可以写为
<a href="/seven_again/pages/save.html">添加客户</a>
(/context/…/…) -
Maven项目下的资源文件(例如db.properties)应该放在resource目录下
参考
李明杰: Java从0到架构师②JavaEE技术基石.
本文完,感谢您的关注支持!