一、登陆注册小案例
1、数据库创建与连接测试
-
创建数据库weblogin,创建users用户表
#创建表users CREATE TABLE `weblogin`.`users`( `uid` INT(10) ZEROFILL NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` VARCHAR(100) COMMENT '用户名', `password` VARCHAR(20) COMMENT '密码', `gender` VARCHAR(2) DEFAULT '男' COMMENT '性别', `email` VARCHAR(100) COMMENT '电子邮箱', `birthday` VARCHAR(100) COMMENT '出生日期', PRIMARY KEY (`uid`) ) ENGINE=INNODB CHARSET=utf8 COLLATE=utf8_general_ci; #给表中添加数据 INSERT INTO `weblogin`.`users` (`uid`, `username`, `password`, `email`, `birthday`) VALUES (NULL, '张三', '123456', 'zhangsan@qq.com', '1999-02-03');
-
导入要依赖的jar包
在web/WEB-INF目录下创建lib目录,用来存放JDBC所依赖的jar包
commons-dbcp2-2.7.0.jar、commons-logging-1.2.jar、commons-pool2-2.7.0.jar、mysql-connector-java-5.1.6.jar
并将这些jar包依赖到项目中(选中导入的jar包,右键添加依赖)
-
创建分包
- 在src目录下创建自己的工作路径(域名反转),然后在该路径下创建以下包
- 创建
dao包
,在此包中存放的是用户的业务逻辑接口 - 创建
domain包
,在此包中存放的是实体类,比如Users用户类,数据库中的一张表对应一个实体类 - 创建
filter包
,在此包中存放的是过滤器,用来处理乱码等问题 - 创建
service包
,在此包中存放的是用户的服务接口等 - 创建
servlet包
,在此包中进行处理用户请求 - 创建
test包
,在此包中测试代码 - 创建
util包
,在此包中存放工具类
-
在src目录下创建
jdbc.properties
配置文档,里面进行MySQL数据库的配置driverClassName=com.mysql.jdbc.Driver #weblogin是数据库名称 url=jdbc:mysql://localhost:3306/weblogin?useUnicode=true&characterEncoding=UTF-8 #MySQL数据库账号 username=root #MySQL数据库密码 password=123 initialSize=20
-
导入DBUtils工具类到util包中
package org.westos.util; import java.io.InputStream; import java.sql.*; import java.util.List; import java.util.Properties; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSourceFactory; public class DBUtils { // 访问数据库的小帮手 public static PreparedStatement pstm; // 数据库连接池封装对象 public static DataSource dataSource; // 操作属性文件对象 public static Properties properties = new Properties(); //完成类中静态属性初始化,并且多次访问时,此静态块只执行一次 static { //让is输入流对象指向src目录下的jdbc.properties InputStream is = DBUtils.class.getClassLoader().getResourceAsStream( "jdbc.properties"); try { //加载属性文件到properties对象中 properties.load(is); //数据库连接加载配置文件,完成访问数据库的所有配置 dataSource = BasicDataSourceFactory.createDataSource(properties); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //保证线程安全的数据库访问,一个线程只绑定一个链接对象,多次访问时同一个连接对象 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); //打开数据库连接 public static Connection getConnection() { Connection conn = tl.get();// 从当前线程上获得链接 try { if (conn == null || conn.isClosed() ) { //从连接池中获取连接对象 conn = dataSource.getConnection(); // 把连接绑定到当前线程上 tl.set(conn); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } //关闭所有数据库访问对象 public static void closeAll(Connection con, PreparedStatement pstm, ResultSet rs) { try { if (rs != null) rs.close(); if (pstm != null) pstm.close(); if (con != null && !con.isClosed()) con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //关闭所有数据库访问对象 public static void closeAll() { try { if (pstm != null) pstm.close(); if (DBUtils.getConnection() != null && !DBUtils.getConnection().isClosed()) DBUtils.getConnection().close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 所有的增删改的方法 public static int myExecuteUpdate(String sql, List list) throws Exception { //添加第二个参数的意义是为了获得新增记录的主键。 pstm = getConnection().prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); if (list != null) { for (int i = 0; i < list.size(); i++) { pstm.setObject(i + 1, list.get(i)); } } return pstm.executeUpdate(); } // 所有的查询的方法 public static ResultSet myExecuteQuery(String sql, List list) { try { //第二个参数的意思,执行更新语句后可以获得主键 pstm = getConnection().prepareStatement(sql); if (list != null) { for (int i = 0; i < list.size(); i++) { pstm.setObject(i + 1, list.get(i)); } } return pstm.executeQuery(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static void startTransaction() { Connection conn = getConnection(); try { conn.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } public static void commit() { Connection conn = getConnection(); try { conn.commit(); } catch (SQLException e) { e.printStackTrace(); } } public static void rollback() { Connection conn = getConnection(); try { conn.rollback(); } catch (SQLException e) { e.printStackTrace(); } } public static void release() { Connection conn = getConnection(); try { conn.close(); tl.remove();// 与线程池有关,解除关系 } catch (SQLException e) { e.printStackTrace(); } } public static void endTransaction() { Connection conn = getConnection(); try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } } }
-
创建
JdbcTest
类,测试数据库连接是否正常package org.westos.test; import org.westos.util.DBUtils; import java.sql.Connection; public class JdbcTest { public static void main(String[] args) { Connection conn; try { //使用工具类获得数据库连接对象 conn = DBUtils.getConnection(); if (conn != null) { System.out.println("MySQL连接成功!"); } else { System.out.println("MySQL连接失败!"); } } catch (Exception ex) { ex.printStackTrace(); } } }
2、用户登录注册逻辑实现及测试
-
在demain包下创建Users实体类
Users类中的成员变量名要与数据库中的列名一一对应
package org.westos.domain; /** * 实体层用户表 */ public class Users { private int uid;//编号 private String username; private String gender; private String password; private String email; private String birthday; public Users(int uid, String username, String gender, String password, String email, String birtuday) { this.uid = uid; this.username = username; this.gender = gender; this.password = password; this.email = email; this.birthday = birtuday; } public Users() { } @Override public String toString() { return "Users{" + "uid=" + uid + ", username='" + username + '\'' + ", gender='" + gender + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", birtuday='" + birthday + '\'' + '}'; } public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } 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; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } }
-
在
dao包
下创建UsersDao接口
,定义用户的业务逻辑package org.westos.dao; import org.westos.domain.Users; //用户业务逻辑接口 public interface UsersDao { //用户登录接口,参数为用户名和密码 public Users login(String username, String password); //用户的注册接口,参数为Users对象 public Users reg(Users users); //根据用户的编号查询用户 public Users queryUsersByUid(int uid); }
-
在
dao包
下创建impl子包
,在子包内创建UsersDaoImpl类
UsersDaoImpl类是UsersDao接口的实现类,负责具体实现UsersDao接口中的业务功能
package org.westos.dao.impl; import org.westos.dao.UsersDao; import org.westos.domain.Users; import org.westos.util.DBUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * @BelongsProject: day01-Servlet * @BelongsPackage: org.westos.dao.impl * @Author: 北风 * @CreateTime: 2020-02-14 09:22 * @Description: ${Description} */ public class UsersDaoImpl implements UsersDao { /* 用户登录方法的具体实现 */ @Override public Users login(String username, String password) { Connection conn;//数据库连接对象 ResultSet rs;//数据集 List args = new ArrayList(); //编写SQL执行语句,账号和密码别写死,使用username和password参数 String sql = "select * from Users where username=? and password=?"; //定义登录获得的对象 Users loginUser = null; try { //获得数据库链接对象 conn = DBUtils.getConnection(); //将方法传进来的两个参数添加进集合中 args.add(username); args.add(password); //使用参数中的数据进行查询操作,并得到结果集 rs = DBUtils.myExecuteQuery(sql, args); //如果结果集不为空,也就是根据账号和密码查询到了用户 //说明登陆成功,返回用户对象 if (rs.next()) { //根据用户查询到的信息创建Users对象,返回 loginUser = new Users(); loginUser.setUid(rs.getInt("uid")); loginUser.setUsername(rs.getString("username")); loginUser.setPassword(rs.getString("password")); loginUser.setGender(rs.getString("gender")); loginUser.setEmail(rs.getString("email")); loginUser.setBirthday(rs.getString("birthday")); } return loginUser; } catch (SQLException e) { e.printStackTrace(); return null; } } /* 注册方法的具体实现 */ @Override public Users reg(Users user) { Connection conn; //连接对象 List args = new ArrayList();//集合,存放注册时使用的参数 //注册时使用的SQl语句,参数别写死,使用user对象的属性进行传递 String sql = "insert into users (username,password,gender,email,birthday) values (?,?,?,?,?)"; //注册成功后返回的对象 Users regUser = null; //执行SQl语句返回的受影响行数 int result; try { conn = DBUtils.getConnection(); //开启事务,有更新操作一律要开启事务 DBUtils.startTransaction(); //将注册时用的参数传进args集合中 args.add(user.getUsername()); args.add(user.getPassword()); args.add(user.getGender()); args.add(user.getEmail()); args.add(user.getBirthday()); //执行插入操作,得到受影响行数 result = DBUtils.myExecuteUpdate(sql, args); //当受影响行数大于0,说明插入成功了 if (result > 0) { //获得用户的编号(主键),返回用户的资料 ResultSet temp = DBUtils.pstm.getGeneratedKeys(); temp.next(); //得到当前插入的行的uid int uid = temp.getInt(1); //调用方法,根据uid得到Users对象 regUser = queryUsersByUid(uid); } //提交事务 DBUtils.commit(); //关闭事务 DBUtils.endTransaction(); //返回Users对象 return regUser; } catch (Exception e) { e.printStackTrace(); //失败则事务回滚 DBUtils.rollback(); //关闭事务 DBUtils.endTransaction(); return null; } } /* 根据用户uid查询用户方法的具体实现 */ @Override public Users queryUsersByUid(int uid) { Connection conn; //连接对象 ResultSet rs; //数据集 List args = new ArrayList();//参数集合 //SQL语句,根据uid查询 String sql = "select * from users where uid=?"; Users user = null; try{ //连接数据库 conn = DBUtils.getConnection(); args.add(uid);//添加参数 //查询 rs = DBUtils.myExecuteQuery(sql,args); if(rs.next()){ //说明就登录成功了。用户资料返回 user = new Users(); user.setUid(rs.getInt("uid")); user.setUsername(rs.getString("username")); user.setPassword(rs.getString("password")); user.setGender(rs.getString("gender")); user.setEmail(rs.getString("email")); user.setBirthday(rs.getString("birthday")); } return user; }catch(Exception ex){ ex.printStackTrace(); return null; } } }
-
在
test包
下创建测试类LodinOrRegTest
,测试用户登录和注册功能package org.westos.test; import org.westos.dao.UsersDao; import org.westos.dao.impl.UsersDaoImpl; import org.westos.domain.Users; public class LodinOrRegTest { public static void main(String[] args) { /* //测试一下用户登录的效果代码 Users loginUser = null; UsersDao usersDao = new UsersDaoImpl(); loginUser = usersDao.login("张三","123456"); if(loginUser!=null){ System.out.println("登录成功!"); System.out.println(loginUser); }else{ System.out.println("登录失败!"); }*/ // 测试一下用户注册的效果 //创建及初始化Users对象 Users regUser = new Users(); regUser.setUsername("李四"); regUser.setGender("男"); regUser.setPassword("123456"); regUser.setEmail("lisi@qq.com"); regUser.setBirthday("1999-02-04"); //注册 UsersDao usersDao = new UsersDaoImpl(); regUser = usersDao.reg(regUser); //打印返回的Users对象 System.out.println(regUser); } }
-
在filter包下创建EncodingFilter类,解决乱码问题
package org.westos.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import java.io.IOException; //凡是过滤器,value = "/*",过滤所有的 @WebFilter(description = "EncodingFilter", value = "/*", initParams = {@WebInitParam(name = "encoding", value = "UTF-8")}) public class EncodingFilter implements Filter { private String encoding; @Override public void init(FilterConfig filterConfig) throws ServletException { this.encoding =filterConfig.getInitParameter("encoding"); } //过滤器在过滤请求的时候,一定会执行doFilter方法,将请求和响应的字符集都设置为UTF-8 //过滤器过滤的是请求 @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //给请求和响应设置为UTF-8编码 servletRequest.setCharacterEncoding(this.encoding); servletResponse.setCharacterEncoding(this.encoding); //过滤器链条,确保继续向后过滤 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
-
在service包下创建UsersService接口
package org.westos.service; import org.westos.domain.Users; public interface UsersService { //用户登录的方法 public Users login(String username, String password); //用户注册的方法 public Users reg(Users user); //根据uid获得Users对象的方法 public Users queryUserByUid(int uid); }
-
在service包下创建impl子包及在子包下创建实现类UsersServiceImpl
其实是调用了UsersDaoImpl类实现的方法
package org.westos.service.impl; import org.westos.dao.UsersDao; import org.westos.dao.impl.UsersDaoImpl; import org.westos.domain.Users; import org.westos.service.UsersService; public class UsersServiceImpl implements UsersService { //创建UsersDao接口对象 private UsersDao usersDao = new UsersDaoImpl(); //登录,调用usersDao的登录方法 @Override public Users login(String username, String password) { return usersDao.login(username, password); } //注册,调用usersDao的注册方法 @Override public Users reg(Users user) { return usersDao.reg(user); } //根据uid查询,调用usersDao的查询方法 @Override public Users queryUserByUid(int uid) { return usersDao.queryUsersByUid(uid); } }
-
在servlet包下创建UsersServlet类
控制层,控制jsp、html显示页面的
package org.westos.servlet; import org.westos.domain.Users; import org.westos.service.UsersService; import org.westos.service.impl.UsersServiceImpl; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "UsersServlet", value = "UsersServlet") public class UsersServlet extends HttpServlet { private String action;//标记状态,是登录还是注册 //UsersServiceImpl对象 private UsersService usersService = new UsersServiceImpl(); //判断是注册还是登录,然后分别交给对应的方法 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (request.getParameter("action") != null) { //得到请求的action类型 this.action = request.getParameter("action"); //匹配,调用相应的方法 switch (this.action) { case "login": login(request, response); case "reg": reg(request, response); break; } } } //doGet()方法,调用doPost()方法 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } //注册方法 private void reg(HttpServletRequest request, HttpServletResponse response) throws IOException { Users user = new Users(); //从请求中得到参数,赋值给user对象 user.setUsername(request.getParameter("username")); user.setPassword(request.getParameter("password")); user.setGender(request.getParameter("gender")); user.setEmail(request.getParameter("email")); user.setBirthday(request.getParameter("birthday")); Users regUser = null; //调用usersService对象的注册方法 regUser = usersService.reg(user); //判断,进行界面跳转 if (regUser != null) { System.out.println("注册成功!"); System.out.println(regUser); //注册成功跳转到登录页面 response.sendRedirect(request.getContextPath() + "/login.jsp"); } else { System.out.println("注册失败!"); } } //登录方法 private void login(HttpServletRequest request, HttpServletResponse response) throws IOException { Users loginUser = null; //根据请求的账号和密码,执行usersService的登录方法,并返回Users对象 loginUser = usersService.login(request.getParameter("username"), request.getParameter("password")); //判断 if (loginUser != null) { System.out.println("登陆成功"); //将登陆成功的用户保存在session中 request.getSession().setAttribute("loginUser", loginUser); //请求重定向,登录成功就定向到主界面 response.sendRedirect(request.getContextPath() + "/main.jsp"); } else { System.out.println("登录失败"); //请求重定向,登录失败就定向到登录失败界面 response.sendRedirect(request.getContextPath() + "/login_failure.jsp"); } } }
3、界面实现
-
登录界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户登录</title> <style> #regDiv { /*div水平居中*/ margin: 0px auto; width: 400px; height: 70px; border: 1px solid #ccc; padding: 10px; } h1 { text-align: center; } input { width: 280px; } .label { width: 30%; } .controller { width: 70%; } table { border-collapse: collapse; border-spacing: 0px 0px; height: auto; } </style> </head> <body> <h1>用户登录</h1> <div id="regDiv"> <form action="${pageContext.request.contextPath}/UsersServlet?action=login" method="post"> <table> <tr> <td class="label">用户名:</td> <td class="controller"><input type="text" name="username"></td> </tr> <tr> <td class="label">密码:</td> <td class="controller"><input type="password" name="password"></td> </tr> <tr> <td colspan="2" style="text-align: center"> <input type="submit" style="width:60px" value="登录"> </td> </tr> </table> </form> </div> </body> </html>
-
注册界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>用户注册</title> <style> #regDiv { /*div水平居中*/ margin: 0px auto; width: 400px; height: 160px; border: 1px solid #ccc; padding: 10px; } h1 { text-align: center; } input { width: 280px; } .label { width: 30%; } .controller { width: 70%; } table { border-collapse: collapse; border-spacing: 0px 0px; height: auto; } </style> </head> <body> <h1>用户注册</h1> <div id="regDiv"> <form action="${pageContext.request.contextPath}/UsersServlet?action=reg" method="post"> <table> <tr> <td class="label">用户名:</td> <td class="controller"><input type="text" name="username"></td> </tr> <tr> <td class="label">密码:</td> <td class="controller"><input type="password" name="password"></td> </tr> <tr> <td class="label">确认密码:</td> <td class="controller"><input type="password" name="confirmpass"></td> </tr> <tr> <td class="label">性别:</td> <td class="controller"> <input type="radio" style="width:20px" name="gender" value="男" checked/>男 <input type="radio" style="width:20px" name="gender" value="女" />女 </td> </tr> <tr> <td class="label">电子邮箱:</td> <td class="controller"><input type="text" name="email" value=""/></td> </tr> <tr> <td class="label">出生日期:</td> <td class="controller"><input type="date" name="birthday" value=""/></td> </tr> <tr> <td colspan="2" style="text-align: center"> <input type="submit" style="width:60px" value="注册"> </td> </tr> </table> </form> </div> </body> </html>
-
主页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title></title> </head> <body> <h1>系统主页面</h1> <hr> <div id="main"> 欢迎您:${sessionScope.loginUser.username}<br> </div> </body> </html>
-
登录失败界面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>登录失败</h1> <hr> <a href="login.jsp">返回</a> </body> </html>
4、程序MVC设计思想
-
什么是MVC
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式,或者说是一种软件分层的思想,为了解耦
- Model(模型):表示应用程序核心,应用程序中用于处理应用程序数据逻辑的部分,通常模型对象负责在数据库中存取数据
- View(视图):显示数据,应用程序中处理数据显示的部分,通常视图是依据模型数据创建的
- Controller(控制器):处理输入,应用程序中处理用户交互的部分,通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
-
优点
- 开发人员可以只用管理结构中的某一层
- 方便使用新层替换原有的层
- 降低层间依赖、也就是解耦,降低程序耦合度
- 有利于各层逻辑的复用
-
缺点
- 降低了系统的性能,必须通过中间层才能访问数据库
- 有时会倒是级联的修改,尤其是在上层做出修改后,下层的结构也要做出对应的修改
-
本案例中的程序结构