m:model(模型)
V:view(视图)
C:controller(控制器)
1.用户通过浏览器传递一个请求给客户端(view视图层将用户的数据传输给Controller控制器层)
2.Controller控制器层拿到用户的请求之后交给Model层中的(service)去处理相应的业务(例:登录请求)
3.service登录事务通过Dao访问JDBC查询用户是否存在
4.Dao将查询的结果返回登录的Service事务
5.Service登录事务将结果返回给控制器层Controller
6.控制层Controller将结果输出到视图层View
7.用户看到处理的结果(登录成功/登录失败)
作用:过滤掉一些不需要的垃圾信息/统一处理乱码的问题/登录验证。。。
Filter实际上也是一种servlet
使用时需要实现java.servlet中的Filter接口
编写过滤器需要重写过滤器中的方法(共有三种)
1.init(初始化过滤器)
public class CharacterEncodingFilter implements Filter {
//初始化 web服务器开启时候初始化
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
}
2.doFilter方法(过滤器主体)
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter执行前");
filterChain.doFilter(servletRequest,servletResponse);//**将请求和响应给到下一个过滤器,如果不写,程序到这里就被拦截停止了
System.out.println("CharacterEncodingFilter执行后");
}
注意!
方法中有三个参数,前两个是ServletRequest和servletResponse,第三个则是filterChain
filterChain值过滤器链:实际开发之后一个网页可能需要添加很多个过滤器而过滤器链filterChain则是将过滤器传递下去的方法(必须写)
Filter中的ServletRequest和servletResponse没有Servlet中的HttpServletRequest和HttpServletRespon作用域广
例如:
// servletRequest.get。。。 无法get到session 扩大它的对象到HttpServletRequest
HttpServletRequest request= (HttpServletRequest) servletRequest;
//将servletRequest强制转换成HttpServletRequest
HttpServletResponse response= (HttpServletResponse) servletResponse;
//将servletResponse强制转换成HttpServletResponse
3.destroy(过滤器销毁)
//销毁 web服务器关闭的时候销毁
public void destroy() {
System.out.println("过滤器销毁");
}
}
运行时在提示栏中看见过滤器初始化在web服务器开启的时候出现
而过滤器销毁则是在web服务器关闭的时候销毁
实现拦截未登录用户访问个人主页页面
1.创建一个登录页面 登录成功后跳转到主页中
Jsp登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
登录
Servlet实现登录业务
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
if (username.equals("admin")){//用户登录成功之后跳转页面 并把用户的session记录下来
resp.sendRedirect("/Sys/homepage.jsp");
req.getSession().setAttribute("User_session",req.getSession().getId());
}else{
resp.sendRedirect("/error.jsp");
}
}
}
2.登录成功后进入主页 主页中包含了一个注销功能注销后返回登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
主页
主页
注销
public class logout extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object User_session =req.getSession().getAttribute("User_session");//先获取浏览器的Session中的User_session
if (User_session!=null) {//判断User_session是否存在
req.getSession().removeAttribute("User_session");//存在便移除
resp.sendRedirect("/Login.jsp");
}else{
resp.sendRedirect("/Login.jsp");
}
}
3.用户名错误则会跳转到错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
用户名错误
点击重试
4.未登录的用户不能访问主页(用过滤器实现)
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// servletRequest.get。。。 无法get到session 扩大它的对象到HttpServletRequest
HttpServletRequest request= (HttpServletRequest) servletRequest;
//将servletRequest强制转换成HttpServletRequest
HttpServletResponse response= (HttpServletResponse) servletResponse;
//将servletResponse强制转换成HttpServletResponse
Object userSession = request.getSession().getAttribute("User_session");
if (userSession==null){
response.sendRedirect("/error.jsp");
}
filterChain.doFilter(servletRequest,servletResponse);
}
在web.xml中配置
<servlet>
<servlet-name>loginservlet-name>
<servlet-class>com.you.servlet.loginservlet-class>
servlet>
<servlet-mapping>
<servlet-name>loginservlet-name>
<url-pattern>/Servlet/loginurl-pattern>
servlet-mapping>
<servlet>
<servlet-name>logoutservlet-name>
<servlet-class>com.you.servlet.logoutservlet-class>
servlet>
<servlet-mapping>
<servlet-name>logoutservlet-name>
<url-pattern>/Servlet/logouturl-pattern>
servlet-mapping>
<filter>
<filter-name>LoginFilterfilter-name>
<filter-class>com.you.Filter.LoginFilterfilter-class>
filter>
<filter-mapping>
<filter-name>LoginFilterfilter-name>
<url-pattern>/Sys/*url-pattern>
filter-mapping>
需要jar包的支持:
实验环境搭建(创建数据库)
导入数据库依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.19version>
dependency>
1.配置参数(Url username password)
//配置信息
String url="jdbc:mysql://localhost:3306/jdbc?serverTimezone=UTC";
String username="root";
String password="123";
2.用Class.forName()加载数据库驱动
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//"com.mysql.cj.jdbc.Driver"在jdbc的依赖包中有Driver.class文件复制导入的包.Driver
3.连接数据库
//连接数据库,代表数据库
Connection connection = DriverManager.getConnection(url, username, password);
以上是公共部分
而写sql语句到数据库中有两种方案
普通方案:connection.createStatement
//向数据库发送sql语句的对象statement:CRUD
Statement statement = connection.createStatement();
//编写sql语句
String sql="select * from jdbc.people";
//执行sql 返回一个rs 这是个结果集
ResultSet rs = statement.executeQuery(sql);
预编译方案:connection.prepareStatement
//编写预编译的sql语句
String sql="insert into jdbc.people (id,name,age,address) value (?,?,?,?)";
//先把预编译的sql语句传过去
//3.获取statement对象用于传输sql语句,prepareStatement预编译
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//预编译完成之后将具体值传进去
preparedStatement.setInt(1,4);//给第一个占位符(?) 赋值为1
preparedStatement.setString(2,"水");//给第二个占位符(?) 赋值为水
preparedStatement.setInt(3,40);//给第三个占位符(?) 赋值为40
preparedStatement.setString(4,"东南");//给第四个占位符(?) 赋值为东南
//5.执行sql
int i = preparedStatement.executeUpdate();
最后记得关闭sql资源(先开后关)
//6.关闭连接 释放资源(一定要做) 先开后关
rs.close();
statement.close();
connection.close();
注意 执行sql语句这里
如果是增删改则使用executeUpdate();即可
而如果是查找则需要使用executeQuery(sql);
事务:
要么都成功,要么都失败!
ACID原则:保证数据的安全。
事务的流程
开启事务
事务提交 commit()
事务回滚 rollback()
关闭事务
转账
A:1000
B:1000
A(900)----100---B(1100)
如果此时服务器断电了 会出现A-100而B并没有+100
这时候就需要事务了
将两个事件绑定在一起进行
这里插入一个新学习的知识点
Junit单元测试
1.导入Junit jar包
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>RELEASEversion>
<scope>compilescope>
/dependency>
简单使用
@Test注解只在方法中有效,只要加了注解的方法,就可以直接运行
public class JunitTest {
@Test
public void B(){
System.out.println("junit测试");
}
}
利用单元测试我们来实现一个转账事务的小Demo
首先在mysql中创建一个account表
其中包含了 id name money
开启事务的mysql流程
START TRANSACTION; #开启事务
update account set money = money-100 where jdbc.account.name='A';
update account set money = money+100 where jdbc.account.name='B';
COMMIT;
在commit之前使用rollback 可以回滚
应用:转账错误
@Test
public void B() {
String url="jdbc:mysql://localhost:3306/jdbc?serverTimezone=UTC";
String username="root";
String password="123";
Connection connection = null;
//2.加载驱动 连接数据库
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection(url, username, password);
//3.通知数据库开启事务 false开启;取反
connection.setAutoCommit(false);
String sql1 ="update account set money = money-100 where jdbc.account.name='A'";
connection.prepareStatement(sql1).executeUpdate();
//制造一个错误
// int i =1/0;
String sql2="update account set money = money+100 where jdbc.account.name='B'";
connection.prepareStatement(sql2).executeUpdate();
connection.commit();//以上两条sql都执行成功了,就会提交事务
System.out.println("提交成功");
} catch (Exception e) {
try {
//如果出现异常就通知数据库回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}