这里通过一个简单的Web界面模拟用户登录注册功能,对Mybatis和Servlet进行一个综合的练习。
1、需求分析
login.html
和register.html
文件,并分别在其中编写登录界面和注册界面的代码DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>欢迎登录title>
<link rel="stylesheet" href="css/login.css">
head>
<body>
<div id="loginDiv">
<form action="#" method="post" id="loginForm">
<h1 id="loginMsg">SIGN INh1>
<label for="username">
<span>用户名:span>
<input type="text" name="username" id="username" placeholder="请输入用户名">
label>
<br/>
<label for="password">
<span>密码:span>
<input type="password" name="password" id="password" placeholder="请输入密码">
label>
<div id="subDiv">
<input type="submit" class="button" value="登录">
<input type="reset" class="button" value="重置">
<br>
<a href="register.html">没有账号?点击注册a>
div>
form>
div>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>欢迎注册title>
<link rel="stylesheet" href="css/register.css">
head>
<body>
<div id="registerDiv">
<form action="#" method="post" id="registerForm">
<h1 id="registerMsg">SIGN UPh1>
<span class="login">已有账号,去<a href="login.html">登录a>span>
<label for="username">
<span>用户名:span>
<input type="text" name="username" id="username" placeholder="请输入用户名">
label>
<br/>
<label for="password">
<span>密码:span>
<input type="password" name="password" id="password" placeholder="请输入密码">
label>
<div id="subDiv">
<input type="submit" class="button" value="注册">
div>
form>
div>
body>
html>
login.css
和register.css
样式文件,并分别编写登陆和注册界面的样式* {
margin: 0;
padding: 0;
border: 0;
}
body,html{
width: 100%;
height: 100%;
}
body{
margin: 0;
padding: 0;
background: url(../img/Desert.jpg) no-repeat center;
background-size: cover;
}
#loginDiv {
width: 30%;
height: 300px;
align-items: center;
justify-content: center;
position: absolute;
top: 32%;
left: 37%;
background-color: rgba(75, 81, 95, 0.5);
box-shadow: rgba(52, 56, 66, 0.5);
border-radius: 5px;
}
#loginMsg {
text-align: center;
margin: 15px auto;
font-size: 40px;
color: #fff;
padding-bottom: 10px;
text-shadow: 4px 1px 1px #000;
}
label {
margin-top: 30px;
margin-left: 20%;
color: azure;
}
span {
font-size: large;
display: inline-block;
min-width: 80px;
}
input {
height: 30px;
width: 140px;
border-radius: 5px;
border-style: hidden;
outline: none;
color: #f0edf3;
background-color: rgba(216, 191, 216, 0.5);
}
label>input {
width: 250px;
font-size: 16px;
margin: 10px auto;
padding-left: 15px;
}
#subDiv {
text-align: center;
margin: 10px auto;
line-height: 40px;
}
#subDiv>input {
font-size: large;
color: rgb(223, 223, 223);
font-family: "黑体", serif;
margin: 5px 10px;
}
#subDiv>a {
/* text-decoration: none; */
margin-top: 10px;
font-size: 18px;
font-family: "楷体", serif;
color: rgba(223, 223, 223, 0.7);
}
* {
margin: 0;
padding: 0;
border: 0;
}
body,html{
width: 100%;
height: 100%;
}
body{
margin: 0;
padding: 0;
background: url(../img/reg_bg_min.jpg) no-repeat center;
background-size: cover;
}
#registerDiv {
width: 25%;
height: 400px;
align-items: center;
justify-content: center;
position: absolute;
top: 25%;
left: 37%;
background-color: rgba(75, 81, 95, 0.5);
box-shadow: rgba(52, 56, 66, 0.5);
border-radius: 5px;
}
#registerMsg {
margin: 40px auto;
margin-left: 10%;
font-size: 40px;
color: #fff;
padding-bottom: 10px;
text-shadow: 4px 1px 1px #000;
}
label {
margin-left: 15%;
color: azure;
top: 50px;
}
span {
font-size: large;
display: inline-block;
min-width: 80px;
font-size: 19px;
font-family: "楷体" serif;
}
input {
height: 38px;
width: 140px;
border-radius: 5px;
border-style: hidden;
outline: none;
color: #f0edf3;
background-color: rgba(216, 191, 216, 0.5);
}
label>input {
width: 250px;
font-size: 16px;
margin: 20px auto;
padding-left: 15px;
}
#subDiv {
text-align: center;
margin: 10px auto;
}
#subDiv>input {
font-size: large;
color: rgb(223, 223, 223);
font-family: "黑体", serif;
margin: 5px 10px;
}
#subDiv>a {
/* text-decoration: none; */
margin-top: 10px;
font-size: 18px;
font-family: "楷体", serif;
color: rgba(223, 223, 223, 0.7);
}
.login {
position: absolute;
display: inline-block;
top: 95px;
margin-left: 10%;
color: rgba(223, 223, 223, 0.7);
font-size: 18px;
font-family: "楷体", serif;
}
.login>a {
color: rgba(223, 223, 223, 0.7);
}
input::placeholder {
color: rgb(227, 222, 231, 0.7);
}
打开MySQL服务,创建db1数据库,并在该数据库下创建tb_user表,同时插入两条测试数据
CREATE DATABASE db1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE db1;
CREATE TABLE tb_user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) UNIQUE,
`password` VARCHAR(20)
);
INSERT tb_user(username, `password`)
VALUES('张三', '123');
INSERT tb_user(username, `password`)
VALUES('李四', '456');
SELECT * FROM tb_user;
在java.com.xbaozi.pojo
目录下创建与数据库对应的实体类User
private Integer id;
private String username;
private String password;
// 忽略了get/set和toString方法
在项目的pom.xml导入对应的驱动坐标和Tomcat插件,并且创建mybatis-config.xml
核心配置文件、UserMapper.xml
映射文件
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.6version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.2version>
plugin>
plugins>
build>
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.xbaozi.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql:///db1?useSSL=false&useUnicode=true&characterEncoding=utf8&useServerPrepStmts=true"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.xbaozi.mapper"/>
mappers>
configuration>
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xbaozi.mapper.BrandMapper">
mapper>
/**
* 通过用户名和密码实现用户登录
* @param username 用户名
* @param password 密码
* @return 返回一个用户类型的参数。若返回是空则用户名或密码错误,否则登录成功
* @description 通过用户名和密码实现用户登录
* @author xBaozi
* @date 19:35 2022/1/27
**/
@Select("select * from tb_user where username = #{username} and password = #{password}")
User select(@Param("username") String username, @Param("password") String password);
/**
* 登录的Servlet类
* @author xBaozi
* @version 1.0
* @className LoginServlet
* @description 登录的Servlet类
* @date 2022/1/27 19:42
*/
@WebServlet(urlPatterns = "/loginServlet")
public class LoginServlet extends javax.servlet.http.HttpServlet {
@Override
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
// 1. 接受用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 避免username存在中文出现乱码现象
username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// 2. 加载mybatis的核心配置文件,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 获取Mapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 5. 调用方法
User user = mapper.select(username, password);
// 6. 释放资源
sqlSession.close();
// 7. 获取字符输出流并设置content type
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 8. 判断是否登录成功
if (user != null) {
writer.write("登录成功
");
} else {
writer.write("登录失败
");
}
}
@Override
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
this.doPost(request, response);
}
}
login.html
中表单的action为/web-demo02/loginServlet
<form action="${pageContext.request.contextPath}/web-demo02/loginServlet" method="post" id="loginForm">
<h1 id="loginMsg">SIGN INh1>
<label for="username">
<span>用户名:span>
<input type="text" name="username" id="username" placeholder="请输入用户名">
label>
<br/>
<label for="password">
<span>密码:span>
<input type="password" name="password" id="password" placeholder="请输入密码">
label>
<div id="subDiv">
<input type="submit" class="button" value="登录">
<input type="reset" class="button" value="重置">
<br>
<a href="register.html">没有账号?点击注册a>
div>
form>
/**
* 通过用户名实现用户的查询
* @description 通过用户名实现用户的查询
* @author xBaozi
* @date 13:34 2022/1/28
* @param username 需要查询的用户名
* @return 返回一个用户类型的参数。若返回是空则用户名可以注册创建,否则用户名已存在
**/
@Select("select * from tb_user where username = #{username}")
User selectByUsername(String username);
/**
* 往数据库中插入数据
* @description 往数据库中插入数据
* @author xBaozi
* @date 13:40 2022/1/28
* @param user 需要插入的user对象
**/
@Insert("insert tb_user(`username`, `password`) values(#{username}, #{password})")
void add(User user);
@WebServlet(urlPatterns = "/registerServlet")
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
username = new String(username.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
// 2. 加载mybatis的核心配置文件,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
// 3. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 获取Mapper对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 5. 调用方法
User user = mapper.selectByUsername(username);
// 6. 获取字符输出流并设置content type
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
// 7. 判断该用户名是否符合注册条件
if (user == null) {
mapper.add(new User(null, username, password));
sqlSession.commit();
writer.write("注册成功
");
} else {
writer.write("该用户名已存在
");
}
// 8. 关闭资源
sqlSession.close();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
register.html
中表单的action为/web-demo02/registerServlet
<form action="${pageContext.request.contextPath}/web-demo02/registerServlet" method="post" id="registerForm">
<h1 id="registerMsg">SIGN UPh1>
<span class="login">已有账号,去<a href="login.html">登录a>span>
<label for="username">
<span>用户名:span>
<input type="text" name="username" id="username" placeholder="请输入用户名">
label>
<br/>
<label for="password">
<span>密码:span>
<input type="password" name="password" id="password" placeholder="请输入密码">
label>
<div id="subDiv">
<input type="submit" class="button" value="注册">
div>
form>
在实现上述代码的时候会发现,在写Servlet的时候,因为需要使用Mybatis来完成数据库的操作,所以对于Mybatis的基础操作就出现如下的重复代码
String resource = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
这就会以下的一些问题:
那如何来优化呢?
public class SqlSessionFactoryUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
工具类抽取以后,以后在对Mybatis的SqlSession进行操作的时候,就可以直接使用,这样就可以很好的解决上面所说的代码重复和重复创建工厂导致性能低的问题了。
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory();
这里存在一个小坑,就是获取SqlSession对象的代码也是重复的,那到底要不要也封装到工具类里面去呢?
答案是不需要,也不能要。
对SqlSessionFactory对象进行封装是从性能角度出发,这个工厂不需要每制造一个SqlSession就同步生成一个从而造成资源浪费。而SqlSession是对数据库进行操作的对象,其可能会对不同的数据库和表进行操作,如果混为一谈则可能会出现数据错乱的情况。
这就如同家中的毛巾,毛巾同样都是擦东西的功能,但我们不可能用打扫卫生的毛巾来擦身子。