小马哥JAVA实战营-JDBC

news2024/11/26 18:32:06

小马哥是一个非常牛逼的技术大牛,最近在看他的课,感兴趣也可以关注一波小马哥(不是引流,是真的很推荐):
小马哥B站

JDBC规范文档

jdbc规范文档下载链接

JDBC的主要特征

  • 面向数据表行列编程
  • 驱动程序需要数据库定制(MySQL、Oracle)
  • SQL 语法与目标数据库保持一致
  • 事务(需要数据库支持)
  • 数据库元信息 (数据库信息、表结构信息等)

JDBC 核心API

  • 数据源接口 -javaxsql.DataSource
    1 主流实现:
  1. Apache DBCP 1/2 DPCP文档 目前已经基本不使用了作为了解
    DBCP 的实现间接依赖-Apache Commons Pool(池化)里面有对象池。
    对象池的概念
    池化 资源少(线程资源,数据库资源,IO资源)比如线程池,数据库连接池 ;消费者多
    池化的特点是有借有还,其核心思想是生产者和消费者模型。
  2. C3P0
  3. Alibaba Druid(字节码提升/优化)
  4. HicariCP
    2 获取方式
    1 普通对象初始化
    Spring Bean 通过spirng来管理
    API 实现 通过api 来实例化
    2 JNDI 依赖查找 JNDI的一些资料
  • JDBC 驱动接口java.sql.Driver

  • 驱动动管理器接口 -java.sql.DriverManager(重点)
    用于管理driver,有三种获取Driver的方式

1 Class.forName 的时候,会在驱动管理器进行注册源码如下:

// classforName 的时候会加载这个类,类加载的时候静态方法执行就会注册驱动
package com.mysql.cj.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

2 同时DriverManager 加载的时候也会注册驱动,通过系统属性,是JVM共享的
读取系统属性jdbc.drivers后,再经过":" 分割尝试获取多值,在通过classLoader获取实现类,意思是驱动类用:分割开

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

可以看到加载了系统属性jdbc.drivers

drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
    public String run() {
        return System.getProperty("jdbc.drivers");
    }
});

可以看到最终还是通过类加载从而注册了驱动,和第一种方式的原理几乎一样

String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
    try {
        println("DriverManager.Initialize: loading " + aDriver);
        Class.forName(aDriver, true,
                ClassLoader.getSystemClassLoader());
    } catch (Exception ex) {
        println("DriverManager.Initialize: load failed: " + ex);
    }
}

3 还有一种获取方式是通过 java SPI 来获取Driver ServiceLoader通过这个类去加载,需要在静态资源目录下放入类的实现,然后加载。
在这里插入图片描述

在这里插入图片描述

那么是怎么拿到一个连接呢,方式是循环的区测试连接是否可用从而拿到一个连接,主要是通过connect方法, 源码如下:

private static Connection getConnection(
     String url, java.util.Properties info, Class<?> caller) throws SQLException {
     /*
      * When callerCl is null, we should check the application's
      * (which is invoking this class indirectly)
      * classloader, so that the JDBC driver class outside rt.jar
      * can be loaded from here.
      */
     ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
     synchronized(DriverManager.class) {
         // synchronize loading of the correct classloader.
         if (callerCL == null) {
             callerCL = Thread.currentThread().getContextClassLoader();
         }
     }
     if(url == null) {
         throw new SQLException("The url cannot be null", "08001");
     }
     println("DriverManager.getConnection(\"" + url + "\")");
     // Walk through the loaded registeredDrivers attempting to make a connection.
     // Remember the first exception that gets raised so we can reraise it.
     SQLException reason = null;

     for(DriverInfo aDriver : registeredDrivers) {
         // If the caller does not have permission to load the driver then
         // skip it.
         if(isDriverAllowed(aDriver.driver, callerCL)) {
             try {
                 println("    trying " + aDriver.driver.getClass().getName());
                 Connection con = aDriver.driver.connect(url, info);
                 if (con != null) {
                     // Success!
                     println("getConnection returning " + aDriver.driver.getClass().getName());
                     return (con);
                 }
             } catch (SQLException ex) {
                 if (reason == null) {
                     reason = ex;
                 }
             }
         } else {
             println("    skipping: " + aDriver.getClass().getName());
         }
     }
     // if we got here nobody could connect.
     if (reason != null)    {
         println("getConnection failed: " + reason);
         throw reason;
     }
     println("getConnection: no suitable driver found for "+ url);
     throw new SQLException("No suitable driver found for "+ url, "08001");
 }

课堂问题:
1 当多个driver同时被加载到classLoader后,到底用那一个?
我们可以看到上面源码里面是通过url来判断的,如果尝试连接成功,则返回成功的,也就是一直尝试到第一个成功的为止。
2 java SPI加载遍历的时候应该注意配置的时候要是配置错误就会导致错误后面的Driver无法加载?
是的,因为加载的时候next() 逻辑里面也是class.forName(),所以如果抛出异常下面的while语句不在执行,后面的驱动类就无法加载

AccessController.doPrivileged(new PrivilegedAction<Void>() {
          public Void run() {

              ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
              Iterator<Driver> driversIterator = loadedDrivers.iterator();

              /* Load these drivers, so that they can be instantiated.
               * It may be the case that the driver class may not be there
               * i.e. there may be a packaged driver with the service class
               * as implementation of java.sql.Driver but the actual class
               * may be missing. In that case a java.util.ServiceConfigurationError
               * will be thrown at runtime by the VM trying to locate
               * and load the service.
               *
               * Adding a try catch block to catch those runtime errors
               * if driver not available in classpath but it's
               * packaged as service and that service is there in classpath.
               */
              try{
              	// 这里如果存在异常,则while循环终止了
                  while(driversIterator.hasNext()) {
                      driversIterator.next();
                  }
              } catch(Throwable t) {
              // Do nothing
              }
              return null;
          }
      });

在这里插入图片描述
在这里插入图片描述
总结一下:驱动管理里面加载是同步进行的,如果系统属性jdbc.drviers 配置了或配置了spi都会进行加载,每个驱动具体的实现类在类加载的时候会向驱动管理注册驱动。

  • 数据连接接口 -java.sql.Connection
    连接可以创建Statement,Statement的主要类型
    1)普通SQL-java.sql.Statement
  1. 预编译SQL命令-java.sql.PrepareStatement
  2. 存储过程SQL命令-java.sql.CallableStatement
  • SQL 命令接口 java.sql.Statement
    DDL语句:crud 一般用executeUpdate()
    如果用execute方法
    如果成功的话不需要返回值(或者是返回false) 如果失败SQLException。也就是我们用这个方法的时候只需要关注是否抛异常,不抛出异常则是成功。
    在这里插入图片描述
    下面来借用derby 来演示一下executeUpdate() 和 execute() 的区别
public static final String DROP_USERS_TABLE_DDL_SQL = "DROP TABLE users";

public static final String CREATE_USERS_TABLE_DDL_SQL = "CREATE TABLE users(" +
     "id INT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), " +
     "name VARCHAR(16) NOT NULL, " +
     "password VARCHAR(64) NOT NULL, " +
     "email VARCHAR(64) NOT NULL, " +
     "phoneNumber VARCHAR(64) NOT NULL" +
     ")";

public static final String INSERT_USER_DML_SQL = "INSERT INTO users(name,password,email,phoneNumber) VALUES " +
     "('A','******','a@gmail.com','1') , " +
     "('B','******','b@gmail.com','2') , " +
     "('C','******','c@gmail.com','3') , " +
     "('D','******','d@gmail.com','4') , " +
     "('E','******','e@gmail.com','5')";
// /db/user-platform这是一个目录,运行后java会在当前程序根目录下创建derby 内存数据库
String databaseURL = "jdbc:derby:/db/user-platform;create=true";
Connection connection = DriverManager.getConnection(databaseURL);
Statement statement = connection.createStatement();
// 删除 users 表 运行结果为false
System.out.println(statement.execute(DROP_USERS_TABLE_DDL_SQL));
// 创建 users 表 运行结果为false
System.out.println(statement.execute(CREATE_USERS_TABLE_DDL_SQL));
// 运行结果为5
System.out.println(statement.executeUpdate(INSERT_USER_DML_SQL)); 

在这里插入图片描述
关于Derby数据库:添加链接描述
ORM 的核心思想(反射),代码demo

static Map<Class, String> typeMethodMappings = new HashMap<Class, String>() {{
     put(Long.class, "getLong");
     put(String.class, "getString");
 }};
 public static void main(String[] args) throws Exception {
     String databaseURL = "jdbc:derby:/db/user-platform;create=true";
     Connection connection = DriverManager.getConnection(databaseURL);
     Statement statement = connection.createStatement();
     // 执行查询语句(DML)
     ResultSet resultSet = statement.executeQuery("SELECT id,name,password,email,phoneNumber FROM users");
     // BeanInfo user这个类的属性及类型 userBeanInfo.getPropertyDescriptors() 可以通过这个方法拿到所有的属性 第二个参数是stop class 到那个父类停止 如果有继承关系会获取所有的属性包含父类的
     BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class, Object.class);
     List<User> users = new ArrayList<>();
     // 如果存在并且游标滚动
     while (resultSet.next()) {
         User user = new User();
         for (PropertyDescriptor propertyDescriptor : userBeanInfo.getPropertyDescriptors()) {
             String fieldName = propertyDescriptor.getName();
             Class<?> type = propertyDescriptor.getPropertyType();
          	 // 如果存在 列名的映射也可以在这里写一个map来映射
             String methodName = typeMethodMappings.get(type);
             // long getLong(String columnLabel) throws SQLException; 里面只有一个参数且为String 所以第二个参数填String.class
             Method resultMethod = resultSet.getClass().getMethod(methodName, String.class);
             Object resultVal = resultMethod.invoke(resultSet, fieldName);
             // propertyDescriptor.getWriteMethod() 就是set方法
             // propertyDescriptor.getReadMethod() 就是read方法
             Method writeMethod = propertyDescriptor.getWriteMethod();
             writeMethod.invoke(user, resultVal);
         }
         users.add(user);
         }
    connection.close();
     }
    ```
- SQL 执行结果接口 -java.sql.ResultSetResultSet 


- 表元数据接口 -java.sql.ResultSetMetaData
这个接口的应用,比如逆向工程,可以生成一些sql 和 类,下面看一个小demo看如何生成sql的,如何生成类等研究源码后来补充
```java
String databaseURL = "jdbc:derby:/db/user-platform;create=true";
Connection connection = DriverManager.getConnection(databaseURL);
Statement statement = connection.createStatement();
// 执行查询语句(DML)
ResultSet resultSet = statement.executeQuery("SELECT id,name,password,email,phoneNumber FROM users");
// ResultSetMetaData 元信息
ResultSetMetaData metaData = resultSet.getMetaData();
// 获取当前的表名称 它的参数是column 填任何列的数量都是返回表名称
System.out.println("当前表的名称:" + metaData.getTableName(1));
System.out.println("当前表的列个数:" + metaData.getColumnCount());
for (int i = 1; i <= metaData.getColumnCount(); i++) {
    System.out.println("列名称:" + metaData.getColumnLabel(i) + ", 类型:" + metaData.getColumnClassName(i));
}
// 反向生成生成sql
StringBuilder builder = new StringBuilder("SELECT ");
for (int i = 1; i <= metaData.getColumnCount(); i++) {
   builder.append(metaData.getColumnLabel(i)).append(",");
}
// 去掉最后一个逗号
builder.deleteCharAt(builder.length() - 1);
builder.append(" FROM ").append(metaData.getTableName(1));
System.out.println(builder.toString());
connection.close();

在这里插入图片描述

  • SQL 执行异常 java.sql.SQLException
    基本特点:
    1)几乎所有的JDBC API操作都需要try catch java.sql.SQLException
    2) java.sql.SQLException 属于检查类型异常,继承Exception
    下面代码是直接复制的小马哥的代码,着重理解getAll() 方法,这里演示了如果自己手写JDBCTemplate,需要怎样实现并处理异常,对于我这个小菜来说收货颇多。
package org.geektimes.projects.user.repository;
import org.geektimes.context.ClassicComponentContext;
import org.geektimes.function.ThrowableFunction;
import org.geektimes.projects.user.domain.User;
import org.geektimes.projects.user.sql.DBConnectionManager;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.apache.commons.lang.ClassUtils.wrapperToPrimitive;
public class DatabaseUserRepository implements UserRepository {
  private static Logger logger = Logger.getLogger(DatabaseUserRepository.class.getName());
  /**
   * 通用处理方式,处理异常
   */
  private static Consumer<Throwable> COMMON_EXCEPTION_HANDLER = e -> logger.log(Level.SEVERE, e.getMessage());
  public static final String INSERT_USER_DML_SQL =
          "INSERT INTO users(name,password,email,phoneNumber) VALUES " +
                  "(?,?,?,?)";
  public static final String QUERY_ALL_USERS_DML_SQL = "SELECT id,name,password,email,phoneNumber FROM users";
  private final DBConnectionManager dbConnectionManager;
  public DatabaseUserRepository() {
      this.dbConnectionManager = ClassicComponentContext.getInstance().getComponent("bean/DBConnectionManager");
  }
  private Connection getConnection() {
      return dbConnectionManager.getConnection();
  }
  @Override
  public boolean save(User user) {
      return false;
  }
  @Override
  public boolean deleteById(Long userId) {
      return false;
  }
  @Override
  public boolean update(User user) {
      return false;
  }
  @Override
  public User getById(Long userId) {
      return null;
  }
  @Override
  public User getByNameAndPassword(String userName, String password) {
      return executeQuery("SELECT id,name,password,email,phoneNumber FROM users WHERE name=? and password=?",
              resultSet -> {
                  // TODO
                  return new User();
              }, COMMON_EXCEPTION_HANDLER, userName, password);
  }
  @Override
  public Collection<User> getAll() {
      return executeQuery("SELECT id,name,password,email,phoneNumber FROM users", resultSet -> {
          // BeanInfo -> IntrospectionException
          BeanInfo userBeanInfo = Introspector.getBeanInfo(User.class, Object.class);
          List<User> users = new ArrayList<>();
          while (resultSet.next()) { // 如果存在并且游标滚动 // SQLException
              User user = new User();
              for (PropertyDescriptor propertyDescriptor : userBeanInfo.getPropertyDescriptors()) {
                  String fieldName = propertyDescriptor.getName();
                  Class fieldType = propertyDescriptor.getPropertyType();
                  String methodName = resultSetMethodMappings.get(fieldType);
                  // 可能存在映射关系(不过此处是相等的)
                  String columnLabel = mapColumnLabel(fieldName);
                  Method resultSetMethod = ResultSet.class.getMethod(methodName, String.class);
                  // 通过放射调用 getXXX(String) 方法
                  Object resultValue = resultSetMethod.invoke(resultSet, columnLabel);
                  // 获取 User 类 Setter方法
                  // PropertyDescriptor ReadMethod 等于 Getter 方法
                  // PropertyDescriptor WriteMethod 等于 Setter 方法
                  Method setterMethodFromUser = propertyDescriptor.getWriteMethod();
                  // 以 id 为例,  user.setId(resultSet.getLong("id"));
                  setterMethodFromUser.invoke(user, resultValue);
              }
          }
          return users;
      }, e -> {
          // 异常处理
      });
  }
  /**
   * @param sql
   * @param function
   * @param <T>
   * @return
   */
  protected <T> T executeQuery(String sql, ThrowableFunction<ResultSet, T> function,
                               Consumer<Throwable> exceptionHandler, Object... args) {
      Connection connection = getConnection();
      try {
          PreparedStatement preparedStatement = connection.prepareStatement(sql);
          for (int i = 0; i < args.length; i++) {
              Object arg = args[i];
              Class argType = arg.getClass();
              // 获取原生的类型 拆箱后的类型
              Class wrapperType = wrapperToPrimitive(argType);
              if (wrapperType == null) {
                  wrapperType = argType;
              }
              // 这里也是把set方法映射一下
              String methodName = preparedStatementMethodMappings.get(argType);
              Method method = PreparedStatement.class.getMethod(methodName, wrapperType);
              method.invoke(preparedStatement, i + 1, arg);
          }
          ResultSet resultSet = preparedStatement.executeQuery();
          return function.apply(resultSet);
      } catch (Throwable e) {
          // 异常处理
          exceptionHandler.accept(e);
      }
      return null;
  }
  private static String mapColumnLabel(String fieldName) {
      return fieldName;
  }
  /**
   * 数据类型与 ResultSet 方法名映射
   */
  static Map<Class, String> resultSetMethodMappings = new HashMap<>();

  static Map<Class, String> preparedStatementMethodMappings = new HashMap<>();
  static {
      resultSetMethodMappings.put(Long.class, "getLong");
      resultSetMethodMappings.put(String.class, "getString");
      // long
      preparedStatementMethodMappings.put(Long.class, "setLong");
      // string
      preparedStatementMethodMappings.put(String.class, "setString");
  }
}

里面用到了小马哥设计的可以抛出异常的Function,小马哥dubbo 源码里面复制过来的代码如下:

@FunctionalInterface
public interface ThrowableFunction<T, R> {

   /**
    * Applies this function to the given argument.
    *
    * @param t the function argument
    * @return the function result
    * @throws Throwable if met with any error
    */
   R apply(T t) throws Throwable;

   /**
    * Executes {@link ThrowableFunction}
    *
    * @param t the function argument
    * @return the function result
    * @throws RuntimeException wrappers {@link Throwable}
    */
   default R execute(T t) throws RuntimeException {
       R result = null;
       try {
           result = apply(t);
       } catch (Throwable e) {
           throw new RuntimeException(e.getCause());
       }
       return result;
   }
   /**
    * Executes {@link ThrowableFunction}
    *
    * @param t        the function argument
    * @param function {@link ThrowableFunction}
    * @param <T>      the source type
    * @param <R>      the return type
    * @return the result after execution
    */
   static <T, R> R execute(T t, ThrowableFunction<T, R> function) {
       return function.execute(t);
   }
}
  • 事务保护点接口 -java.sgl.Savepoint
    如果我们记录了保护点可以从那个点进行回滚
    下面这两个方法是关于事务的保存点和回滚的位于Connection接口里面
Savepoint setSavepoint(String name) throws SQLException;
void rollback(Savepoint savepoint) throws SQLException;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《3.linux应用编程和网络编程-第8部分-3.8.网络基础》 3.8.1.网络通信概述 3.8.3.网络通信基础知识2

进程间通信&#xff1a; 管道 、 信号量、 共享内存&#xff0c; 技术多&#xff0c;操作麻烦 线程就是解决 进程间 通信 麻烦的事情&#xff0c;这是线程的 优势 3.8.1.网络通信概述 3.8.1.1、从进程间通信说起&#xff1a;网络域套接字socket&#xff0c;网络通信其实就是位…

2023.7月最新版idea安装Jrebel实现热部署,可解决后端启动等待时间过长问题

2023.7最新版idea热部署配置 一 下载jrebel插件二 激活我使用的方法 三 配置方式1 设置自动编译2 设置 compiler.automake.allow.when.app.running3 勾选项目&#xff0c;然后以Rebel方式启动 4 Settings查看Activation情况四 报错解决1 启动失败 2 端口被占用 五 总结 一 下载…

JS 的 new 到底是干什么的?

大部分讲 new 的文章会从面向对象的思路讲起&#xff0c;但是我始终认为&#xff0c;在解释一个事物的时候&#xff0c;不应该引入另一个更复杂的事物。 今天我从「省代码」的角度来讲 new。 --------------------------- 想象我们在制作一个策略类战争游戏&#xff0c;玩家…

网络编程【网络编程基本概念、 网络通信协议、IP地址 、 TCP协议和UDP协议】(一)-全面详解(学习总结---从入门到深化)

目录 网络编程基本概念 网络通信协议 IP地址 TCP协议和UDP协议 网络编程基本概念 计算机网络 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其 外部设备&#xff0c;通过通信线路连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软 件及网络通信协议的…

echarts图表进度条类型图

1、实现效果 左边是类别、数量&#xff0c;中间类似于进度条&#xff0c;右边是所占百分比 2、实现思路 x轴不显示&#xff0c;y轴的axisLabel用富文本&#xff0c;显示机器与台数&#xff1b;图表有两个数据组&#xff0c;分别用蓝色和灰色表示&#xff0c;两个柱子重合&…

Jmeter beanshell编程实例

目录 1、引言 2、需求 3、BeanShell实现 3.1、原始单元测试的java代码&#xff1a; 3.2、调用的RSAUtil原始方法&#xff1a; 3.3、使用BeanShell预处理器实现报文加密&#xff1a; 库导入部分&#xff1a; JSON报文组装&#xff1a; RSA加密&#xff1a; 3.4、取样器…

【C语言】指针进化:传参与函数(2)

莫道君行早&#xff0c;更有早行人。— 出自《增广贤文上集》 解释&#xff1a;别说你出发的早&#xff0c;还有比你更早的人。 这篇博客我们将会深入的理解数组传参和函数指针等指针&#xff0c;是非常重要的内容&#xff0c;学好这部分才能算真正学懂C语言。 目录 一维数组传…

【网络】socket——预备知识 | 套接字 | UDP网络通信

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 在前面本喵对网络的整体轮廓做了一个大概的介绍&#xff0c;比如分层&#xff0c;协议等等内容&#x…

UFS 14 - UFS RPMB安全读写命令

UFS 14 - UFS RPMB安全读写命令 1 SECURITY PROTOCOL IN/OUT Commands1.1 CDB format of SECURITY PROTOCOL IN/OUT commands1.2 Supported security protocols list description1.3 Certificate data description 2 CDB format of SECURITY PROTOCOL IN/OUT commands demo2.1 …

Spring Boot原理分析(一):项目启动流程、自动装配

文章目录 一、项目启动流程二、SpringBootApplication.java源码解析1.准备工作2.源码3.自定义注解4.组合注解5.注解ComponentScan过滤器 6.注解SpringBootConfigurationConfiguration 7.注解EnableAutoConfiguration&#xff08;1&#xff09;Spring手动装配使用XML配置文件使用…

Nerf论文阅读笔记Neuralangelo: High-Fidelity Neural Surface Reconstruction

Neuralangelo&#xff1a;高保真神经表面重建 公众号&#xff1a;AI知识物语&#xff1b;B站暂定&#xff1b;知乎同名 视频入门介绍可以参考 B站——CVPR 2023最新工作&#xff01;Neuralangelo&#xff1a;高保真Nerf表面重建 https://www.bilibili.com/video/BV1Ju411W7…

杨氏矩阵,字符串左旋,字符串旋转结果题目解析

杨氏矩阵 题目要求:有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 示例 分析:我们仔细分析&#xff0c;不难发现&#xff0c;对于杨氏矩阵老说&#xff0c;右上角和左下…

leetcode1020. 飞地的数量

https://leetcode.cn/classic/problems/number-of-enclaves/description/ 给你一个大小为 m x n 的二进制矩阵 grid &#xff0c;其中 0 表示一个海洋单元格、1 表示一个陆地单元格。 一次 移动 是指从一个陆地单元格走到另一个相邻&#xff08;上、下、左、右&#xff09;的…

哈希的应用->位图

ps&#xff1a;左移位并不是向左移动位&#xff0c;而是低数据位向高数据位挪动 位图&#xff08;主要接口&#xff0c;set(size_t)标识、reset(size_t)取消、test(size_t) 查看 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一…

做软件测试到底要不要学编程?

乔布斯曾经说过“每个人都应该学习编程&#xff0c;因为它会教你如何思考”&#xff0c;看&#xff0c;乔帮主都觉得所有人都应该学编程&#xff0c;那你说做测试的要不要学&#xff1f;当然要。 作为测试人员&#xff0c;除了上面这个原因&#xff0c;我觉得如果会编程&#x…

Android架构之MVC,MVP,MVVM解析

MVC架构 View&#xff1a;Acitivity(View)、Fragment(View)视图&#xff0c;在android里xml布局转成View后&#xff0c;加载到了Activity/Fragment里了。 Controller&#xff1a;Controller对应着Activity/Fragment&#xff0c;绑定UI&#xff0c;处理各种业务。 Model&#xf…

python接口自动化(三十)--html测试报告通过邮件发出去——中(详解)

简介 上一篇&#xff0c;我们虽然已经将生成的最新的测试报告发出去了&#xff0c;但是MIMEText 只能发送正文&#xff0c;无法带附件&#xff0c;因此我还需要继续改造我们的代码&#xff0c;实现可以发送带有附件的邮件。发送带附件的需要导入另外一个模块 MIMEMultipart。还…

java版电子招标采购系统源码之电子招标采购实践与展望-招标采购管理系统

统一供应商门户 便捷动态、呈现丰富 供应商门户具备内外协同的能力&#xff0c;为外部供应商集中推送展示与其相关的所有采购业务信息&#xff08;历史合作、考察整改&#xff0c;绩效评价等&#xff09;&#xff0c;支持供应商信息的自助维护&#xff0c;实时风险自动提示。…

springboot+MySQL实现4S店车辆管理系统

本系统为了数据库结构的灵活性所以打算采用MySQL来设计数据库&#xff0c;而java技术&#xff0c;B/S架构则保证了较高的平台适应性。本文主要介绍了本系统的开发背景&#xff0c;所要完成的功能和开发的过程&#xff0c;主要说明了系统设计的重点、设计思想。

计算机体系结构基础知识介绍之使用多问题和静态调度来利用 流水线

为了提高处理器的性能&#xff0c;我们需要让每个时钟周期内发出多条指令&#xff0c;而不是只发出一条。这种多发射处理器有三种主要类型&#xff1a; 1. 静态调度的超标量处理器 2. VLIW&#xff08;非常长指令字&#xff09;处理器 3. 动态调度的超标量处理器。 这三种类型的…