书城项目第八阶段:使用Filter过滤器实现后台的权限管理

简介: 书城项目第八阶段:使用Filter过滤器实现后台的权限管理

7、书城第八阶段

1、使用Filter过滤器拦截/pages/manager/所有内容,实现权限检查

新建com.atguigu/filter/MangerFilter

package com.atguigu.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class ManageFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest= (HttpServletRequest) servletRequest;
        Object user = httpServletRequest.getSession().getAttribute("user");
        if (user==null){
            httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);
        }else {
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    @Override
    public void destroy() {
    }
}

配置web.xml

<filter>
        <filter-name>ManageFilter</filter-name>
        <filter-class>com.atguigu.filter.ManageFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ManageFilter</filter-name>
        <url-pattern>/pages/manager/*</url-pattern>
        <url-pattern>/manager/bookServlet</url-pattern>
    </filter-mapping>

2、ThreadLocal的使用

ThreadLocal的作用,它可以解决多线程的数据安全问题。
ThreadLocal它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

Threadlocal的特点:

1、Threadlocal可以为当前线程关联一个数据。(它可以像Map一样存取数据,key为当前线程)

2、每一个Threadlocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 Threadlocal对象实例。

3、每个Threadlocal对象实例定义的时候,一般都是static类型

4、Threadlocal中保存数据,在线程销毁后。会由JVM虚拟自动释放。

新建tmp/src/threadlocal/ThreadLocalTest

package threadlocal;
import java.util.Random;
public class ThreadLocalTest {
//    public final static Map<String,Object> data=new ConcurrentHashMap<>();线程安全
//    public final static Map<String,Object> data=new Hashtable<>();
    public static ThreadLocal<Object> threadLocal=new ThreadLocal<>();
    private static Random random=new Random();
    public static class Task implements Runnable{
        @Override
        public void run() {
//            threadLocal.set("abc");
//            threadLocal.set("bbj");
//            System.out.println(threadLocal.get());//bbj 覆盖
            //在run方法中 ,随机生成一个变量(线程要关联的数据),然后一当前线程名为key保存到map中
            Integer i = random.nextInt(1000);
            //获取当前线程名
            String name = Thread.currentThread().getName();
            System.out.println("线程["+name+"]生成的随机数是:"+i);
//            data.put(name,i);
            threadLocal.set(i);
            //模拟操作
//            try {
//                Thread.sleep(3000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            new OrderService().createOrder();
            //在Run方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
//            Object o = data.get(name);
            Object o=threadLocal.get();
            System.out.println("线程["+name+"]快结束时取出关联的数据是:"+o);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(new Task()).start();
        }
    }
}

新建threadlocal/OrderService

package threadlocal;
public class OrderService {
    public void createOrder(){
        String name = Thread.currentThread().getName();
        System.out.println("OrderService 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
        new OrderDao().saveOrder();
    }
}

新建threadlocal/OrderDao

package threadlocal;
public class OrderDao {
    public void saveOrder(){
        String name = Thread.currentThread().getName();
//        System.out.println("OrderDao 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.data.get(name));
        System.out.println("OrderDao 当前线程["+name+"]中保存的数据是:"+ThreadLocalTest.threadLocal.get());
    }
}

结果


3、使用Filter和ThreadLocal组合管理事务

3.1 使用ThreadLocal确保所有操作都使用同一个Connection来实现

验证是否为同一线程

修改 OrderServiceImpl 模拟错误

package com.atguigu.service.impl;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.OrderDao;
import com.atguigu.dao.OrderItemDao;
import com.atguigu.dao.impl.BookDaoImpl;
import com.atguigu.dao.impl.OrderDaoImpl;
import com.atguigu.dao.impl.OrderItemDaoImpl;
import com.atguigu.pojo.*;
import com.atguigu.service.OrderService;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class OrderServiceImpl implements OrderService {
    private OrderDao orderDao =new OrderDaoImpl();
    private OrderItemDao orderItemDao=new OrderItemDaoImpl();
    private BookDao bookDao=new BookDaoImpl();
    @Override
    public String createOrder(Cart cart, Integer userId) {
        System.out.println("OrderServiceImpl 程序在["+Thread.currentThread().getName()+"]中");
        //订单号==唯一性
        String orderId=System.currentTimeMillis()+""+userId;
        //创建一个订单对象
        Order order=new Order(orderId,new Date(),cart.getTotalPrice(),0,userId);
        //保存订单
        orderDao.saveOrder(order);
        //模拟错误
        int i=12/0;
        //遍历购物车中每一个商品项转换为订单保存到数据库
        for (Map.Entry<Integer, CartItem>entry:cart.getItems().entrySet()) {
            //获取购物车每一个商品项
            CartItem cartItem=entry.getValue();
            //转换为订单
            OrderItem orderItem=new OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),orderId);
            //保存到数据库
            orderItemDao.saveOrderItem(orderItem);
            //更新库存和销量
            Book book = bookDao.queryBookById(cartItem.getId());
            book.setSales(book.getSales()+cartItem.getCount());
            book.setStock(book.getStock()-cartItem.getCount());
            bookDao.updateBook(book);
        }
        //清空购物车
        cart.clear();
        return orderId;
    }
    @Override
    public List<Order> showAllOrders() {
        return orderDao.queryOrders();
    }
    @Override
    public int sendOrder(String orderId) {
        return orderDao.changeOrderStatus(orderId,1);
    }
    @Override
    public List<OrderItem> showOrderDetail(String orderId) {
        return orderItemDao.queryOrderItemByOrderId(orderId);
    }
    @Override
    public List<Order> showMyOrders(int userId) {
        return orderDao.queryByUserId(userId);
    }
    @Override
    public int receiverOrder(String orderId) {
        return orderDao.changeOrderStatus(orderId,2);
    }
}

结果 网页,添加购物车去结账

t_order表无记录

t_order_item有记录

原理

修改 JdbcUtils

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;
    private static ThreadLocal<Connection> conns=new ThreadLocal<>();
    static {
        try {
            Properties properties=new Properties();
            //读取jdbc.properties属性配置文件
            InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            //从流中加载数据
            properties.load(inputStream);
            //创建数据连接池
            dataSource= (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);//Ctrl+ALT+T
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    /**
     * 获取数据库连接池中的连接
     * @return 如果返回null,说明获取连接失败<br/> 有值就是获取连接成功
     */
    public static Connection getConnection(){
        Connection conn=conns.get();
        if (conn==null){
            try {
                conn= dataSource.getConnection();//从数据库连接池中获取连接
                conns.set(conn);//保存到ThreadLocal对象中,供后面的jdbc操作使用
                conn.setAutoCommit(false);//设置为手动管理事务
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        return conn;
    }
    /**
     * 提交事务,并关闭释放连接
     */
    public static void commitAndClose(){
        Connection connection=conns.get();
        if (connection!=null){//如果不等于null,说明之前使用过连接,操作过数据库
            try {
                connection.commit();//提交 事务
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }finally {
                try {
                    connection.close();//关闭连接,释放资源
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        //一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }
    /**
     * 回滚事务,并关闭释放连接
     */
    public static void rollbackAndClose(){
        Connection connection=conns.get();
        if (connection!=null){//如果不等于null,说明之前使用过连接,操作过数据库
            try {
                connection.rollback();//回滚 事务
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }finally {
                try {
                    connection.close();//关闭连接,释放资源
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        //一定要执行remove操作,否则就会出错。(因为Tomcat服务器底层使用了线程池技术)
        conns.remove();
    }
        /**
         * 关闭连接,放回数据库连接池
         * @param conn
        public static void close(Connection conn){
            if (conn!=null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
         */
}

修改 JdbcUtilsTest

package com.atguigu.test;
import org.junit.Test;
public class JdbcUtilsTest {
    @Test
    public void testJdbcUtils(){
//        for (int i = 0; i < 100; i++) {
//            Connection connection = JdbcUtils.getConnection();
//            System.out.println(connection);
//            JdbcUtils.close(connection);
//        }
    }
}

修改 BaseDao

package com.atguigu.dao;
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 {
    //使用DbUtils操作数据库
    private QueryRunner queryRunner=new QueryRunner();
    /**
     * update() 方法用来执行,Insert\Update\Delete语句
     * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
     */
    public int update(String sql,Object... args){
        System.out.println("BaseDao 程序在["+Thread.currentThread().getName()+"]中");
        Connection connection= JdbcUtils.getConnection();
        try {
            return queryRunner.update(connection,sql,args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 查询返回一个javabean的sql语句
     * @param type 返回的对象类型
     * @param sql 执行的sql语句
     * @param args sql对应的参数值
     * @param <T> 返回类型的泛型
     * @return
     */
    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 e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 查询返回多个javabean的sql语句
     * @param type 返回的对象类型
     * @param sql 执行的sql语句
     * @param args sql对应的参数值
     * @param <T> 返回类型的泛型
     * @return
     */
    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 e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 执行返回一行一列的sql语句
     * @param sql 执行的sql语句
     * @param args sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql,Object... args){
        Connection conn=JdbcUtils.getConnection();
        try {
            return queryRunner.query(conn,sql,new ScalarHandler(),args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

修改 OrderServlet

package com.atguigu.web;
import com.atguigu.pojo.Cart;
import com.atguigu.pojo.User;
import com.atguigu.service.OrderService;
import com.atguigu.service.impl.OrderServiceImpl;
import com.atguigu.utils.JdbcUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OrderServlet extends BaseServlet {
    private OrderService orderService = new OrderServiceImpl();
    /**
     * 生成订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先获取Cart购物车对象
        Cart cart = (Cart) req.getSession().getAttribute("cart");  //获取Userid
        User loginUser = (User) req.getSession().getAttribute("user");
        if (loginUser == null) {
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
            return;
        }
        System.out.println("OrderServlet 程序在["+Thread.currentThread().getName()+"]中");
        Integer userId = loginUser.getId();
        //调用orderservice.createorder(cart,userid);生成订单
        String orderId = null;
        try {
            orderId = orderService.createOrder(cart, userId);
            JdbcUtils.commitAndClose();//提交事务
        } catch (Exception e) {
            JdbcUtils.rollbackAndClose();//回滚事务
            e.printStackTrace();
        }
//        req.setAttribute("orderId",orderId);
        //请求转发至pages/cart/checkout.jsp
//        req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req,resp);
        req.getSession().setAttribute("orderId", orderId);
        resp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");
    }
    /**
     * 查看所有订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 发货
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 查看订单详情
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 查看我的订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showMyOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 签收订单/确认收货
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void receiverOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}

结果 网页,添加购物车去结账


3.2、使用Filter统一给所有Service方法都加上try-catch来实现管理事务


新建 filter/TransactionFilter

package com.atguigu.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public abstract class BaseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action=req.getParameter("action");
//        System.out.println(action);
        //action的value和调用的方法名是统一的
//        if ("login".equals(action)){
            System.out.println("处理登录的需求");
//           login(req,resp);
//        }else if ("regist".equals(action)){
            System.out.println("处理注册的需求");
//            regist(req,resp);
//        }
        //反射
        try {
            //获取action业务鉴别字符串,获取相应的业务方法 反射对象
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            //调用目标业务方法
            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给Filter过滤器
        }
    }
}

配置web.xml

<filter>
        <filter-name>TransactionFilter</filter-name>
        <filter-class>com.atguigu.filter.TransactionFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>TransactionFilter</filter-name>
        <!--  /*当前工程下所有请求      -->
        <url-pattern>/*</url-pattern>
    </filter-mapping>

修改 OrderServlet

package com.atguigu.web;
import com.atguigu.pojo.Cart;
import com.atguigu.pojo.User;
import com.atguigu.service.OrderService;
import com.atguigu.service.impl.OrderServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class OrderServlet extends BaseServlet {
    private OrderService orderService = new OrderServiceImpl();
    /**
     * 生成订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void createOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //先获取Cart购物车对象
        Cart cart = (Cart) req.getSession().getAttribute("cart");  //获取Userid
        User loginUser = (User) req.getSession().getAttribute("user");
        if (loginUser == null) {
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
            return;
        }
        System.out.println("OrderServlet 程序在["+Thread.currentThread().getName()+"]中");
        Integer userId = loginUser.getId();
        //调用orderservice.createorder(cart,userid);生成订单
        String orderId = orderService.createOrder(cart, userId);
//        req.setAttribute("orderId",orderId);
        //请求转发至pages/cart/checkout.jsp
//        req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req,resp);
        req.getSession().setAttribute("orderId", orderId);
        resp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");
    }
    /**
     * 查看所有订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showAllOrders(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 发货
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void sendOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 查看订单详情
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 查看我的订单
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void showMyOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
    /**
     * 签收订单/确认收货
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void receiverOrder(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}

修改 BaseServlet

package com.atguigu.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public abstract class BaseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action=req.getParameter("action");
//        System.out.println(action);
        //action的value和调用的方法名是统一的
//        if ("login".equals(action)){
            System.out.println("处理登录的需求");
//           login(req,resp);
//        }else if ("regist".equals(action)){
            System.out.println("处理注册的需求");
//            regist(req,resp);
//        }
        //反射
        try {
            //获取action业务鉴别字符串,获取相应的业务方法 反射对象
            Method method = this.getClass().getDeclaredMethod(action,HttpServletRequest.class,HttpServletResponse.class);
            //调用目标业务方法
            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给Filter过滤器
        }
    }
}

3.3、将所有异常都交给Tomcat,让Tomcat显示友好的错误信息页面

在web.xml 中我们可以通过错误页面配置来进行管理。

新建 pages/error/error500.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>error500</title>
    <%--静态包含 base标签,css样式,jquery文件 --%>
    <%@ include file="/pages/common/head.jsp"%>
</head>
<body>
很抱歉,您访问的页面不存在,或已经被删除!!!<br>
<a href="index.jsp">返回首页</a>
</body>
</html>

并配置 web.xml

 <!-- error-page标签配置,服务器出错之后,自动跳转的页面   -->
    <error-page>
        <!--  error-code是错误类型      -->
        <error-code>500</error-code>
        <!-- location标签表示,要跳转的页面路径-->
        <location>/pages/error/error500.jsp</location>
    </error-page>

修改 TransactionFilter

package com.atguigu.filter;
import com.atguigu.utils.JdbcUtils;
import javax.servlet.*;
import java.io.IOException;
public class TransactionFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            filterChain.doFilter(servletRequest,servletResponse);
            JdbcUtils.commitAndClose();//提交事务
        } catch (Exception e) {
            JdbcUtils.rollbackAndClose();//回滚事务
            e.printStackTrace();
            throw new RuntimeException(e);//把异常抛给Tomcat管理展示友好的错误页面
        }
    }
    @Override
    public void destroy() {
    }
}

新建error/error404.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>error404</title>
    <%--静态包含 base标签,css样式,jquery文件 --%>
    <%@ include file="/pages/common/head.jsp"%>
</head>
<body>
很抱歉,您访问的页面不存在,或已经被删除!!!<br>
<a href="index.jsp">返回首页</a>
</body>
</html>

配置web.xml

<!-- error-page标签配置,服务器出错之后,自动跳转的页面   -->
    <error-page>
        <!--  error-code是错误类型      -->
        <error-code>404</error-code>
        <!-- location标签表示,要跳转的页面路径-->
        <location>/pages/error/error404.jsp</location>
    </error-page>
相关文章
|
网络协议 Linux 网络安全
Linux系列——关于防火墙iptables的常用命令
Linux系列——关于防火墙iptables的常用命令
|
Java 网络安全 开发工具
【Azure 事件中心】Event Hub 无法连接,出现 Did not observe any item or terminal signal within 60000ms in 'flatMapMany' 的错误消息
【Azure 事件中心】Event Hub 无法连接,出现 Did not observe any item or terminal signal within 60000ms in 'flatMapMany' 的错误消息
615 1
|
11月前
|
存储 算法 JavaScript
【动态规划篇】股海擒龙诀:精准狙击股票买卖最佳时机
【动态规划篇】股海擒龙诀:精准狙击股票买卖最佳时机
|
监控 安全 网络安全
WPA2 与 802.1X:有什么区别?
【10月更文挑战第17天】
1176 1
WPA2 与 802.1X:有什么区别?
|
存储 SQL 弹性计算
元数据驱动的 SaaS 架构与背后的技术思考
在抽象能力以及沉淀了产品的基础上,把所承载和沉淀的业务能力快速输出,贡献给整个行业。
10308 108
元数据驱动的 SaaS 架构与背后的技术思考
|
SQL 缓存 JavaScript
PageHelper 使用中的一些坑
PageHelper 使用中的一些坑
PageHelper 使用中的一些坑
|
监控 NoSQL 算法
在Linux中,如何排查死锁问题?
在Linux中,如何排查死锁问题?
|
安全 网络安全 数据安全/隐私保护
深入理解IP劫持及其对网络安全的影响
【8月更文挑战第24天】
827 0
|
SQL 缓存 关系型数据库
日志系统:一条SQL更新语句是如何执行的?
本文探讨了MySQL中更新语句的执行流程和日志系统,包括redo log(重做日志)和binlog(归档日志)的作用。更新语句会经过连接器、分析器、优化器和执行器,同时涉及redo log和binlog以确保数据的安全性和一致性。redo log用于快速更新并保证crash-safe,采用Write-Ahead Logging策略,先写入redo log再更新磁盘。binlog则是逻辑日志,用于归档和数据恢复。两阶段提交保证redo log和binlog的一致性。文章还提到,定期全量备份的频率影响数据库系统的恢复能力和数据安全性。
237 0
日志系统:一条SQL更新语句是如何执行的?
|
Ubuntu 网络协议 Java
在Android平板上使用code-server公网远程Ubuntu服务器编程
在Android平板上使用code-server公网远程Ubuntu服务器编程
325 0