一,目标
1,理解JavaEE的框架层级;
2,实现基本的前端+servlet+JDBC的功能;
3,熟练下各个层级代码的书写规范;
4,重点是在后端;
5,熟练掌握Druid连接池和DBUtils工具类库的使用;
二,框架层级分析
总的来说是不同层之间分工明确,但是不同层需要调用其他层的功能来处理本层的功能,利用组合的方式。使用类与类之间组合的方式使得框架的几层结构连接成一个整体。
- 先写了数据库连接池的JDBCUtils,实现java代码建立一个数据库连接池,并且可以随时从连接池中获取到连接和关闭connection对象,从而可以操作数据库。
- 定义了Dao持久层的规范,DAO:Data Access Object访问数据库信息的类和接口,包括了对数据(一般是JavaBean对象)的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。有时也称作:BaseDAO。作用:为了实现功能的模块化,更有利于代码的维护和升级。
BaseDao抽象类:包括了对数据(一般是JavaBean对象)的CRUD(Create、Retrival、Update、Delete),具有复用性,不对具体业务。一般使用DBUtils工具类库。
UserDao接口:具体的某个JavaBean类的接口,接口内定义的抽象方法都是具体的JavaBean对象需要的业务功能,比如xxx。
UserDaoImpl类:继承BaseDao抽象类,可以调用通用的BaseDao抽象类的方法,实现UserDao接口,实现UserDao接口中业务的方法。
上述属于从数据库到Dao层,实现了Dao层得到的数据写入到数据库中,并能够从数据库中得到指定的数据回传到Dao层进行验证。
- 然后是Service业务层,这一层处理从客户端请求得到的数据,处理业务的一层,这层实现的方法 UserService接口:每个方法都是一个业务处理的功能,比如登陆功能,注册功能, UserServiceImpl类:通过和UserDaoImpl类组合的方式,调用UserDaoImpl类的对象的方法,具体实现UserService接口的每个方法。
4.web显示层,其实包含两层,一层是前端的代码,另一层当前端显示层用户将数据输入到前端页面,并且点击提交之后,页面会先到servlet请求这一层,将数据进行初步的处理,然后调用service层的功能,进行验证,验证之后,利用请求转发将页面跳转到其他页面。
三,功能分析
用户注册和登陆的实现
-
用户注册,需求如下:
- 访问注册页面;
2)填写注册信息,提交给服务器;
3)服务器应该保存用户;
4)当用户已经存在--提示用户注册失败,用户名已存在;
5)当用户不存在,注册成功;
-
用户登陆,需求如下:
1)访问登陆页面;
2)填写用户名密码后提交;
3)服务器判断用户是否存在;
4)如果登陆失败,返回用户名或者密码错误信息;
5)如果登录成功,返回登陆成功信息;
四,代码实现
1.创建数据库表
数据库为book,建一个用户表为t_user;
drop database if exists book;
create database book;
use book;
create table t_user(
`id` int primary key auto_increment,
`username` varchar(20) not null unique,
`password` varchar(20) not null,
`email` varchar(100)
);
insert into t_user(`username`,`password`,`email`) values('admin','admin','ethansung@163.com');
select *
from t_user;
2.pojo类-User类
package com.ethan.pojo;
/**
* @author ethan
* @create 2020-08-09 13:30
*/
public class User {
private int id;
private String username;
private String password;
private String email;
public User() {
}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
3.实现JDBCUtils
该类封装Druid数据库连接池实现数据库数据库连接池以及获取数据库连接,关闭连接的功能,为Dao持久层与数据库交互建立联系;
package com.ethan.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.junit.Test;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author ethan
* @create 2020-08-09 13:35
*/
public class JDBCUtils {
@Test
public void testConnection(){
for (int i = 0; i < 100; i++) {
Connection conn = JDBCUtils.getConnection();
System.out.println(conn);
JDBCUtils.closeConnection(conn);
}
}
private static DruidDataSource dataSource;
static {
try {
/*
方法1:
File f = new File("F:\\FontEndCodeTest\\JavaWebSelf\\Book\\src\\jdbc.properties");
InputStream is =new FileInputStream(f);
*/
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭连接,放回数据库连接池
* @param conn
*/
public static void closeConnection(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4.实现Dao持久层
Dao持久层通过java代码实现与数据库的交互,实现CRUD功能; DAO:Data Access Object访问数据库信息的类和接口,包括了对数据(一般是JavaBean对象)的CRUD(Create、Retrival、Update、Delete),而不包含任何业务相关的信息。 BaseDao抽象类:包括了对数据(一般是JavaBean对象)的CRUD(Create、Retrival、Update、Delete),具有复用性,不对具体业务。一般使用DBUtils工具类库进行封装。
package com.ethan.dao.impl;
import com.ethan.utils.JDBCUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* @author ethan
* @create 2020-08-09 14:30
*/
public abstract class BaseDao {
private QueryRunner runner = new QueryRunner();
/**
* 用于执行insert、update、delete操作
* @param sql
* @param args
* @return 返回-1说明sql语句执行失败
*/
public int update(String sql,Object...args){
Connection conn = JDBCUtils.getConnection();
try {
return runner.update(conn,sql,args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeConnection(conn);
}
return -1;
}
/**
* 查询返回一个bean对象的sql查询方法
* @param type javabean类类型
* @param sql sql语句
* @param args 可变参数
* @param <T> JavaBean返回值类型
* @return 返回查询到的javabean对象
*/
public <T> T queryForOne(Class<T> type,String sql,Object...args){
Connection conn = JDBCUtils.getConnection();
try {
BeanHandler<T> rsh = new BeanHandler<T>(type);
T query = runner.query(conn, sql, rsh, args);
return query;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.closeConnection(conn);
}
return null;
}
/**
* 查询一组javabean对象
* @param type 返回对象的类型
* @param sql 执行的sql语句
* @param args sql的参数
* @param <T> 返回值类型的泛型
* @return
*/
public <T> List<T> queryForList(Class<T> type,String sql,Object...args){
Connection conn = JDBCUtils.getConnection();
try {
List<T> queryList = runner.query(conn, sql, new BeanListHandler<T>(type), args);
return queryList;
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeConnection(conn);
}
return null;
}
public Object queryForSingleValue(String sql,Object...args){
Connection conn = JDBCUtils.getConnection();
try {
Object query = runner.query(conn, sql, new ScalarHandler(), args);
return query;
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.closeConnection(conn);
}
return null;
}
}
UserDao接口:具体的某个JavaBean类的接口,接口内定义的抽象方法都是具体的JavaBean对象需要的业务功能,比如xxx。
package com.ethan.dao;
import com.ethan.pojo.User;
/**
* @author ethan
* @create 2020-08-09 15:27
*/
public interface UserDao {
/**
* 根据用户名查询用户信息
* @param name 用户名
* @return 返回null,该用户没有注册
*/
public User queryUserByUsername(String name);
/**
* 根据用户名和密码查询用户信息
* @param name 用户名
* @param Password 用户密码
* @return 返回null,该用户没有注册
*/
public User queryUserByUsernameandPassword(String name,String Password);
/**
* 保存用户信息
* @param user
* @return 返回-1表示保存失败
*/
public int saveUser(User user);
}
UserDaoImpl类:继承BaseDao抽象类,可以调用通用的BaseDao抽象类的方法,实现UserDao接口,实现UserDao接口中业务的方法。
package com.ethan.dao.impl;
import com.ethan.dao.UserDao;
import com.ethan.pojo.User;
/**
* @author ethan
* @create 2020-08-09 15:34
*/
public class UserDaoImpl extends BaseDao implements UserDao {
@Override
public User queryUserByUsername(String name) {
String sql = "select id,username,email from t_user where username=?";
User user = queryForOne(User.class, sql, name);
return user;
}
@Override
public User queryUserByUsernameandPassword(String name, String password) {
String sql = "select id,password,username,email from t_user where username=? and password=?";
User user = queryForOne(User.class, sql, name,password);
return user;
}
@Override
public int saveUser(User user) {
String sql = "insert into t_user(username,password,email) values(?,?,?)";
int updateCount = update(sql, user.getUsername(), user.getPassword(), user.getEmail());
return updateCount;
}
}
5.实现Service层
Service业务层,这一层处理从客户端请求得到的数据,处理业务的一层,这层实现的方法 UserService接口:每个方法都是一个业务处理的功能,比如登陆功能,注册功能,
package com.ethan.service;
import com.ethan.pojo.User;
/**
* 一个业务,一个功能,一个方法
* @author ethan
* @create 2020-08-09 16:06
*/
public interface UserService {
/**
* 注册用户
* @param user
*/
public void registerUser(User user);
/**
* 用户登录
* @param user
* @return 返回null,登录失败,非空,登录成功
*/
public User login(User user);
/**
* 检查用户名是否可用
* @param username
* @return 返回true代表用户名已存在,返回false表示用户名可用
*/
public boolean existsUsername(String username);
}
UserServiceImpl类:通过和UserDaoImpl类组合的方式,调用UserDaoImpl类的对象的方法,具体实现UserService接口的每个方法。
package com.ethan.service.impl;
import com.ethan.dao.UserDao;
import com.ethan.dao.impl.BaseDao;
import com.ethan.dao.impl.UserDaoImpl;
import com.ethan.pojo.User;
import com.ethan.service.UserService;
/**
* @author ethan
* @create 2020-08-09 16:10
*/
public class UserServiceImpl implements UserService {
/**
* 多用组合,少用继承。
*/
private UserDao userDao = new UserDaoImpl();
@Override
public void registerUser(User user) {
userDao.saveUser(user);
}
@Override
public User login(User user) {
return userDao.queryUserByUsernameandPassword(user.getUsername(),user.getPassword());
}
@Override
public boolean existsUsername(String username) {
User user = userDao.queryUserByUsername(username);
if(user == null){
System.out.println("用户名可用!");
return false;
}else{
System.out.println("用户名已经存在!");
return true;
}
}
}
6.实现Servlet类(Web展示层)
web显示层,其实包含两层,一层是前端的代码,另一层当前端显示层用户将数据输入到前端页面,并且点击提交之后,页面会跳转到servlet请求这一层, 将数据进行初步的处理,然后调用service层的功能,进行验证,验证之后,利用请求转发将页面跳转到其他页面。
注册提交后跳转到RegisterServlet进行注册功能的验证和处理
package com.ethan.web;
import com.ethan.pojo.User;
import com.ethan.service.UserService;
import com.ethan.service.impl.UserServiceImpl;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.PrivateKey;
/**
* @author ethan
* @create 2020-08-10 20:50
*/
public class RegisterServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取到表单请求过来的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String repwd = req.getParameter("repwd");
String email = req.getParameter("email");
String code = req.getParameter("code");
//2.验证 验证码是否正确 ===要求验证码写死,为abcde
if("abcde".equalsIgnoreCase(code)){
//3.若验证码正确,检查用户名是否可用
if(userService.existsUsername(username)){
//用户名不可用,返回注册页面
System.out.println("用户名[ "+username+" ]已存在!");
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/pages/user/regist.html");
requestDispatcher.forward(req,resp);
}else {
//用户名可用,将注册的用户信息调用service层保存到数据库
userService.registerUser(new User(username,password,email));
//将用户注册信息保存到数据库之后,页面跳转到注册成功页面
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
}
}else{
//验证码不正确,返回注册页面
System.out.println("验证码[ "+code+" ]错误!");
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/pages/user/regist.html");
requestDispatcher.forward(req,resp);
}
}
}
登录提交后跳转到RegisterServlet进行注册功能的验证和处理
package com.ethan.web;
import com.ethan.pojo.User;
import com.ethan.service.UserService;
import com.ethan.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author ethan
* @create 2020-08-10 23:21
*/
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取参数
String username = req.getParameter("username");
String password = req.getParameter("password");
User login = userService.login(new User(username, password, null));
if(login == null){
//登录失败,返回登陆界面
req.getRequestDispatcher("/pages/user/login.html").forward(req,resp);
}else {
//登录成功
req.getRequestDispatcher("/pages/user/login_success.html").forward(req,resp);
}
}
}
7.前端代码
1.登录页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>尚硅谷会员登录页面</title>
<!--base标签,用来固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
</head>
<body>
<div id="login_header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
</div>
<div class="login_banner">
<div id="l_content">
<span class="login_word">欢迎登录</span>
</div>
<div id="content">
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>尚硅谷会员</h1>
<a href="pages/user/regist.html">立即注册</a>
</div>
<div class="msg_cont">
<b></b>
<span class="errorMsg">请输入用户名和密码</span>
</div>
<div class="form">
<form action="loginServlet" method="post">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username" />
<br />
<br />
<label>用户密码:</label>
<input cla ss="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" />
<br />
<br />
<input type="submit" value="登录" id="sub_btn" />
</form>
</div>
</div>
</div>
</div>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
2.登录成功页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>尚硅谷会员注册页面</title>
<!--base标签,用来固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
h1 a {
color:red;
}
</style>
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<div>
<span>欢迎<span class="um_span">韩总</span>光临尚硅谷书城</span>
<a href="pages/order/order.html">我的订单</a>
<a href="index.html">注销</a>
<a href="index.html">返回</a>
</div>
</div>
<div id="main">
<h1>欢迎回来 <a href="index.html">转到主页</a></h1>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
3.注册页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>尚硅谷会员注册页面</title>
<!--base标签,用来固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>
<script type="text/javascript">
//页面加载完成之后
$(function () {
//1.给最后的注册按钮绑上监听事件
$("#sub_btn").click(function () {
/*
1. 验证用户名,要求:必须是字母,数字下划线组成,长度为5-12位;
*/
//1.获取输入框的内容
var usernameText = $("#username").val();
//2.创建正则表达式对象
var usernamePatt = /^\w{5,12}$/;
//3.使用test方法进行验证
if(!(usernamePatt.test(usernameText))){
//4.提示用户结果
$("span.errorMsg").text("用户名不合法!");
return false;
}
/*
2. 验证密码,要求:必须是字母,数字下划线组成,长度为5-12位;
*/
//1.获取输入框的内容
var passwordText = $("#password").val();
//2.创建正则表达式对象
var passwordPatt = /^\w{5,12}$/;
//3.使用test方法进行验证
if(!(passwordPatt.test(passwordText))){
//4.提示用户结果
$("span.errorMsg").text("密码不合法!");
return false;
}
/*
3. 验证确认密码,要求:和密码填写一致;
*/
// 1.获取确认密码的内容
var repwdText = $("#repwd").val();
// 2.和密码进行比较
if (repwdText != passwordText){
$("span.errorMsg").text("确认密码和密码不一致!");
return false;
}
// 3.提示用户
/*
4. 验证邮箱,要求:符合邮箱的正确格式;
*/
// 1.获取邮箱内容
var emailText = $("#email").val();
// 2.创建正则表达式
var emailPatt = /^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{1,2}[a-z]+)+$/;
// 3.test验证
if(!(emailPatt.test(emailText))){
// 4.提示客户
$("span.errorMsg").text("邮箱不合法!");
return false;
}
/*
5. 验证码,只验证用户输入了验证码
*/
var codeText = $("#code").val();
//去掉内容的前后空格
alert("去空格之前:["+codeText+"]");
codeText = $.trim(codeText);
alert("去空格之后:["+codeText+"]");
if( codeText == null || codeText == ""){
$("span.errorMsg").text("验证码不能为空!");
return false;
}
//当验证用户名合法之后,去掉不合法字样
$("span.errorMsg").text("");
})
})
</script>
<style type="text/css">
.login_form{
height:420px;
margin-top: 25px;
}
</style>
</head>
<body>
<div id="login_header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
</div>
<div class="login_banner">
<div id="l_content">
<span class="login_word">欢迎注册</span>
</div>
<div id="content">
<div class="login_form">
<div class="login_box">
<div class="tit">
<h1>注册尚硅谷会员</h1>
<span class="errorMsg"></span>
</div>
<div class="form">
<form action="registerServlet" method="post">
<label>用户名称:</label>
<input class="itxt" type="text" placeholder="请输入用户名"
autocomplete="off" tabindex="1" name="username" id="username" />
<br />
<br />
<label>用户密码:</label>
<input class="itxt" type="password" placeholder="请输入密码"
autocomplete="off" tabindex="1" name="password" id="password" />
<br />
<br />
<label>确认密码:</label>
<input class="itxt" type="password" placeholder="确认密码"
autocomplete="off" tabindex="1" name="repwd" id="repwd" />
<br />
<br />
<label>电子邮件:</label>
<input class="itxt" type="text" placeholder="请输入邮箱地址"
autocomplete="off" tabindex="1" name="email" id="email" />
<br />
<br />
<label>验证码:</label>
<img alt="" src="static/img/code.bmp" style="float: right; margin-right: 40px">
<input class="itxt" type="text" name="code" style="width: 150px;" id="code"/>
<br />
<br />
<input type="submit" value="注册" id="sub_btn" />
</form>
</div>
</div>
</div>
</div>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
4.注册成功页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>尚硅谷会员注册页面</title>
<!--base标签,用来固定相对路径跳转的结果-->
<base href="http://localhost:8080/book/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<style type="text/css">
h1 {
text-align: center;
margin-top: 200px;
}
h1 a {
color:red;
}
</style>
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<span class="wel_word"></span>
<div>
<span>欢迎<span class="um_span">韩总</span>光临尚硅谷书城</span>
<a href="order/order.html">我的订单</a>
<a href="index.html">注销</a>
<a href="index.html">返回</a>
</div>
</div>
<div id="main">
<h1>注册成功! <a href="index.html">转到主页</a></h1>
</div>
<div id="bottom">
<span>
尚硅谷书城.Copyright ©2015
</span>
</div>
</body>
</html>
总结
1.新的知识点
- 表单传递数据特别是密码之类的,用dopost请求。 原因:get方式会把请求参数拼接到请求路径上(URL上面会把你的输入信息显示出来),并且有长度限制。post方式把请求参数显示到请求体中(提交的数据封装到请求体中,不会显示出现在URL中),没有长度限制。
2.运用到了请求转发,注意base标签的作用,和相对路径和绝对路径。
- 学习到IDEA的debug调试