整个项目源代码可以到我的github上下载。之前已经做好前端页面,现在要通过 servlet 程序以及 JDBC 具体实现用户注册和登录,
JavaEE项目的三层架构 为什么要分层呢?通过一层完成所有事情不行吗?
分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。 我们知道有些项目代码量是巨大的,如果放在一层后期维护和升级会很麻烦,如果分出不同的层,每层都有不同负责的人员,那么维护和升级会变得轻松很多。需求分析 需求一:用户注册 1)访问注册页面 2)填写注册信息,提交给服务器 3)服务器应该保存用户 4)当用户已经存在—-提示用户注册 失败,用户名已存在 5)当用户不存在—–注册成功 需求二:用户登录 1)访问登陆页面 2)填写用户名密码后提交 3)服务器判断用户是否存在 4)如果登陆失败 —>>>> 返回用户名或者密码错误信息 5)如果登录成功 —>>>> 返回登陆成功 信息
需要的接口和类
1 2 3 4 5 6 7 8 web 层 com.atguigu.web/servlet/controller service 层 com.atguigu.service Service 接口包 com.atguigu.service.impl Service 接口实现类 dao 持久层 com.atguigu.dao Dao 接口包 com.atguigu.dao.impl Dao 接口实现类 实体 bean 对象 com.atguigu.pojo/entity/domain/bean JavaBean 类 测试包 com.atguigu.test/junit 工具类 com.atguigu.utils
完成类编写后的目录结构如下
创建数据库和表 这里我使用的是 MySql
+ Navicat
,新建一个book
数据库,并新建一个t_user
表。 通过建立Unique
类型索引,可以使该字段唯一。 插入一条数据
当然也可以直接使用如下 Sql
语句创建
1 2 3 4 5 6 7 8 9 10 11 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 (32 ) not null , email varchar (200 ) ); insert into t_user(username,password,email) values ('admin' ,'admin' ,'admin@atguigu.com' );select * from t_user;
编写数据库对应的JavaBean对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 package com.atguigu.pojo;public class User { private Integer id; private String username; private String password; private String 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; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + 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; } }
编写工具类JdbcUtils JdbcUtils
工具类主要用来建立数据库连接 与释放数据库连接
导入jar包 数据库和连接池需要如下jar
包
1 2 druid-1.1 .9 .jar mysql-connector-java-5.1 .7 -bin.jar
以下是测试需要:
1 2 hamcrest-core-1.3 .jar junit-4.12 .jar
编写jdbc.properties配置文件 放在src
文件夹下 内容根据自己情况修改username
改为你的用户名password
改为你的密码initialSize
为初始连接池大小maxActive
为最大可用连接数
1 2 3 4 5 6 username=root password=123456 url=jdbc:mysql: driverClassName=com.mysql.jdbc.Driver initialSize=5 maxActive=10
编写JdbcUtils工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.atguigu.utils;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.InputStream;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;public class JdbcUtils { private static DruidDataSource dataSource; static { try { Properties properties = new Properties(); InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties" ); properties.load(inputStream); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () { Connection conn = null ; try { conn = dataSource.getConnection(); } catch (Exception e) { e.printStackTrace(); } return conn; } public static void close (Connection conn) { if (conn != null ) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } } }
JdbcUtils测试 我们在test
包下创建JdbcUtilsTest
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.atguigu.test;import com.atguigu.utils.JdbcUtils;import org.junit.Test;import java.sql.Connection;public class JdbcUtilsTest { @Test public void testJdbcUtils () { for (int i=0 ; i<100 ; ++i) { Connection con = JdbcUtils.getConnection(); System.out.println(con); JdbcUtils.close(con); } } }
编写BaseDao BaseDao
类用来封装数据库的更新,查询操作(包括查询一行,查询多行,查询一个值)
导入 DBUtils 的jar包
编写 BaseDao 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 package com.atguigu.dao.impl;import com.atguigu.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;public abstract class BaseDao { private QueryRunner queryRunner = new QueryRunner(); public int update (String sql, Object ... args) { Connection connection = JdbcUtils.getConnection(); try { return queryRunner.update(connection, sql, args); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.close(connection); } return -1 ; } public <T> T queryForOne (Class<T> type, String sql, Object ... args) { Connection con = JdbcUtils.getConnection(); try { return queryRunner.query(con, sql, new BeanHandler<T>(type), args); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.close(con); } return null ; } public <T> List<T> queryForList (Class<T> type, String sql, Object ... args) { Connection con = JdbcUtils.getConnection(); try { return queryRunner.query(con, sql, new BeanListHandler<T>(type), args); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { JdbcUtils.close(con); } return null ; } public Object queryForSingleValue (String sql, Object ... args) { Connection conn = JdbcUtils.getConnection(); try { return queryRunner.query(conn, sql, new ScalarHandler(), args); } catch (Exception e) { e.printStackTrace(); } finally { JdbcUtils.close(conn); } return null ; } }
编写UserDao和测试 UserDao
也是属于Dao
层,相比于BaseDao
更加抽象,用来通过用户名查询是否有这个用户,用户名和密码查询,保存用户信息
UserDao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.atguigu.dao;import com.atguigu.pojo.User;public interface UserDao { public User queryUserByUsername (String username) ; public User queryUserByUsernameAndPassword (String username, String password) ; public int saveUser (User user) ; }
UserDaoImpl实现类 UserDaoImpl
实现类继承BaseDao
并实现UserDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.atguigu.dao.impl;import com.atguigu.dao.UserDao;import com.atguigu.pojo.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()); } }
UserDao测试 在test
下创建UserDaoTest
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.atguigu.test;import com.atguigu.dao.UserDao;import com.atguigu.dao.impl.UserDaoImpl;import com.atguigu.pojo.User;import org.junit.Test;import static org.junit.Assert.*;public class UserDaoTest { UserDao userDao = new UserDaoImpl(); @Test public void queryUserByUsername () { if ( userDao.queryUserByUsername("admin1234" ) == null ) { System.out.println("用户名可用!" ); } else { System.out.println("用户名已存在!" ); } } @Test public void queryUserByUsernameAndPassword () { if ( userDao.queryUserByUsernameAndPassword("admin" , "admin1234" ) == null ) { System.out.println("用户名或密码错误,登录失败!" ); } else { System.out.println("登录成功!" ); } } @Test public void saveUser () { System.out.println( userDao.saveUser(new User(null , "wzg169" , "123456" , "wzg169 @qq.com" ))); } }
编写 UserService 和 测试 UserService
更加抽象化,具体完成注册,登录,查询用户名是否存在操作,为Servlet
程序提供服务。
UserService接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.atguigu.service;import com.atguigu.pojo.User;public interface UserService { public void registUser (User user) ; public User login (User user) ; public boolean existsUsername (String username) ; }
UserServiceImpl实现类 UserServiceImpl
实现UseService
,底层实际是调用UserDao
来进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.atguigu.service.impl;import com.atguigu.dao.UserDao;import com.atguigu.dao.impl.UserDaoImpl;import com.atguigu.pojo.User;import com.atguigu.service.UserService;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 ; } }
UserService测试 在test
下创建UserServiceTest
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.atguigu.test;import com.atguigu.pojo.User;import com.atguigu.service.UserService;import com.atguigu.service.impl.UserServiceImpl;import org.junit.Test;import static org.junit.Assert.*;public class UserServiceTest { UserService userService = new UserServiceImpl(); @Test public void registUser () { userService.registUser(new User(null , "bbj168" , "666666" , "bbj168@qq.com" )); userService.registUser(new User(null , "abc168" , "666666" , "abc 168@qq.com" )); } @Test public void login () { System.out.println( userService.login(new User(null , "wzg168" , "123456" , null ))); } @Test public void existsUsername () { if (userService.existsUsername("wzg1688" )){ System.out.println("用户名已存在!" ); } else { System.out.println("用户名可用!" ); } } }
编写Web层 实现用户注册的功能 图解用户注册 修改 regist.html 和 regist_success.html 页面
添加base
标签 通过写base
标签我们可用固定相对路径跳转的结果,这样可以让我们在写相对路径时更清晰,一般推荐这么做。
修改base
标签对相对路径的影响 我们在添加完base
标签之后需要对已有的相对路径进行修改,我们可以先重新部署服务器,之后看哪些资源失败了,来看需要修改哪个的相对路径。
修改注册表单的提交地址和请求方式
编写RegistServlet程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.atguigu.web;import com.atguigu.pojo.User;import com.atguigu.service.UserService;import com.atguigu.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;public class RegistServlet extends HttpServlet { private UserService userService = new UserServiceImpl(); protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); String email = req.getParameter("email" ); String code = req.getParameter("code" ); if ("abcde" .equalsIgnoreCase(code)){ if (userService.existsUsername(username)){ System.out.println("用户名[" + username + "]已存在!" ); req.getRequestDispatcher("/pages/user/regist.html" ).forward(req, resp); } else { userService.registUser(new User(null , username, password, email)); 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映射 在web.xml
中添加如下语句
1 2 3 4 5 6 7 8 <servlet > <servlet-name > RegistServlet</servlet-name > <servlet-class > com.atguigu.web.RegistServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > RegistServlet</servlet-name > <url-pattern > /registServlet</url-pattern > </servlet-mapping >
实现用户登录的功能 图解用户登录
修改 login.html 页面和 login_success.html 页面
添加base
标签
修改base
标签对相对路径的影响
修改注册表单的提交地址和请求方式编写 LoginServlet 程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.atguigu.web;import com.atguigu.pojo.User;import com.atguigu.service.UserService;import com.atguigu.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;public class LoginServlet extends HttpServlet { private UserService userService = new UserServiceImpl(); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); User loginUser = userService.login(new User( null , username, password, null )); if (loginUser == null ) { req.getRequestDispatcher("/pages/user/login.html" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.html" ).forward(req, resp); } } }
配置Servlet映射 在web.xml
中添加如下语句
1 2 3 4 5 6 7 8 <servlet > <servlet-name > LoginServlet</servlet-name > <servlet-class > com.atguigu.web.LoginServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > LoginServlet</servlet-name > <url-pattern > /loginServlet</url-pattern > </servlet-mapping >