这是,尚硅谷书城项目1.0,距离2月26日开学日只剩16天,😭😭😭,跺jiojio了
前言 / 目标
之前我们,简单学习过了xml、tomcat、servlet…在这个项目,我们要结合所学内容,实现网站功能。我们需要实现尚硅谷书城网站的,登入和注册功能。废话不多说,我们广块开始🐛🐛🐛🐛
主页面
注册页面
登入页面
1、javaEE三层架构介绍
分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。
下面是文件名规范1下
web层
com.flzj.web/servlet/controller
service层
Service接口包 com.flzj.service
Service接口实现类 com.flzj.service.impl
dao持久层
Dao接口包 com.flzj.dao
Dao接口实现类 com.flzj.dao.impl
实体bean对象
com.flzj.pojo/entity/domain/bean JavaBean类
测试包
com.flzj.test/junit
工具类
com.flzj.utils
然后我们创建好后,大概是这个样子的,赶快继续开始我们的项目嗷
2、创建数据库
因为,我们要实现用户的注册和登入功能,这里我们创建一张用户表
DROP TABLE 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(32) NOT NULL,
`email` VARCHAR(200)
);
INSERT INTO t_user(`username`,`password`,`email`)
VALUES('admin','admin','admin@flzj.com');
SELECT * FROM t_user;
3、编写数据库表对应的javaBean对象
public class User {
private Integer id;
private String username;
private String password;
private String email;
public User(){
}
public User(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
}
4、编写工具类
编写工具类,方便对数据库的操作
导个包🤤🤤🤤🤤
改改jdbc.properties的配置
public class JdbcUtils {
private static DruidDataSource dataSource;
static{
try {
Properties properties = new Properties();
//读取jdbc.properties属性配置文件
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//从流中加载数据
properties.load(inputStream);
// 创建 数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
System.out.println(dataSource.getConnection());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
}
/* 获取数据库连接池的连接
如果返回null就是连接失败
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//关闭连接
public static void close(Connection conn){
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
💣刚刚,👴跟着视频,差点跟进坑里,因为我的idea没有src,所以properties文件要放到resource下,才能被读到
然后我们赶快去com.flzj.test文件下创建一个类去测试看看
public class JdbcUtilsTest {
@Test
public void testJdbcUtils(){
System.out.println(JdbcUtils.getConnection());
}
}
效果图
5、Dao持久层
5.1、编写BaseDao
public abstract class BaseDao {
//使用DbUtils操作数据
private QueryRunner queryRunner = new QueryRunner();
/***
* update() 方法来执行,Insert\Update\Delete语句
* @return -1 表示失败,成功返回影响的行数
*/
public int update(String sql,Object ...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.update(connection,sql, args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return -1;
}
/***
* 查询返回一条或者一个javabean的sql语句
* @param type 返回的对象类型
* @param sql 执行sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T>T queryForOne(Class<T> type,String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}
/***
* 查询返回多个javabean的sql语句
* @param type 返回的对象类型
* @param sql 执行sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T> List<T> queryForList(Class<T> type,String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanListHandler<T>(type),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}
/***
* 执行返回一行一列的sql语句
* @param sql 执行sql语句
* @param args sql对应的参数值
* @return
*/
public Object queryForSingleValue(String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new ScalarHandler(),args);
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}
}
5.2、编写UserDao
public interface UserDao {
/***
* 根据用户查询用户信息
* @param username 用户名
* @param password 密码
* @return 如果返回null,说明用户名或密码错误
*/
public User queryUserByUsername(String username,String password);
/***
* 根据用户查询用户信息
* @param username 用户名
* @return 如果返回null,说明没有这个用户
*/
public User queryUserByUsernameAndPassword(String username);
/***
* 保存用户信息
* @param user
* @return
*/
public int SaveUser(User user);
}
然后再写个类来实现它
public class UserDaoImpl extends BaseDao implements UserDao {
@Override
public User queryUserByUsername(String username) {
String sql = "select `id`,`username`,`password`,`email` from t_user where username = ?";
return queryForOne(User.class,sql,username);
}
@Override
public User queryUserByUsernameAndPassword(String username, String password) {
String sql = "select `id`,`username`,`password`,`email` from t_user where username = ? and password = ?";
return queryForOne(User.class,sql,username,password);
}
@Override
public int SaveUser(User user) {
String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?)";
return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
}
}
那么我们要怎么自动生成3个方法帮我们快速测试下捏?
所以说,我们跟着视频写的那个test真的皮用都没有了😭😭😭😭,idea版本高,自动就生成了test
public class UserDaoTest {
//方便测试才放外面的
UserDao userDao = new UserDaoImp();
@Test
public void queryUserByUsername() {
if(userDao.queryUserByUsername("admin123") == null) {
System.out.println("用户名可用");
}else{
System.out.println("用户名已存在");
}
}
@Test
public void queryUserByUsernameAndPassword() {
if(userDao.queryUserByUsernameAndPassword("admin","admin") == null){
System.out.println("用户名或密码错误");
}else{
System.out.println("登入成功");
}
}
@Test
public void saveUser() {
System.out.println(userDao.saveUser(new User(null,"ft","123456","246fzlj@qq.com")));
}
}
下图为测试第一个方法的效果图
6、Service业务层
6.1、编写UserService
登入、注册、检查用户名是否存在
public interface UserService {
/**
* 注册用户
* @param user
*/
public void registUser(User user);
/**
* 登入
* @param user
* @return 如果登入失败返回null
*/
public User login(User user);
/**
* 判断用户账号是否存在
* @param username
* @return 返回true说明存在
*/
public boolean existsUsername(String username);
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void registUser(User user) {
userDao.saveUser(user);
}
@Override
public User login(User user) {
return userDao.queryUserByUsernameAndPassword(user.getUsername(),user.getPassword());
}
@Override
public boolean existsUsername(String username) {
if(userDao.queryUserByUsername(username) == null){
return false;
}
return true;
}
}
6.2、测试
registUser()方法 (负责注册)
@Test
public void registUser() {
userService.registUser(new User(null,"aaa","123456","aaa@qq.com"));
userService.registUser(new User(null,"bbb","123456","bbb@qq.com"));
}
login()方法 (负责登入)
@Test
public void login() {
if(userService.login(new User(null,"aaa","123456",null))== null){
System.out.println("登入失败");
}else{
System.out.println("登入成功");
}
}
existsUsername() 方法 (用来判断用户注册的用户名是否存在)
@Test
public void existsUsername() {
if(userService.existsUsername("admin")){
System.out.println("用户名已存在");
}else {
System.out.println("用户名可用");
}
}
7、实现功能
7.1、实现用户注册功能
图解
代码实现
首先我们要写个RegistServlet程序,和去web.xml里配置
public class RegistServlet 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 email = req.getParameter("email");
String code = req.getParameter("code");
// 2、检查验证码是否正确 这里我们先写死abcde
if("abcde".equals(code)) {
// 正确
// 3、检查用户名是否可用
if(!userService.existsUsername(username)) {
// 可用
// 调用Sservice保存到数据库
userService.registUser(new User(null,username,password,email));
// 跳到注册成功末面regist_success.html
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}else {
// 不可用
System.out.println("账号已存在");
// 跳回注册页面
req.getRequestDispatcher("/pages/user/regist_success.html").forward(req,resp);
}
}else{
// 不正确
System.out.println("验证密码[ "+code+" ]错误");
// 跳回注册页面
req.getRequestDispatcher("/pages/user/regist.html").forward(req,resp);
}
}
}
<servlet>
<servlet-name>RegistServlet</servlet-name>
<servlet-class>com.flzj.web.RegistServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegistServlet</servlet-name>
<url-pattern>/registServlet</url-pattern>
</servlet-mapping>
然后我们溜到,regist.html,
改表单提交路径到registServlet
<form action="registServlet" 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>
<input class="itxt" type="text" style="width: 150px;" id="code" name="code"/>
<img alt="" src="../../static/img/code.bmp" style="float: right; margin-right: 40px">
<br />
<br />
<input type="submit" value="注册" id="sub_btn" />
</form>
这里我们先用base标签,改路径。这么一改,我们的regist.html里面的css,jquery,什么的路径都要改动
<!--写base标签,永远固定相对路径跳转的结果
记住工程名(book)后面要加/ 下面这图base标签,book的后面没加/害我搞了半天
-->
<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>
为什么捏?😄,因为,写上base标签后,这里http://localhost:8080/book 的 …/…/ 的是 http://localhost:8080,所以这里被解读的相对路径为:http://localhost:8080/static/css/style.css,这根本不存在,所以我们只要把…/…/去掉即刻。
其他地方同理,什么之前写的,图片、视频、的相对路径 都要改改,其实实际上,也就是去去…/…/ 而已
下图为注册成功
7.2、用户登入
图解
代码实现
1、同样的,先去写一个base标签,修改css或者图片的相对路径,改改form表单的method 和 action
2、我们写个LoginServlet程序
public class LoginServlet extends HttpServlet {
private UserService userService = new UserServiceImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("接收到用户的账为"+username+"\t"+password);
// 2、调用XxxService. xxx()处理业务
// userService. login(登录
User user = userService.login(new User(null, username, password, null));
// 3、根据login()方法返回结果判断登录是否成功
if(!(user == null)) {
// 成功
// 跳到成功页面login_ success. html
System.out.println(username + " 登入成功");
request.getRequestDispatcher("/pages/user/login_success.html").forward(request,response);
}else {
// 失败
// 跳回登录页面
System.out.println(username + " 登入失败");
request.getRequestDispatcher("/pages/user/login.html").forward(request,response);
}
}
}
7.3、小问题
刚刚出现了,浏览器缓存问题,就是,我这里html文件怎么修改,web工程里的相同html就是不会变,这个时候我们只要清楚缓存就行了,谷歌浏览器快捷键ctrl + shift + delet
还有一个,就是之前,servlet没学好的同学肯定会问了,嗨呀,这个表单的题交action为什么是action=“loginServlet”(servlet名),而不是action="/loginServlet"
<form action="loginServlet" method="post">
<label>用户名称:</label>...
因为这样会被解释成 http://ip:port/你写的,下图为action = “/loginServlet”
8、IDE的Debug功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bytAw2v5-1644416046724)(D:\java\javaweb\picture\sanguigubookstoreitem\IDE调试1.png)]
我们去注册页面,正确填写后,点击注册,启动了RegistServlet程序,卡在我们设置的断点位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6C8bGAMX-1644416046724)(D:\java\javaweb\picture\sanguigubookstoreitem\注册页面点击后被卡.png)]
8.1、测试工具栏
8.2、变量窗口
它可以查看当前方法里的,所有有效变量(这里是doPost()方法里的所有有效变量)
8.3、方法调用栈窗口
1、方法调用栈可以查看当前线程有哪些方法调用信息
2、下面的调用上一行的方法,比如这里的doPost()方法调用了 getParameter()方法