servlet映射路径匹配解析

开头

servlet是javaweb用来处理请求和响应的重要对象,本文将从源码的角度分析tomcat内部是如何根据请求路径匹配得到处理请求的servlet的

假设有一个request请求路径为/text/servlet/get,并且在web.xml中配置了4个servlet,代码如下,那么该请求调用的是哪一个servlet呢?

<servlet>
    <servlet-name>servlet01</servlet-name>
    <servlet-class>com.monian.study.servlet.Servlet01</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet01</servlet-name>
    <url-pattern>/test/servlet/get</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>servlet02</servlet-name>
    <servlet-class>com.monian.study.servlet.Servlet02</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet02</servlet-name>
    <url-pattern>/test/servlet/*</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>servlet03</servlet-name>
    <servlet-class>com.monian.study.servlet.Servlet03</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet03</servlet-name>
    <url-pattern>/test/*</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>servlet04</servlet-name>
    <servlet-class>com.monian.study.servlet.Servlet04</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet04</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

    <servlet>
    <servlet-name>servlet05</servlet-name>
    <servlet-class>com.monian.study.servlet.Servlet05</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>servlet05</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

相应各个servlet的代码,代码很简单,调用哪一个servlet就输出哪个servlet的名称:

servlet代码

public class Servlet01 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Servlet01");
  }
}

public class Servlet02 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Servlet02");
  }
}

public class Servlet03 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Servlet03");
  }
}

public class Servlet04 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Servlet04");
  }
}

public class Servlet05 extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.getWriter().write("Servlet05");
  }

}

源码

org.apache.catalina.mapper.Mapper#internalMapWrapper

// &#x5728;&#x672C;&#x4F8B;&#x5B50;&#x4E2D; path = '/zxq/test/servlet/get',&#x7528;offset&#x548C;end&#x6765;&#x63A7;&#x5236;&#x8DEF;&#x5F84;&#x90E8;&#x5206;&#x957F;&#x5EA6;
// contextPath = '/zxq'
private final void internalMapWrapper(ContextVersion contextVersion,
    CharChunk path,
    MappingData mappingData) throws IOException {

  int pathOffset = path.getOffset();
  int pathEnd = path.getEnd();
  boolean noServletPath = false;

  // contextVersion.path = '/zxq'
  int length = contextVersion.path.length();
  if (length == (pathEnd - pathOffset)) {
    noServletPath = true;
  }
  int servletPath = pathOffset + length;
  // path = '/text/servlet/get'
  path.setOffset(servletPath);

  // &#x89C4;&#x5219;1&#xFF1A;&#x5148;&#x5F00;&#x59CB;&#x7CBE;&#x786E;&#x5339;&#x914D;
  MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
  internalMapExactWrapper(exactWrappers, path, mappingData);

  // &#x89C4;&#x5219;2&#xFF1A;&#x524D;&#x7F00;&#x5339;&#x914D;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;&#x8DEF;&#x5F84;&#x5339;&#x914D;
  boolean checkJspWelcomeFiles = false;
  MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
  if (mappingData.wrapper == null) {
    internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
        path, mappingData);
    if (mappingData.wrapper != null && mappingData.jspWildCard) {
      char[] buf = path.getBuffer();
      if (buf[pathEnd - 1] == '/') {
        /*
         * Path ending in '/' was mapped to JSP servlet based on
         * wildcard match (e.g., as specified in url-pattern of a
         * jsp-property-group.

         * Force the context's welcome files, which are interpreted
         * as JSP files (since they match the url-pattern), to be
         * considered. See Bugzilla 27664.

         */
        mappingData.wrapper = null;
        checkJspWelcomeFiles = true;
      } else {
        // See Bugzilla 27704
        mappingData.wrapperPath.setChars(buf, path.getStart(),
            path.getLength());
        mappingData.pathInfo.recycle();
      }
    }
  }

  if(mappingData.wrapper == null && noServletPath &&
      contextVersion.object.getMapperContextRootRedirectEnabled()) {
    // The path is empty, redirect to "/"
    path.append('/');
    pathEnd = path.getEnd();
    mappingData.redirectPath.setChars
        (path.getBuffer(), pathOffset, pathEnd - pathOffset);
    path.setEnd(pathEnd - 1);
    return;
  }

  // Rule 3 -- Extension Match
  MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
  if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
    internalMapExtensionWrapper(extensionWrappers, path, mappingData,
        true);
  }

  // Rule 4 -- Welcome resources processing for servlets
  if (mappingData.wrapper == null) {
    boolean checkWelcomeFiles = checkJspWelcomeFiles;
    if (!checkWelcomeFiles) {
      char[] buf = path.getBuffer();
      checkWelcomeFiles = (buf[pathEnd - 1] == '/');
    }
    if (checkWelcomeFiles) {
      for (int i = 0; (i < contextVersion.welcomeResources.length)
          && (mappingData.wrapper == null); i++) {
        path.setOffset(pathOffset);
        path.setEnd(pathEnd);
        path.append(contextVersion.welcomeResources[i], 0,
            contextVersion.welcomeResources[i].length());
        path.setOffset(servletPath);

        // Rule 4a -- Welcome resources processing for exact macth
        internalMapExactWrapper(exactWrappers, path, mappingData);

        // Rule 4b -- Welcome resources processing for prefix match
        if (mappingData.wrapper == null) {
          internalMapWildcardWrapper
              (wildcardWrappers, contextVersion.nesting,
                  path, mappingData);
        }

        // Rule 4c -- Welcome resources processing
        //            for physical folder
        if (mappingData.wrapper == null
            && contextVersion.resources != null) {
          String pathStr = path.toString();
          WebResource file =
              contextVersion.resources.getResource(pathStr);
          if (file != null && file.isFile()) {
            internalMapExtensionWrapper(extensionWrappers, path,
                mappingData, true);
            if (mappingData.wrapper == null
                && contextVersion.defaultWrapper != null) {
              mappingData.wrapper =
                  contextVersion.defaultWrapper.object;
              mappingData.requestPath.setChars
                  (path.getBuffer(), path.getStart(),
                      path.getLength());
              mappingData.wrapperPath.setChars
                  (path.getBuffer(), path.getStart(),
                      path.getLength());
              mappingData.requestPath.setString(pathStr);
              mappingData.wrapperPath.setString(pathStr);
            }
          }
        }
      }

      path.setOffset(servletPath);
      path.setEnd(pathEnd);
    }

  }

  /* welcome file processing - take 2
   * Now that we have looked for welcome files with a physical
   * backing, now look for an extension mapping listed
   * but may not have a physical backing to it. This is for
   * the case of index.jsf, index.do, etc.

   * A watered down version of rule 4
   */
  if (mappingData.wrapper == null) {
    boolean checkWelcomeFiles = checkJspWelcomeFiles;
    if (!checkWelcomeFiles) {
      char[] buf = path.getBuffer();
      checkWelcomeFiles = (buf[pathEnd - 1] == '/');
    }
    if (checkWelcomeFiles) {
      for (int i = 0; (i < contextVersion.welcomeResources.length)
          && (mappingData.wrapper == null); i++) {
        path.setOffset(pathOffset);
        path.setEnd(pathEnd);
        path.append(contextVersion.welcomeResources[i], 0,
            contextVersion.welcomeResources[i].length());
        path.setOffset(servletPath);
        internalMapExtensionWrapper(extensionWrappers, path,
            mappingData, false);
      }

      path.setOffset(servletPath);
      path.setEnd(pathEnd);
    }
  }

  // Rule 7 -- Default servlet
  if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
    if (contextVersion.defaultWrapper != null) {
      mappingData.wrapper = contextVersion.defaultWrapper.object;
      mappingData.requestPath.setChars
          (path.getBuffer(), path.getStart(), path.getLength());
      mappingData.wrapperPath.setChars
          (path.getBuffer(), path.getStart(), path.getLength());
      mappingData.matchType = MappingMatch.DEFAULT;
    }
    // Redirection to a folder
    char[] buf = path.getBuffer();
    if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
      String pathStr = path.toString();
      // Note: Check redirect first to save unnecessary getResource()
      //       call. See BZ 62968.

      if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
        WebResource file;
        // Handle context root
        if (pathStr.length() == 0) {
          file = contextVersion.resources.getResource("/");
        } else {
          file = contextVersion.resources.getResource(pathStr);
        }
        if (file != null && file.isDirectory()) {
          // Note: this mutates the path: do not do any processing
          // after this (since we set the redirectPath, there
          // shouldn't be any)
          path.setOffset(pathOffset);
          path.append('/');
          mappingData.redirectPath.setChars
              (path.getBuffer(), path.getStart(), path.getLength());
        } else {
          mappingData.requestPath.setString(pathStr);
          mappingData.wrapperPath.setString(pathStr);
        }
      } else {
        mappingData.requestPath.setString(pathStr);
        mappingData.wrapperPath.setString(pathStr);
      }
    }
  }

  path.setOffset(pathOffset);
  path.setEnd(pathEnd);
}

匹配路径代码

org.apache.catalina.mapper.Mapper#find(org.apache.catalina.mapper.Mapper.MapElement[], org.apache.tomcat.util.buf.CharChunk, int, int)

// &#x4ECE;map&#x627E;&#x5230;&#x4E00;&#x4E2A;&#x6700;&#x4E0E;&#x8DEF;&#x5F84;&#x5339;&#x914D;&#x7684;
private static final <t> int find(MapElement<t>[] map, CharChunk name,
    int start, int end) {

  int a = 0;
  int b = map.length - 1;

  // Special cases: -1 and 0
  if (b == -1) {
    return -1;
  }

  // -1&#x8868;&#x793A;&#x5B8C;&#x5168;&#x4E0D;&#x5339;&#x914D;&#xFF0C;&#x76F4;&#x63A5;&#x8FD4;&#x56DE;
  if (compare(name, start, end, map[0].name) < 0 ) {
    return -1;
  }
  // &#x5B8C;&#x5168;&#x5339;&#x914D;&#x6216;&#x90E8;&#x5206;&#x5339;&#x914D;&#xFF0C;&#x4E14;&#x53EA;&#x6709;&#x4E00;&#x4E2A;&#x5F85;&#x5339;&#x914D;&#x7684;servlet&#x76F4;&#x63A5;&#x8FD4;&#x56DE;
  if (b == 0) {
    return 0;
  }

  // &#x7C7B;&#x4F3C;&#x4E8E;&#x4E8C;&#x5206;&#x67E5;&#x627E;&#xFF0C;&#x627E;&#x5230;&#x4E00;&#x4E2A;&#x6700;&#x957F;&#x8DEF;&#x5F84;&#x5339;&#x914D;
  int i = 0;
  while (true) {
    i = (b + a) >>> 1;
    int result = compare(name, start, end, map[i].name);
    if (result == 1) {
      a = i;
    } else if (result == 0) {
      return i;
    } else {
      b = i;
    }
    if ((b - a) == 1) {
      int result2 = compare(name, start, end, map[b].name);
      if (result2 < 0) {
        return a;
      } else {
        return b;
      }
    }
  }

}

private static final int compare(CharChunk name, int start, int end,
    String compareTo) {
  int result = 0;
  char[] c = name.getBuffer();
  int len = compareTo.length();
  if ((end - start) < len) {
    len = end - start;
  }
  // &#x6BD4;&#x8F83;url-pattern&#x4E0E; &#x8BF7;&#x6C42;&#x8DEF;&#x5F84;path&#xFF0C;&#x82E5;&#x6709;&#x4E00;&#x4E2A;&#x5B57;&#x7B26;&#x4E0D;&#x76F8;&#x7B49;&#x9000;&#x51FA;&#x5FAA;&#x73AF;
  for (int i = 0; (i < len) && (result == 0); i++) {
    if (c[i + start] > compareTo.charAt(i)) {
      result = 1;
    } else if (c[i + start] < compareTo.charAt(i)) {
      result = -1;
    }
  }

  // &#x90FD;&#x76F8;&#x7B49;&#x7684;&#x8BDD;&#x518D;&#x6BD4;&#x8F83;&#x957F;&#x5EA6;&#xFF0C;&#x8BF7;&#x6C42;&#x8DEF;&#x5F84;&#x957F;&#x5EA6;&#x6BD4;&#x5F85;&#x5339;&#x914D;&#x90E8;&#x5206;&#x957F;
  if (result == 0) {
    if (compareTo.length() > (end - start)) {
      result = -1;
    } else if (compareTo.length() < (end - start)) {
      result = 1;
    }
  }
  // result=0&#x4EE3;&#x8868;&#x5B8C;&#x5168;&#x5339;&#x914D;&#xFF0C; result=-1&#x4EE3;&#x8868;&#x4E0D;&#x5339;&#x914D;&#xFF0C;result=1&#x4EE3;&#x8868;&#x5F00;&#x5934;&#x90E8;&#x5206;&#x5339;&#x914D;
  return result;
}</t></t>

针对上述的匹配举个例子,假设有两个servlet都是通配符匹配的,url-pattern为 /test/one/ 和/test/ ,tomcat解析的时候会去掉通配符再排序[‘/test’, ‘test/one’],之后再去匹配数据中的元素也就是map[i].name,匹配路径 ‘/test/one/two’会返回url-parttern=/test/one/* 的这个servlet,这就是最长路径匹配

精确匹配

可以看到符合精确匹配的只有servlet01,且name就是它配置的url-pattern值,然后与requestPath进行匹配

private final void internalMapExactWrapper
    (MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
  // &#x627E;&#x5230;&#x4E00;&#x4E2A;&#x4E0E;path&#x7CBE;&#x786E;&#x5339;&#x914D;&#x7684;wrapper
  MappedWrapper wrapper = exactFind(wrappers, path);
  if (wrapper != null) {
    mappingData.requestPath.setString(wrapper.name);
    mappingData.wrapper = wrapper.object;
    if (path.equals("/")) {
      // Special handling for Context Root mapped servlet
      mappingData.pathInfo.setString("/");
      mappingData.wrapperPath.setString("");
      // This seems wrong but it is what the spec says...

      mappingData.contextPath.setString("");
      mappingData.matchType = MappingMatch.CONTEXT_ROOT;
    } else {
      mappingData.wrapperPath.setString(wrapper.name);
      mappingData.matchType = MappingMatch.EXACT;
    }
  }
}

private static final <t, e extends mapelement<t>> E exactFind(E[] map,
    CharChunk name) {
  // find&#x65B9;&#x6CD5;&#x4F1A;&#x8FD4;&#x56DE;&#x90E8;&#x5206;&#x5339;&#x914D;&#x6216;&#x5B8C;&#x5168;&#x5339;&#x914D;&#x7684;map
  int pos = find(map, name);
  if (pos >= 0) {
    E result = map[pos];
    // &#x5B8C;&#x5168;&#x5339;&#x914D;
    if (name.equals(result.name)) {
      return result;
    }
  }
  return null;
}</t,>

显而易见的开头那个request与servlet01的url-pattern是精确匹配的

servlet映射路径匹配解析

通配符匹配 (路径匹配)

接下来web.xml去掉servlet01的配置,只剩下4个servlet,从前面来看,精确匹配肯定是失败的因为现在去掉servlet01已经没有符合要求的servlet去精确匹配了,只能进行路径匹配了,而路径匹配符合要求的有两个servlet

/**
 * Wildcard mapping.

 */
private final void internalMapWildcardWrapper
(MappedWrapper[] wrappers, int nesting, CharChunk path,
    MappingData mappingData) {

  int pathEnd = path.getEnd();

  int lastSlash = -1;
  int length = -1;
  // &#x627E;&#x4E00;&#x4E2A;&#x6700;&#x5339;&#x914D;path&#x8DEF;&#x5F84;&#x7684;&#xFF0C;&#x6839;&#x636E;&#x4E0A;&#x9762;&#x7684;&#x5339;&#x914D;&#x4EE3;&#x7801;&#x53EF;&#x4EE5;&#x5F97;&#x5230;servlet02
  int pos = find(wrappers, path);
  if (pos != -1) {
    boolean found = false;
    while (pos >= 0) {
      if (path.startsWith(wrappers[pos].name)) {
        length = wrappers[pos].name.length();
        if (path.getLength() == length) {
          found = true;
          break;
        // path&#x4E0D;&#x4EE5;/&#x5F00;&#x5934;&#xFF0C;&#x5219;&#x91CD;&#x65B0;&#x627E;
        } else if (path.startsWithIgnoreCase("/", length)) {
          found = true;
          break;
        }
      }
      // &#x83B7;&#x53D6;path&#x6700;&#x540E;&#x4E00;&#x4E2A;/ &#x6240;&#x5728;&#x7684;&#x4F4D;&#x7F6E;
      if (lastSlash == -1) {
        lastSlash = nthSlash(path, nesting + 1);
      } else {
        lastSlash = lastSlash(path);
      }
      path.setEnd(lastSlash);
      pos = find(wrappers, path);
    }
    path.setEnd(pathEnd);
    if (found) {
      mappingData.wrapperPath.setString(wrappers[pos].name);
      if (path.getLength() > length) {
        mappingData.pathInfo.setChars
            (path.getBuffer(),
                path.getOffset() + length,
                path.getLength() - length);
      }
      mappingData.requestPath.setChars
          (path.getBuffer(), path.getOffset(), path.getLength());
      mappingData.wrapper = wrappers[pos].object;
      mappingData.jspWildCard = wrappers[pos].jspWildCard;
      mappingData.matchType = MappingMatch.PATH;
    }
  }
}

因此servlet02是匹配的,输出

若再web.xml去掉servlet02,那么匹配的就是servlet03了

另外我们可以从上面的代码得到若请求路径path = ‘/test/servlet/get’, 则 ‘/‘ 、 ‘/test/‘ 、 ‘/test/servlet/‘ 、 ‘/test/servlet/get/‘ 与之匹配,’/test/serv/*’ 这种不匹配

路径匹配是能匹配请求路径以 .jsp 、.html结尾的request的

servlet映射路径匹配解析

扩展名匹配(后缀匹配)

web.xml中注释servlet02和servlet03后,再次访问.jsp后缀结尾的请求就会直接报404了,可以看后续的匹配逻辑虽然能匹配到处理.jsp的servlet但我们并没有在相应路径下配置jsp文件,那么自然报404错误了

servlet映射路径匹配解析

下图可以看到后缀匹配的servlet有三个,一个我们自定义的后缀为do,另外两个jsp和jspx是tomcat内置的默认处理jsp的servlet

servlet映射路径匹配解析
/**
 * Extension mappings.

 *
 * @param wrappers          Set of wrappers to check for matches
 * @param path              Path to map
 * @param mappingData       Mapping data for result
 * @param resourceExpected  Is this mapping expecting to find a resource
 */
private final void internalMapExtensionWrapper(MappedWrapper[] wrappers,
    CharChunk path, MappingData mappingData, boolean resourceExpected) {
  char[] buf = path.getBuffer();
  int pathEnd = path.getEnd();
  int servletPath = path.getOffset();
  int slash = -1;
  for (int i = pathEnd - 1; i >= servletPath; i--) {
    if (buf[i] == '/') {
      slash = i;
      break;
    }
  }
  if (slash >= 0) {
    int period = -1;
    for (int i = pathEnd - 1; i > slash; i--) {
      if (buf[i] == '.') {
        period = i;
        break;
      }
    }
    if (period >= 0) {
      // &#x622A;&#x53D6;&#x5230;&#x540E;&#x7F00;&#x7684;&#x5B57;&#x7B26;&#x4F4D;&#x7F6E; &#x5339;&#x914D;
      path.setOffset(period + 1);
      path.setEnd(pathEnd);
      MappedWrapper wrapper = exactFind(wrappers, path);
      if (wrapper != null
          && (resourceExpected || !wrapper.resourceOnly)) {
        mappingData.wrapperPath.setChars(buf, servletPath, pathEnd
            - servletPath);
        mappingData.requestPath.setChars(buf, servletPath, pathEnd
            - servletPath);
        mappingData.wrapper = wrapper.object;
        mappingData.matchType = MappingMatch.EXTENSION;
      }
      path.setOffset(servletPath);
      path.setEnd(pathEnd);
    }
  }
}

servlet映射路径匹配解析

根据find的匹配逻辑可以匹配到我们自定义的servlet05,输出

servlet映射路径匹配解析

首页welcome资源匹配

若上述匹配都失败了则尝试寻找默认的资源文件,默认有三个,也可以自定义配置

假设请求路径为http://localhost:8082/zxq/ 以’/’结尾,那么会尝试将文件名加到path后面,以index.jsp为例,加完后路径为’/zxq/index.jsp’,之后会以此新路径再去尝试精确匹配、路径匹配、物理文件查找再进行扩展名匹配顺序查找,直到找到能处理此path的servlet

在webapp目录下加一个index.jsp文件之后能成功访问到,执行此请求的就是tomcat默认的jsp servlet

servlet映射路径匹配解析

默认匹配

‘/’就是默认匹配,当上述匹配都失败的时候,则启用这个servlet,也就是本文中的servlet04

servlet映射路径匹配解析

Original: https://www.cnblogs.com/monianxd/p/16573389.html
Author: 默念x
Title: servlet映射路径匹配解析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621472/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Python–序列化与反序列化

    序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态(存在内存中)写入到临时或持久性存储区(硬盘)。以后,可以通过从存储区中读取或反序列化对象的…

    数据库 2023年6月9日
    0106
  • 2022-8-16 mysql 第二天 约束

    重点,DQL是我们每天都要接触编写最多也是最难的SQL,该语言用来查询记录,不会修改数据库和表结构。 构建数据库 创建一张student表: DROP TABLE IF EXIST…

    数据库 2023年6月14日
    0112
  • 强大博客搭建全过程(1)-hexo博客搭建保姆级教程

    1、 前言 本人本来使用国内的开源项目solo搭建了博客,但感觉1核CPU2G内存的服务器,还是稍微有点重,包括服务器内还搭建了数据库。如果自己开发然后搭建,耗费时间又比较多,于是…

    数据库 2023年6月16日
    0115
  • (面试)大型网站应用之海量数据、高并发解决方案

    面试时会遇到这样的提问,就记录下来,加深印象;以后工作中也可以用到。 海量数据的解决方案: 网站访问数据的特点大多数呈现为”二八定律”:80%的业务访问集中…

    数据库 2023年6月11日
    054
  • MySQL事务ACID原理深度解析

    什么是MySQL事务? 事务是指对数据库的一组操作的集合,集合中的SQL语句要么全部执行成功,要么就全部失败,如果集合中任一操作出错,则此集合所有对数据库的操作全部回滚。 以常见的…

    数据库 2023年5月24日
    091
  • mysql 事务 隔离性 锁

    1、四大特性 1.1 原子性(Atomicity) 事务是一个不可分割的最小工作单元。事务是一个不可分割的最小工作单元。 [En] A transaction is an indi…

    数据库 2023年5月24日
    077
  • MySQL索引(一)

    一、索引概念 二、索引类型 (一)业务逻辑分类 1、NORMAL – 普通索引 2、UNIQUE – 唯一索引 3、PRIMARY KEY – …

    数据库 2023年6月16日
    097
  • 2_JDBC

    使用客户端工具访问数据库, 需要手工建立连接, 输入用户名和密码登陆, 编写SQL语句, 点击执行, 查看操作结果(结果集或受行数影响) 在实际开发中, 当用户的数据发生改变时, …

    数据库 2023年6月11日
    061
  • Java8日期时间(LocalDate、LocalTime、LocalDateTime)

    在看题的时候发现了Java8中新加入的日期时间类,觉得这个小哥写的不错,自己也跟着练习下。原文地址:https://blog.csdn.net/yy139926/article/d…

    数据库 2023年6月14日
    083
  • 0811JDBC随笔

    1.JDBC体系系统 一组规范:接口 JDBC接口(API)包括两个层次: 面向应用的API:Java API,抽象接口,供应用开发人员使用(连接数据库,执行SQL语句,获得结果)…

    数据库 2023年6月14日
    088
  • Xtrabackup 8.0.x关于MySQL的版本支持的浅析

    我们知道从MySQL 8.0.x开始,我们必须用Percona Xtrabackup 8.0.x来备份,之前的Percona XtraBackup 2.4已经不支持MySQL 8….

    数据库 2023年5月24日
    0150
  • LRU算法

    class LRUCahce { private Node head; private Node tail; private Map hashMap; private int ca…

    数据库 2023年6月14日
    078
  • PHP str_repeat()

    str_repeat str_repeat() 函数把字符串重复指定的次数。 示例: function strRepeat() { echo str_repeat("*&…

    数据库 2023年6月14日
    078
  • DM8数据库备份还原的原理及应用

    ▷▶介质管理层(MML) DM8根据自身实现自定义了一套API接口SBT来负责备份数据在存储介质上的实际读写操作。 整个存储介质上的读写操作,由MML调用SBT完成。 目前 DM8…

    数据库 2023年6月11日
    085
  • MySQL学习(4)—MySQL索引

    ps:没有特殊说明,此随笔中默认采用innoDB存储引擎中的索引,且索引都是指B+树(多路平衡搜索树)结构组织的索引。其中聚集索引、复合索引、前缀索引、唯一索引默认都是使用B+树,…

    数据库 2023年6月14日
    079
  • mapreduce统计单词个数

    WordCount类代码: import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Pat…

    数据库 2023年6月11日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球