Mybatis---从入门到深化

news2025/1/16 3:40:27

一、什么是框架?

框架即一个半成品软件。开发者从头开发一个软件需要花费大量精力,于是有一些项目组开发出半成品软件,开发者在这些软件的基础上进行开发,这样的软件就称之为框架。

如果将开发完成的软件比作是一套已经装修完毕的新房,框架就好比是一套已经修建好的毛坯房。用户直接购买毛坯房,保证建筑质量和户型合理的同时可以进行风格的自由装修。

使用框架开发的好处:

  1. 省去大量的代码编写、减少开发时间、降低开发难度。
  2. 限制程序员必须使用框架规范开发,增强代码的规范性,降低程序员之间沟通及日后维护的成本。
  3. 将程序员的注意力从技术中抽离出来,更集中在业务层面。

使用框架就好比和世界上最优秀的软件工程师共同完成一个项目,并且他们完成的还是基础、全局的工作。

二、什么是ORM框架?

ORM(Object Relationl Mapping),对象关系映射,即在数据库和对象之间作映射处理。

之前我们使用JDBC操作数据库,必须手动进行数据库和对象间的数据转换。

// 新增方法,将对象转为sql语句字段
public void AddUser(User user) throws Exception {
  Class.forName("com.mysql.jdbc.Driver");
  Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","root", "root");
  String sql = "INSERT INTO user values (null,?,?,?,?)";

  PreparedStatement preparedStatement = connection.prepareStatement(sql);
  preparedStatement.setString(1,user.getName());
  preparedStatement.setInt(2,user.getAge());
  preparedStatement.setString(3,user.getAddress());
  preparedStatement.setString(4,user.getSex());
  preparedStatement.executeUpdate();
  // 省略资源关闭...
}


// 查询方法,将数据库结果集转为对象
public List<User> findAllUser() throws Exception {
  Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
  PreparedStatement preparedStatement = connection.prepareStatement("select * from user");
  ResultSet resultSet = preparedStatement.executeQuery();

  //遍历查询结果集
  List<User> users = new ArrayList<>();
  while(resultSet.next()){
    // 拿到每一列数据
    int id = resultSet.getInt("id");
    String name = resultSet.getString("name");
    int age = resultSet.getInt("age");
    String address = resultSet.getString("address");
    String sex = resultSet.getString("sex");
    // 将数据封装到对象中
    User user = new User();
    user.setId(id);
    user.setName(name);
    user.setAge(age);
    user.setAddress(address);
    user.setSex(sex);
    users.add(user);
   }
  // 省略资源关闭...
  return users;
}

这段代码中,数据库数据与对象数据的转换代码繁琐、无技术含量。而使用ORM框架代替JDBC后,框架可以帮助程序员自动进行转换,只要像平时一样操作对象,ORM框架就会根据映射完成对数据库的操作,极大的增强了开发效率。

三、什么是MyBatis?

MyBatis是一个半自动的ORM框架,其本质是对JDBC的封装。使用MyBatis不需要写JDBC代码,但需要程序员编写SQL语句。之前是apache的一个开源项目iBatis,2010年改名为MyBatis。

补充:

Hibernate也是一款持久层ORM框架,多年前的市场占有率很高,但近年来市场占有率越来越低。

MyBatis与Hibernate的比较:

  • MyBatis是一个半自动的ORM框架,需要手写SQL语句。
  • Hibernate是一个全自动的ORM框架,不需要手写SQL语句。
  • 使用MyBatis的开发量要大于Hibernate。

为什么Hibernate市场占有率越来越低:

  • 对于新手学习Hibernate时间成本比MyBatis大很多,MyBatis上手很快。
  • Hibernate不需要写SQL语句是因为框架来生成SQL语句。对于复杂查询,开发者很难控制生成的SQL语句,这就导致SQL调优很难进行。
  • 之前的项目功能简单,数据量小,所以使用Hibernate可以快速完成开发。而近年来项目的数据量越来越大,而互联网项目对查询速度要求也很高,这就要求我们一定要精细化的调整SQL语句。此时灵活性更强,手动编写SQL语句的MyBatis慢慢代替了Hibernate使用。
  • 在高并发、大数据、高性能、高响应的互联网项目中,MyBatis是首选的持久框架。而对于对性能要求不高的比如内部管理系统等可以使用Hibernate。

四、MyBatis入门案例

环境搭建

  1. 将SQL文件导入数据库

  2. 创建maven工程,引入依赖

    <dependencies>
      <!--  mybatis  -->
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
      </dependency>
      <!--  mysql驱动包  -->
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
      </dependency>
    
      <!--  junit  -->
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
      </dependency>
      <!--  log4j  -->
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
      </dependency>
    </dependencies>
  3.  创建mybatis核心配置文件SqlMapConfig.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
      <!--  配置环境  -->
      <environments default="mysql">
        <environment id="mysql">
          <!--  事务类型  -->
          <transactionManager type="JDBC"></transactionManager>
          <!--  数据源  -->
          <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql:///mybatis"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
          </dataSource>
        </environment>
      </environments>
    </configuration>
  4. 将log4j.properties文件放入resources中,让控制台打印SQL语句。

  5. 创建实体类

    public class User {
      private int id;
      private String username;
      private String sex;
      private String address;
        // 省略getter/setter/构造方法/toString方法
    }
  6. 创建持久层接口和映射文件 

 6.1 在java目录创建持久层接口

public interface UserMapper {
  List<User> findAll();
}

6.2 在resource目录创建映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itbaizhan.mapper.UserMapper">
  <select id="findAll" resultType="com.itbaizhan.pojo.User">
     select * from user
  </select>
</mapper>

6.3 将映射文件配置到mybatis核心配置文件中

<!--  注册映射文件  -->
<mappers>
  <mapper resource="com/itbaizhan/mapper/UserMapper.xml">     </mapper>
</mappers>

映射文件注意事项:

  • 映射文件要和接口名称相同。

  • 映射文件要和接口的目录结构相同。

  • 映射文件中namespace属性要写接口的全名。

  • 映射文件中标签的id属性是接口方法的方法名。

  • 映射文件中标签的resultType属性是接口方法的返回值类型。

  • 映射文件中标签的parameterType属性是接口方法的参数类型。

  • 映射文件中resultType、parameterType属性要写全类名,如果是集合类型,则写其泛型的全类名。

  7. 测试持久层接口方法 

@Test
public void testFindAll() throws Exception {
  // (1)读取核心配置文件
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  // (2)创建SqlSessionFactoryBuilder对象
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
  SqlSessionFactory factory = builder.build(is);
  // (4)SqlSessionFactory对象获取SqlSession对象
  SqlSession session = factory.openSession();
  // (5)SqlSession对象获取代理对象
  UserMapper userMapper = session.getMapper(UserMapper.class);
  // (6)代理对象执行方法
  List<User> all = userMapper.findAll();
  all.forEach(System.out::println);

  // (7)释放资源
  session.close();
  is.close();
}

 五、MyBatis核心对象及工作流程

MyBatis核心对象

  • SqlSessionFactoryBuilder

    SqlSession工厂构建者对象,使用构造者模式创建SqlSession工厂对象。

  • SqlSessionFactory

    SqlSession工厂,使用工厂模式创建SqlSession对象。

  • SqlSession

    该对象可以操作数据库,也可以使用动态代理模式创建持久层接口的代理对象操作数据库。

  • Mapper

    持久层接口的代理对象,他具体实现了持久层接口,用来操作数据库。

MyBatis工作流程

  1. 创建SqlSessionFactoryBuilder对象

  2. SqlSessionFactoryBuilder对象构建了SqlSessionFactory对象:构造者模式

  3. SqlSessionFactory对象生产了SqlSession对象:工厂模式

  4. SqlSession对象创建了持久层接口的代理对象:动态代理模式

  5. 代理对象操作数据库

六、使用SqlSession操作数据库 

除了代理对象能够操作数据库,SqlSession也能操作数据库。只是这种方式在开发中使用的较少,接下来我们使用SqlSession操作数据库:

@Test
public void testFindAll2() throws Exception {
  // (1)读取核心配置文件
  InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
  // (2)创建SqlSessionFactoryBuilder对象
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
  SqlSessionFactory factory = builder.build(is);
  // (4)SqlSessionFactory对象获取SqlSession对象
  SqlSession session = factory.openSession();
  // (5)SqlSession直接操作数据库
  List<User> users = session.selectList("com.itbaizhan.mapper.UserMapper.findAll");
  users.forEach(System.out::println);
  // (6)关闭资源
  session.close();
  is.close();
}

 七、Mapper动态代理原理

接下来我们通过源码,了解MyBatis的Mapper对象究竟是怎么生成的,他又是如何代理接口的方法。

获取代理对象

点开测试类的getMapper方法,查看该方法最终调用了什么方法。

当看到Proxy.newProxyInstance时,可以确定getMapper方法最终调用的是JDK动态代理方法,且使用MapperProxy类定义代理方式

查看代理方式

点开MapperProxy类,查看invoke方法,查看代理对象是如何工作的。

 

可以看到,MapperProxy调用了MapperMethod的execute方法定义了代理方式,且底层调用的是SqlSession的方法,根据映射文件标签不同调用不同的SqlSession方法。

结论:

  • SqlSession的getMapper方法,最终是调用的是JDK动态代理方法,生成一个代理对象,类型就是传入的接口类型。
  • MapperProxy对象通过调用MapperMethod的execute方法定义了代理方式,该方法的底层调用的是SqlSession的方法。

八、MyBatis增删改查

 8.1 新增用户

8.1.1 持久层接口添加方法

void add(User user);

8.1.2 映射文件添加标签

<insert id="add" parameterType="com.itbaizhan.pojo.User">
   insert into user(username,sex,address) values(#{username},#{sex},#{address})
</insert>

8.1.3 编写测试方法

@Test
public void testAdd() throws Exception {
  InputStream is= Resources.getResourceAsStream("SqlMapConfig.xml");
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  SqlSessionFactory factory = builder.build(is);
  SqlSession session = factory.openSession();
  UserMapper userMapper = session.getMapper(UserMapper.class);
  User user = new User("程序员", "男", "上海");
  userMapper.add(user);
  // 提交事务
  session.commit();
  session.close();
  is.close();
}

注意:

  1. 当接口方法的参数类型为POJO类型时,SQL语句中绑定参数时使用#{POJO的属性名}即可。
  2. MyBatis事务默认手动提交,所以在执行完增删改方法后,需要手动调用SqlSession对象的事务提交方法,否则数据库将不发生改变。

8.2 修改用户

优化测试类

我们发现MyBatis的测试方法在操作数据库前都需要获取代理对象,操作数据库后都需要释放资源,可以利用Junit的前置后置方法,优化测试类代码。

InputStream is = null;
SqlSession session = null;
UserMapper userMapper = null;

@Before
public void before() throws IOException {
  // (1)读取核心配置文件
  is = Resources.getResourceAsStream("SqlMapConfig.xml");
  // (2)创建SqlSessionFactoryBuilder对象
  SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
  // (3)SqlSessionFactoryBuilder对象获取SqlSessionFactory对象
  SqlSessionFactory factory = builder.build(is);
  // (4)SqlSessionFactory对象获取SqlSession对象
  session = factory.openSession();
  // (5)获取代理对象
  userMapper = session.getMapper(UserMapper.class);
}

@After
public void after() throws IOException {
  // 释放资源
  session.close();
  is.close();
}

持久层接口添加方法

void update(User user);

 映射文件添加标签

<update id="update" parameterType="com.itbaizhan.pojo.User">
   update user
   set username = #{username},
   sex    = #{sex},
   address=#{address}
   where id = #{id}
</update>

 编写测试方法

@Test
public void testUpdate(){
  User user = new User(8,"程序员1","女","深圳");
  userMapper.update(user);
  session.commit();
}

 8.3 用户删除、根据Id查询

删除用户

持久层接口添加方法

void delete(int userId);

 映射文件添加标签

<delete id="delete" parameterType="int">
   delete from user where id = #{id}
</delete>

注:当方法的参数类型是简单数据类型时,#{}中可以写任意名称

  • 简单数据类型:基本数据类型、字符串等

编写测试方法 

@Test
public void testDelete(){
  userMapper.delete(8);
  session.commit();
}

 根据ID查询用户

持久层接口添加方法

User findById(int userId);

 映射文件添加标签

<select id="findById" parameterType="int" resultType="com.itbaizhan.pojo.User">
   select * from user where id = #{userId}
</select>

 编写测试方法

@Test
public void testFindById(){
  User user = userMapper.findById(1);
  System.out.println(user);
}

 8.4 模糊查询

8.4.1 使用#定义参数 

持久层接口添加方法

List<User> findByNameLike(String username);

 映射文件添加标签

<select id="findByNameLike" parameterType="string" resultType="com.itbaizhan.user.User">
   select * from user where username like #{name}
</select>

 编写测试方法

@Test
public void testFindByNameLike(){
  List<User> users = userMapper.findByNameLike("%王%");
  for (User user:users){
    System.out.println(user);
   }
}

 我们看到在映射文件中,parameterType的值为string而没有写java.lang.String,这是为什么呢?

  • 参数/返回值类型为基本数据类型/包装类/String等类型时,我们可以写全类名,也可以写别名。
数据类型别名
byte_byte
long_long
short_short
int_int
int_integer
double_double
float_float
boolean_boolean
Stringstring
Bytebyte
Longlong
Shortshort
Integerint/integer
Doubledouble
Floatfloat
Booleanboolean
Datedate
BigDecimaldecimal/bigdecimal
Objectobject
Mapmap
HashMaphashmap
Listlist
ArrayListarraylist
Collectioncollection
Iteratoriterator

8.4.2 使用$定义参数 

模糊查询如果不想在调用方法时参数加%,可以使用拼接参数的方式设置Sql:

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
   select * from user where username like '%${value}%'
</select>

测试方法写法如下:

@Test
public void testFindByNameLike(){
  List<User> users = userMapper.findByUsernameLike("程序");
  users.forEach(System.out::println);
}

#和$的区别:

  1. #表示sql模板的占位符,$表示将字符串拼接到sql模板中。
  2. #可以防止sql注入,一般能用#就不用$。
  3. ${}内部的参数名必须写value。

8.4.3 使用<bind>定义参数 

如果使用#还不想在调用方法的参数中添加%,可以使用<bind><bind>允许我们在 Sql语句以外创建一个变量,并可以将其绑定到当前的Sql语句中。用法如下:

<select id="findByUsernameLike" parameterType="string" resultType="com.itbaizhan.pojo.User">
  <bind name="likeName" value="'%'+username+'%'"/>
   select * from user where username like #{likeName}
</select>

测试方法写法如下:

@Test
public void testFindByNameLike(){
  List<User> users = userMapper.findByUsernameLike("程序");
  users.forEach(System.out::println);
}

8.5 分页查询

 

分页查询时,Sql语句使用limit关键字,需要传入开始索引和每页条数两个参数。MyBatis的多参数处理有以下方式:

顺序传参

Sql中的参数使用arg0,arg1...或param1,param2...表示参数的顺序。此方法可读性较低,在开发中不建议使用。

持久层接口方法

/**
   * 分页查询
   * @param startIndex 开始索引
   * @param pageSize 每页条数
   * @return
   */
List<User> findPage(int startIndex,int pageSize);

 映射文件

<select id="findPage" resultType="com.itbaizhan.mapper.User">
   select * from user limit #{arg0},#{arg1}
</select>

<select id="findPage" resultType="com.itbaizhan.mapper.User">
   select * from user limit #{param1},#{param2}
</select>

 测试类

@Test
public void testFindPage(){
  List<User> users = userMapper.findPage(0,3);
  users.forEach(System.out::println);
}

 8.6 @Param传参

在接口方法的参数列表中通过@Param定义参数名称,在Sql语句中通过注解中所定义的参数名称指定参数位置。此方式参数比较直观的,推荐使用。

持久层接口方法

List<User> findPage1(@Param("startIndex") int startIndex, @Param("pageSize")int pageSize);

 映射文件

<select id="findPage1" resultType="com.itbaizhan.mapper.User">
  select * from user limit #{startIndex},#{pageSize}
</select>

 测试类

@Test
public void testFindPage1(){
  List<User> users = userMapper.findPage1(3,3);
  users.forEach(System.out::println);
}

 8.7 POJO传参

自定义POJO类,该类的属性就是要传递的参数,在SQL语句中绑定参数时使用POJO的属性名作为参数名即可。此方式推荐使用。

自定义POJO

public class PageQuery {
  private int startIndex;
  private int pageSize;
    // 省略getter/setter/构造方法
}

持久层接口方法

List<User> findPage2(PageQuery pageQuery);

 映射文件

<select id="findPage2" resultType="com.itbaizhan.pojo.User" parameterType="com.itbaizhan.pojo.PageQuery">
   select * from user limit #{startIndex},#{pageSize}
</select>

 测试类

 

@Test
public void testFindPage2(){
  PageQuery pageQuery = new PageQuery(3, 3);
  List<User> users = userMapper.findPage2(pageQuery);
  users.forEach(System.out::println);
}

8.8 Map传参

如果不想自定义POJO,可以使用Map作为传递参数的载体,在SQL语句中绑定参数时使用Map的Key作为参数名即可。此方法推荐使用。

持久层接口方法

List<User> findPage3(Map<String,Object> params);

 映射文件

<select id="findPage3" resultType="com.itbaizhan.pojo.User" parameterType="map">
   select * from user limit #{startIndex},#{pageSize}
</select>

 测试类

@Test
public void testFindPage3(){
  Map<String,Object> params = new HashMap();
  params.put("startIndex",0);
  params.put("pageSize",4);
  List<User> users = userMapper.findPage3(params);
  users.forEach(System.out::println);
}

 9、聚合查询、主键回填

查询用户总数

持久层接口方法

int findCount();

 映射文件

<select id="findCount" resultType="int">
   select count(id) from user
</select>

 测试类

@Test
public void testFindCount(){
  System.out.println(userMapper.findCount());
}

 主键回填

有时我们需要获取新插入数据的主键值。如果数据库中主键是自增的,这时我们就需要使用MyBatis的主键回填功能。

持久层接口方法

void add(User user);

映射文件

<insert id="add" parameterType="com.itbaizhan.user.User">
  <!-- keyProperty:主键属性名,keyColumn:主键列名,resultType:主键类型,order:执行时机 -->
  <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
     SELECT LAST_INSERT_ID();
  </selectKey>
   insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>

 SELECT LAST_INSERT_ID():查询刚刚插入的记录的主键值,只适用于自增主键,且必须和insert语句一起执行。

测试类 

@Test
public void testAdd(){
  User user = new User("尚学堂", new Date(), "男", "北京");
  userMapper.add(user);
  session.commit();
  System.out.println(user.getId());
}

 10、配置文件

10.1 配置文件_<properties>

MyBatis配置文件结构:

-configuration
    -properties(属性)
        -property
    -settings(全局配置参数)
        -setting
    -plugins(插件)
        -plugin
    -typeAliases(别名)
        -typeAliase
        -package
    -environments(环境)
        -environment
            -transactionManager(事务管理)
            -dataSource(数据源)
    -mappers(映射器)
        -mapper
        -package

 

 

properties

属性值定义。properties标签中可以定义属性值,也可以引入外部配置文件。无论是内部定义还是外部引入,都可以使用${name}获取值。

例如:我们可以将数据源配置写到外部的db.properties中,再使用properties标签引入外部配置文件,这样可以做到动态配置数据源。

编写db.properties 

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root

 在配置文件中引入db.properties

<properties resource="db.properties"></properties>
<environments default="mysql">
  <environment id="mysql">
    <transactionManager type="JDBC"></transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
</environments>

 当然我们也可以将数据源数据通过<properties>配置到MyBatis配置文件内,但这样做没什么意义。

<properties>
  <property name="jdbc.driver" value="com.mysql.jdbc.Driver"></property>
  <property name="jdbc.url" value="jdbc:mysql://localhost:3306/mybatis"></property>
  <property name="jdbc.username" value="root"></property>
  <property name="jdbc.password" value="root"></property>
</properties>

<environments default="mysql">
  <environment id="mysql">
    <transactionManager type="JDBC"></transactionManager>
    <dataSource type="POOLED">
      <property name="driver" value="${jdbc.driver}"/>
      <property name="url" value="${jdbc.url}"/>
      <property name="username" value="${jdbc.username}"/>
      <property name="password" value="${jdbc.password}"/>
    </dataSource>
  </environment>
</environments>

 10.2 配置文件_<settings>

<settings>是配置MyBatis运行时的一些行为的,例如缓存、延迟加载、命名规则等一系列控制性参数。后期我们会使用该标签配置缓存和延迟加载等。

10.3 配置文件_<plugins>

<plugins>是配置MyBatis插件的。插件可以增强MyBatis功能,比如进行sql增强,打印日志,异常处理等。后期我们会使用该标签配置分页插件。

10.4 配置文件_<typeAliases>

 

MyBatis对常用类有默认别名支持,比如java.lang.Stirng的别名为string。除此之外,我们也可以使用<typeAliases>设置自定义别名。 

为一个类配置别名

 

<typeAliases>
    <typeAlias type="全类名" alias="别名"></typeAlias>
</typeAliases>

此时我们即可在映射文件中使用自定义别名,如:

配置文件:

<typeAliases>
    <typeAlias type="com.itbaizhan.pojo.User" alias="User">     </typeAlias>
</typeAliases>

 映射文件

<select id="findAll" resultType="User">
   select * from user
</select>

 为一个所有包下的所有类配置别名

<typeAliases>
  <package name="包名"></package>
</typeAliases>

此时该包下的所有类都有了别名,别名省略包名,和类名相同。如:

 

 

配置文件:

<typeAliases>
  <package name="com.itbaizhan.pojo"></package>
</typeAliases>

 映射文件:

 

<select id="findPage2" resultType="User" parameterType="PageQuery">
   select * from user limit #{startIndex},#{pageSize}
</select>

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

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

相关文章

超级实用的电脑小技巧大全

技巧一&#xff1a; 一些用户正在经历缓慢和间歇性的下载速度。 解决方案&#xff1a; 比如小编在下载界面做了温馨提示&#xff0c;要求用户在使用本软件前&#xff0c;退出电脑上安装的任何杀毒软件&#xff08;360安全卫士、QQ管家、金山卫士、百度卫士等&#xff09;&am…

C语言 - 通讯录详解

通讯录 文章目录1. 基本思路2.代码实现2.1 定义各种**宏**和**结构体**。2.2 创建结构体并进行初始化2.3 打印菜单&#xff0c;模拟用户的选择2.4 增加联系人2.5 删除联系人2.6 查找联系人2.7 修改联系人2.8 对通讯录进行升序排序2.9 打印通讯录2.10 结束程序并销毁通讯录3. 文…

再度入榜 | 中睿天下入选《中国网络安全企业100强》

近日&#xff0c;国内权威网络安全媒体、第三方安全咨询机构安全牛发布第十版《中国网络安全企业100强》&#xff08;以下简称“百强”&#xff09;&#xff0c;中睿天下以强大的综合发展实力和强劲的发展势头再次荣登百强榜单。 安全牛作为国内网络安全行业权威媒体&#xff0…

能解决你80%关于存储的疑惑

概述 在计算机系统中&#xff0c;常用的存储介质包括寄存器、内存、SSD、磁盘等&#xff0c;寄存器的速写速度与CPU相同&#xff0c;一个时钟周期是0.3纳秒&#xff0c;而内存访问需要120纳秒&#xff0c;寄存器的读写速度比内存要快几百倍&#xff0c;固态硬盘访问需要50&…

Vue3基础(26)___defineProps、defineEmits、defineExpose组件通信

defineProps、defineEmits、defineExpose组件通信 在使用这个之前&#xff0c;我们需要知道setup的语法糖写法&#xff0c;因为上面的三个api需要在这个语法糖中才能使用&#xff1a; <script setup>console.log(LiuQing) </script>里面的代码会被编译成组件 set…

m分别通过GA遗传优化算法对企业不同产品订单生产进行时间优化

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 首先介绍MATLAB部分的遗传算法的优化算法介绍&#xff1a; 遗传算法的原理 遗传算法GA把问题的解表示成“染色体”&#xff0c;在算法中也即是以二进制编码的串。并且&#xff0c;在执行…

WhatsApp营销引流-SendWS拓客系统功能后台介绍(五):WhatsApp素材管理

WhatsApp营销引流 很多新创的品牌想在海外推广自家产品&#xff0c;由于自己的品牌影响力不及其他老牌大牌&#xff0c;想在海外打开名气首先可以选择利用WhatsApp来进行营销引流&#xff0c;这是最简单最直接的方法&#xff0c;只需要低成本即可实现高回报。 据统计WhatsApp…

Python面向对象

目录 1.初识对象 2. 类的成员方法 3. 类和对象 4. 构造方法 5. 其它内置方法 6. 封装 7. 继承 7.1 继承的基础语法 7.2 复写和调用父类成员 8. 多态 1.初识对象 1. 生活中或是程序中&#xff0c;我们都可以使用设计表格、生产表格、填写表格的形式组织数据。 2. 进…

【2011】408联考操作系统真题整理

2011年 23 题目 满足短作业优先且不会发生饥饿现象的调度算法&#xff1a;高响应比优先 解析 24 题目 用户态执行的是命令解释程序 解析 法一&#xff1a;正向选择 DOS对应联机命令接口 法二&#xff1a;逆向排除 非用户态 -> 内核态 25 题目 在支持多线程的…

vs2019搭建与配置Qt,并实现影像显示

vs2019搭建与配置Qt&#xff0c;并实现影像显示 1.关于qt在vs2019的配置 主要参考了这个博客 其中有一个我在配置过程中出现的问题&#xff0c;在此记录一下 Qt Visual Studio Tools 在vs2019一直无法加载&#xff0c;一直在initializing,且提示"未能正确加载qtvstools…

数据库的基本操作(4)

先总结一下上一章的内容。 1.修改 update 表名 set 列名 值... where 条件&#xff1b; 2.删除 delete from 表名 where 条件&#xff1b; 3.mysql的约束 约束&#xff1a;数据库对数据本身有一些要求和限制。 NOT NULL 数据不能为空。 UNIQUE 数据唯一&#xff08;针…

【目标检测】目标检测的评价指标(七个)

目录&#xff1a;目标检测的评价指标一、正样本与负样本二、真正(TP)、假正(FP)、真负(TN)、假负(FN)&#xff08;1&#xff09;正确的正向预测&#xff08;True Positive&#xff0c;TP&#xff09;&#xff1a;正样本被正确检测的数量&#xff08;2&#xff09;错误的正向预测…

国际海运详解:国际海运的发货方式有哪些?区别是什么?

在跨境物流运输中&#xff0c;海运是一个种常用的运输方式&#xff0c;下面来重点介绍国际海运的几种发货方式和区别&#xff1a; 一、电放提单 是电报放货的缩写。提单信息以电子报纸或电子信息的形式发送给目的港船公司&#xff0c;收货人可以更换加盖电子印章的提单和电子保…

LeetCode-775-全局倒置与局部倒置

1、数学方法 根据题意&#xff0c;显然全局倒置的值大于等于局部倒置的值。因此我们不必求出具体的全局倒置的值和局部倒置的值&#xff0c;我们只需要证明全局倒置的值大于局部倒置的值即可。 因此我们可以从后往前进行查询&#xff0c;只要我们能够证明区间[i1,n−1][i1,n-…

小啊呜产品读书笔记001:《邱岳的产品手记-02》开篇词010203讲

小啊呜产品读书笔记001&#xff1a;《邱岳的产品手记-02》 开篇词&01&02&03讲一、今日阅读计划二、泛读&知识摘录1、开篇词 产品经理的世界没有对错2、01讲 验证码是个好设计吗&#xff1f;3、02讲 产品经理工具指南4、03讲 产品案例分析Trigraphy的设计哲学三…

VS2019编码修改为UTF-8的方法

1.修改windows系统配置 设置 时区和语言 语言 》 管理语言设置 2. 修改VS2019 在扩展里安装Force UTF-8(No BOM) 和 Fix File Encoding 插件 解决控制台中文乱码问题 1.按下winr&#xff0c;输出regedit&#xff0c;打开注册表编辑器 2.在注册表编辑器中打开路径HKE…

VBA调用宏的方式总结大全

文章目录背景方式一 : Excel菜单执行宏方式二 : 按钮绑定宏1. 插入按钮2. 绑定宏方式三 : 窗体绑定宏1. 插入窗体2. 拖入按钮3. 绑定按钮点击事件触发宏(写代码)方式四 : 事件绑定宏背景 很多小伙伴在拿到模型之后, 看着满屏的代码, 却不知道如何下手使用这些代码. 这篇文章就…

【DOTS学习笔记】面向数据设计DOD

目录前言程序设计方法ODD->DODCache的3C与3R面向数据设计需要思考的问题AOSSOADOTS面向数据设计原则前言 本文是Metaverse大衍神君的《DOTS之路》系列课程的学习笔记 程序设计方法 Instructional Programming 指令化编程 脱离指令打孔输入后&#xff0c;伴随着机器汇编语言…

操作系统4小时速成:进程同步,临界资源,互斥,信号量的作用,死锁产生的四个条件,安全状态,银行家算法

操作系统4小时速成&#xff1a;进程同步&#xff0c;临界资源&#xff0c;互斥&#xff0c;信号量的作用&#xff0c;死锁产生的四个条件&#xff0c;安全状态&#xff0c;银行家算法 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&am…

vhost dpdk 共享内存

struct kvm_memory_slot http://tinylab.org/kvm-intro-part1 https://www.cnblogs.com/LoyenWang/p/13943005.html &#xff08;免费订阅,永久学习&#xff09;学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂 更多DPDK相关学习资料有…