前言1、环境准备
1.1、项目搭建1.2、界面编写1.3、数据库搭建1.4、实体类创建1.5、导入依赖并编写配置文件 2、登录实现
2.1、需求分析2.2、代码实现2.3、结果演示 3、注册实现
3.1、需求分析3.2、代码实现3.3、结果演示 4、代码优化
前言这里通过一个简单的Web界面模拟用户登录注册功能,对Mybatis和Servlet进行一个综合的练习。
使用Maven搭建项目使用HTML编写简单的前端界面使用Mybatis连接MySQL数据库并进行操作通过Servlet对登录注册的请求做出响应
1、需求分析
1、环境准备 1.1、项目搭建- 创建Web项目,这里是不使用骨架进行创建,具体步骤参考往期博客JavaWeb-IDEA利用Tomcat发布网站中的“不使用骨架搭建Web项目”
- 在webapp下创建login.html和register.html文件,并分别在其中编写登录界面和注册界面的代码
欢迎登录
欢迎注册
- 在webapp下的css文件夹下新建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 {
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 {
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);
}
1.3、数据库搭建
打开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;
1.4、实体类创建
在java.com.xbaozi.pojo目录下创建与数据库对应的实体类User
private Integer id; private String username; private String password; // 忽略了get/set和toString方法1.5、导入依赖并编写配置文件
在项目的pom.xml导入对应的驱动坐标和Tomcat插件,并且创建mybatis-config.xml核心配置文件、UserMapper.xml映射文件
org.mybatis mybatis 3.5.5 mysql mysql-connector-java 5.1.47 javax.servlet javax.servlet-api 3.1.0 provided commons-io commons-io 2.6 org.apache.tomcat.maven tomcat7-maven-plugin 2.2
2、登录实现 2.1、需求分析
- 用户在登录页面输入用户名和密码,提交请求给LoginServlet在LoginServlet中接收请求和数据[用户名和密码]在LoginServlt中通过Mybatis实现调用UserMapper来根据用户名和密码查询数据库表将查询的结果封装到User对象中进行返回在LoginServlet中判断返回的User对象是否为null如果为nul,说明根据用户名和密码没有查询到用户,则登录失败,返回"登录失败"数据给前端如果不为null,则说明用户存在并且密码正确,则登录成功,返回"登录成功"数据给前端
- 在UserMapper接口中提供一个根据用户名和密码查询用户对象的方法。@Param注解的作用:用于传递参数,是方法的参数可以与SQL中的字段名相对应。
@Select("select * from tb_user where username = #{username} and password = #{password}")
User select(@Param("username") String username, @Param("password") String password);
- 编写LoginServlet
@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
账号密码错误情况
账号密码正确
3、注册实现 3.1、需求分析- 用户在注册页面输入用户名和密码,提交请求给RegisterServlet在RegisterServlet中接收请求和数据,即用户名和密码在RegisterServlet中通过Mybatis实现调用UserMapper来根据用户名查询数据库表将查询的结果封装到User对象中进行返回在RegisterServlet中判断返回的User对象是否为null如果为null,说明根据用户名可用,则调用UserMapper来实现添加用户如果不为null,则说明用户不可以,返回"用户名已存在"数据给前端
- 编写UserMapper提供根据用户名查询用户数据方法和添加用户方法
@Select("select * from tb_user where username = #{username}")
User selectByUsername(String username);
@Insert("insert tb_user(`username`, `password`) values(#{username}, #{password})")
void add(User user);
- 编写RegisterServlet类
@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
用户已存在情况
注册成功情况
4、代码优化在实现上述代码的时候会发现,在写Servlet的时候,因为需要使用Mybatis来完成数据库的操作,所以对于Mybatis的基础操作就出现如下的重复代码
String resource = "mybatis-config.xml"; InputStream is = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
这就会以下的一些问题:
重复代码不利于后期的维护SqlSessionFactory工厂类进行重复创建。相当于每次买手机都需要重新创建一个手机生产工厂来给你制造一个手机一样,资源消耗非常大但性能却非常低。
那如何来优化呢?
代码重复可以抽取工具类对指定代码只需要执行一次可以使用静态代码块
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是对数据库进行操作的对象,其可能会对不同的数据库和表进行操作,如果混为一谈则可能会出现数据错乱的情况。
这就如同家中的毛巾,毛巾同样都是擦东西的功能,但我们不可能用打扫卫生的毛巾来擦身子。



