MyBatis 高级使用

news2024/11/17 21:45:36

文章目录

  • 动态SQL语句
    • if
    • choose
    • trim
    • foreach
  • 批量操作
    • 批量插入
    • 批量更新
    • 批量删除
    • BatchExecutor
  • 关联查询
    • 嵌套查询
    • 延迟加载
  • 分页操作
    • 逻辑分页
    • 物理分页
  • MyBatis Generator
    • 添加配置文件
    • 添加插件
    • 生成
  • 通用Mapper
    • 方式一
    • 方式二
  • MyBatis-Plus

动态SQL语句

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

需要判断的时候,条件写在test中

    <select id="selectListIf" parameterType="user" resultMap="BaseResultMap" >
        select
            <include refid="baseSQL"></include>
        from t_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="userName != null">
                and user_name = #{userName}
            </if>
        </where>
    </select>

choose

需要选择一个条件的时候

    <!-- choose 的使用 -->
    <select id="selectListChoose" parameterType="user" resultMap="BaseResultMap" >
        select
        <include refid="baseSQL"></include>
        from t_user
        <where>
            <choose>
                <when test="id != null">
                    id = #{id}
                </when>
                <when test="userName != null and userName != ''">
                    and user_name like CONCAT(CONCAT('%',#{userName,jdbcType=VARCHAR}),'%')
                </when>
                <otherwise>

                </otherwise>
            </choose>
        </where>
    </select>

trim

需要去掉where、and、逗号之类的符号的时候

    <!--
        trim 的使用
        替代where标签的使用
    -->
    <select id="selectListTrim" resultMap="BaseResultMap" 
            parameterType="user">
        select <include refid="baseSQL"></include>
        <trim prefix="where" prefixOverrides="AND |OR ">
            <if test="userName!=null">
                and user_name = #{userName}
            </if>
            <if test="age != 0">
                and age = #{age}
            </if>
        </trim>
    </select>

foreach

需要遍历集合的时候

    <delete id="deleteByList" parameterType="java.util.List">
         delete from t_user 
         where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
             #{item.id,jdbcType=INTEGER}
        </foreach>
    </delete>

批量操作

在项目中会有一些批量操作的场景,比如导入文件批量处理数据的情况,当数据量非常大,比如超过几万条的时候,在Java代码中循环发送SQL到数据库执行肯定是不现实的,因为这个意味着要跟数据库创建几万次会话。即使在同一个连接中,也有重复编译和执行SQL的开销。

例如循环插入10000条(大约耗时3秒钟):

public class Test03Batch {

    public SqlSession session;

    @Before
    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 循环插入10000
     */
    @Test
    public void test1(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setUserName("a"+i);
            mapper.insertUser(user);
         }
         session.commit();
         session.close();
         long end = System.currentTimeMillis();
         System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫秒");
    }
}

在MyBatis里面是支持批量的操作的,包括批量的插入、更新、删除。我们可以直接传入一个List、Set、Map或者数组,配合动态SQL的标签,MyBatis会自动帮我们生成语法正确的SQL语句。

批量插入

批量插入只要在values后面增加插入的值就可以了。

insert into tbl_emp (emp_id, emp_name, gender,email, d_id) values ( ?,?,?,?,? ),( ?,?,?,?,? ),( ?,?,?,?,? )

在Mapper文件里面,我们使用foreach标签拼接 values部分的语句:

    <!-- 批量插入 -->
    <insert id="insertUserList" parameterType="java.util.List" >
        insert into t_user(user_name,real_name)
        values
        <foreach collection="list" item="user" separator=",">
            (#{user.userName},#{user.realName})
        </foreach>

    </insert>

Java代码里面,直接传入一个List类型的参数。

    /**
     * 批量插入
     */
    @Test
    public void test2(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setUserName("a"+i);
            list.add(user);
        }
        mapper.insertUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫秒");
    }

使用批量插入方式,插入一万条大约耗时1秒钟。动态SQL批量插入效率要比循环发送SQL执行要高得多。最关键的地方就在于减少了跟数据库交互的次数,并且避免了开启和结束事务的时间消耗。

批量更新

批量更新通过case when,来匹配id相关的字段值

update t_user set 
user_name = 
case id 
when ? then ? 
when ? then ? 
when ? then ? end ,
real_name = 
case id
when ? then ? 
when ? then ? 
when ? then ? end 
where id in ( ? , ? , ? )

所以在Mapper文件里面最关键的就是case when和where的配置。

    <update id="updateUserList">
     update t_user set
        user_name =
        <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
        when #{user.id} then #{user.userName}
        </foreach>
         ,real_name =
         <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
          when #{user.id} then #{user.realName}
         </foreach>
         where id in
         <foreach collection="list" item="item" open="(" separator="," close=")">
          #{item.id,jdbcType=INTEGER}
         </foreach>
     </update>

java代码实现

    /**
     * 批量更新
     */
    @Test
    public void test3(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setId(i);
            user.setUserName("a"+i);
            list.add(user);
        }
        mapper.updateUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("批量更新"+count+"条,耗时:" + (end -start )+"毫秒");
    }

批量删除

批量删除也是类似的。

    <delete id="deleteByList" parameterType="java.util.List">
         delete from t_user where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.id,jdbcType=INTEGER}
        </foreach>
    </delete>

BatchExecutor

MyBatis的动态标签的批量操作也是存在一定的缺点的,比如数据量特别大的时候,拼接出来的SQL语句过大。

MySQL的服务端对于接收的数据包有大小限制,max_allowed_packet 默认是 4M,需要修改默认配置或者手动地控制条数,才可以解决这个问题。

Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (7188967 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.

在全局配置文件中,可以配置默认的Executor的类型(默认是SIMPLE)。其中有一种BatchExecutor。

<setting name="defaultExecutorType" value="BATCH" />

也可以在创建会话的时候指定执行器类型

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

Executor

image.png

  1. SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
  2. ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
  3. BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。executeUpdate()是一个语句访问一次数据库,executeBatch()是一批语句访问一次数据库(具体一批发送多少条SQL跟服务端的max_allowed_packet有关)。BatchExecutor底层是对JDBC ps.addBatch()和ps. executeBatch()的封装。

关联查询

嵌套查询

在查询业务数据的时候经常会遇到关联查询的情况,比如查询员工就会关联部门(一对一),查询学生成绩就会关联课程(一对一),查询订单就会关联商品(一对多)等等。

嵌套查询 1对1 , 1个用户对应一个部门

 
    <resultMap id="nestedMap1" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept">
            <id column="did" property="dId"/>
            <result column="d_name" property="dName"/>
            <result column="d_desc" property="dDesc"/>
        </association>
    </resultMap>

    <select id="queryUserNested" resultMap="nestedMap1">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        LEFT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

嵌套查询 1对多 ,1个部门有多个用户

<!-- -->
    <resultMap id="nestedMap2" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
        <collection property="users" ofType="user">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="userName" column="user_name" jdbcType="VARCHAR" />
            <result property="realName" column="real_name" jdbcType="VARCHAR" />
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
            <result property="dId" column="d_id" jdbcType="INTEGER"/>
        </collection>
    </resultMap>
    <select id="queryDeptNested" resultMap="nestedMap2">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        RIGHT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

延迟加载

在MyBatis里面可以通过开启延迟加载的开关来解决这个问题。

在settings标签里面可以配置:

<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。默认false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- MyBatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<setting name="proxyFactory" value="CGLIB" />

lazyLoadingEnabled决定了是否延迟加载(默认false)。

aggressiveLazyLoading决定了是不是对象的所有方法都会触发查询。

1对1的延迟加载配置

    <!-- 延迟加载 1对1 -->
    <resultMap id="nestedMap1Lazy" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept" column="d_id" select="queryDeptByUserIdLazy">

        </association>
    </resultMap>
    <resultMap id="baseDept" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
    </resultMap>
    <select id="queryUserNestedLazy" resultMap="nestedMap1Lazy">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t1.d_id
        FROM t_user t1
    </select>
    <select id="queryDeptByUserIdLazy" parameterType="int" resultMap="baseDept">
        select * from t_department where did = #{did}
    </select>

开启了延迟加载的开关,调用user.getDept()时才会发起第二次查询;

    /**
     * 1对1  关联查询 延迟加载
     * @throws Exception
     */
    @Test
    public void test03() throws Exception{
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.queryUserNestedLazy();
        for (User user : users) {
      
            System.out.println(user.getUserName() + "---->"+user.getDept());
        }
    }

分页操作

逻辑分页

MyBatis里面有一个逻辑分页对象RowBounds,里面主要有两个属性,offset和limit(从第几条开始,查询多少条)。我们可以在Mapper接口的方法上加上这个参数,不需要修改xml里面的SQL语句。

接口中定义

    public List<User> queryUserList(RowBounds rowBounds);

测试类

    @Test
    public void test01() throws Exception{
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        // 设置分页的数据
        RowBounds rowBounds = new RowBounds(1,3);
        List<User> users = mapper.queryUserList(rowBounds);
        for (User user : users) {
            System.out.println(user);
        }
    }

RowBounds的工作原理其实是对ResultSet的处理。它会舍弃掉前面offset条数据,然后再取剩下的数据的limit条。

// DefaultResultSetHandler.java
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
   DefaultResultContext<Object> resultContext = new DefaultResultContext();
   ResultSet resultSet = rsw.getResultSet();
   this.skipRows(resultSet, rowBounds);
   while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
       ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
       Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
       this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

很明显,如果数据量大的话,这种翻页方式效率会很低(跟查询到内存中再使用subList(start,end)没什么区别)。所以我们要用到物理翻页。

物理分页

物理翻页是真正的翻页,它是通过数据库支持的语句来翻页。

第一种简单的办法就是传入参数(或者包装一个page对象),在SQL语句中翻页。

<select id="selectUserPage" parameterType="map" resultMap="BaseResultMap">
  select * from t_user limit #{curIndex} , #{pageSize}
</select>

这种方式会存在两个问题:第一个问题是我们要在Java业务代码里面去计算起止序号;第二个问题是:每个需要翻页的Statement都要编写limit语句,会造成Mapper映射器里面很多代码冗余。

需要一种通用的方式,不需要去修改配置的任何一条SQL语句,我们只要传入当前是第几页,每页多少条就可以了,自动计算出来起止序号。

我们最常用的做法就是使用翻页的插件,比如PageHelper。

// pageSize每一页几条
PageHelper.startPage(pn, 10);
List<Employee> emps = employeeService.getAll();
// navigatePages 导航页码数
PageInfo page = new PageInfo(emps, 10);
return Msg.success().add("pageInfo", page);

PageHelper是通过MyBatis的拦截器实现的,简单地来说,它会根据PageHelper的参数,改写我们的SQL语句。比如MySQL会生成limit语句,Oracle会生成rownum语句,SQL Server会生成top语句。

MyBatis Generator

在项目中使用MyBaits的时候,针对需要操作的一张表,需要创建实体类、Mapper映射器、Mapper接口,里面又有很多的字段和方法的配置,这部分的工作是非常繁琐的。而大部分时候我们对于表的基本操作是相同的,比如根据主键查询、根据Map查询、单条插入、批量插入、根据主键删除等等等等。当我们的表很多的时候,意味着有大量的重复工作。

所以有没有一种办法,可以根据我们的表,自动生成实体类、Mapper映射器、Mapper接口,里面包含了我们需要用到的这些基本方法和SQL呢?

MyBatis也提供了一个代码生成器,叫做MyBatis Generator,简称MBG(它是MyBatis的一个插件)。我们只需要修改一个配置文件,使用相关的jar包命令或者Java代码就可以帮助我们生成实体类、映射器和接口文件。

MBG的配置文件里面有一个Example的开关,这个东西用来构造复杂的筛选条件的,换句话说就是根据我们的代码去生成where条件。

原理:在实体类中包含了两个有继承关系的Criteria,用其中自动生成的方法来构建查询条件。把这个包含了Criteria的实体类作为参数传到查询参数中,在解析Mapper映射器的时候会转换成SQL条件。

添加配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- 数据库的驱动包路径 -->
    <classPathEntry location="C:\Users\mysql-connector-java-8.0.11.jar" />

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 去掉生成文件中的注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- 数据库链接URL、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC"
                        userId="root"
                        password="123456">
            <property name="nullCatalogMeansCurrent" value="true" />
        </jdbcConnection>

        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!-- 生成模型的包名和位置 -->
        <javaModelGenerator targetPackage="com.domain" targetProject="./src/main/java">
            <!-- 是否在当前路径下新加一层schema -->
            <property name="enableSubPackages" value="false" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- 生成的映射文件包名和位置 -->
        <sqlMapGenerator targetPackage="com.mapper"  targetProject="./src/main/java">
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- 生成DAO的包名和位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.mapper"  targetProject="./src/main/java">
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <table  tableName="t_user" domainObjectName="User"   />


    </context>
</generatorConfiguration>

添加插件

在pom.xml中添加对应的插件

    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <!-- 指定配置文件的位置 -->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

生成

maven项目中执行插件帮助我们快速生成需要的表结构对应的相关文件。

通用Mapper

当我们的表字段发生变化的时候,我们需要修改实体类和Mapper文件定义的字段和方法。如果是增量维护,那么一个个文件去修改。如果是全量替换,我们还要去对比用MBG生成的文件。字段变动一次就要修改一次,维护起来非常麻烦。

方式一

因为MyBatis的Mapper是支持继承的。所以可以把Mapper.xml和Mapper接口都分成两个文件。一个是MBG生成的,这部分是固定不变的。然后创建DAO类继承生成的接口,变化的部分就在DAO里面维护。

public interface UserMapperExt extends UserMapper {
    public List<User> selectUserByName(String userName);
}

对应的映射文件

<?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.mapper.UserMapperExt" >
  <resultMap id="BaseResultMapExt" type="com.domain.User" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="user_name" property="userName" jdbcType="VARCHAR" />
    <result column="real_name" property="realName" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="age" property="age" jdbcType="INTEGER" />
    <result column="d_id" property="dId" jdbcType="INTEGER" />
    <result column="i_id" property="iId" jdbcType="INTEGER" />
  </resultMap>
  
  <select id="selectUserByName" resultMap="BaseResultMapExt" >
    select * from t_user where user_name = #{userName}
  </select>
</mapper>

在全局配置文件中我们也需要扫描

    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/UserMapperExt.xml"/>
    </mappers>

所以以后只要修改Ext的文件就可以了。这么做有一个缺点,就是文件会增多。

方式二

既然针对每张表生成的基本方法都是一样的,也就是公共的方法部分代码都是一样的,能不能把这部分合并成一个文件,让它支持泛型呢?

编写一个支持泛型的通用接口,比如叫GPBaseMapper,把实体类作为参数传入。这个接口里面定义了大量的增删改查的基础方法,这些方法都是支持泛型的。

自定义的Mapper接口继承该通用接口,例如BlogMapper extends GPBaseMapper,自动获得对实体类的操作方法。遇到没有的方法,我们依然可以在我们自己的Mapper里面编写。

能想到的解决方案,早就有人做了这个事了,这个东西就叫做通用Mapper。

用途:主要解决单表的增删改查问题,并不适用于多表关联查询的场景。

除了配置文件变动的问题之外,通用Mapper还可以解决:

  1. 每个Mapper接口中大量的重复方法的定义;
  2. 屏蔽数据库的差异;
  3. 提供批量操作的方法;
  4. 实现分页。

使用方式:在Spring中使用时,引入jar包,替换applicationContext.xml中的sqlSessionFactory和configure。

<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
   <property name="basePackage" value="com.crud.dao"/>
</bean>

MyBatis-Plus

MyBatis-Plus是原生MyBatis的一个增强工具,可以在使用原生MyBatis的所有功能的基础上,使用plus特有的功能。

MyBatis-Plus的核心功能:

通用 CRUD:定义好Mapper接口后,只需要继承BaseMapper 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件。

条件构造器:通过EntityWrapper(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的SQL。

代码生成器:支持一系列的策略配置与全局配置,比MyBatis的代码生成更好用。

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

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

相关文章

【运维篇】二、配置文件与多环境控制

文章目录 1、临时属性2、IDEA中的临时属性3、配置文件4级分类4、关于四级分类的思考5、自定义配置文件6、多环境开发&#xff08;yaml版&#xff09;7、配置文件按环境分类8、include与group再细粒度9、一点思考10、多环境开发兼容问题 1、临时属性 jar包或者镜像已经打完了&a…

根据每帧点云的PCD文件和每帧的位姿合成整个点云地图(附python open3d 代码)

现在有多个PCD文件表示每帧的点云,有一个位姿文件,里面是每帧的位姿,需要根据每帧点云和每帧的位姿合成整个地图。 首先,从文件中读取所有点云文件的路径,并将其存储到一个列表中。然后,读取位姿文件,并将其转换为一个 3x4 的矩阵。 然后,遍历所有点云文件。对于每帧点…

如何通过简历展示自己的执行力和动力?

导语: 简历是求职过程中的重要工具&#xff0c;通过合适的展示方式能够有效地展示自己的执行力和动力。本文将分享一些技巧&#xff0c;帮助您在简历中突出这两个关键能力。 突出成就和项目经历: 在简历中详细描述您曾经完成的项目或工作&#xff0c;并着重强调其中的具体成果…

ICS TRIPLEX T9402 自动化控制模块

ICS TRIPLEX T9402 是一款自动化控制模块&#xff0c;通常用于工业自动化和控制系统中&#xff0c;用于监测、控制和自动化各种工业过程。以下是该产品的一些主要特点&#xff1a; 可靠性&#xff1a; T9402 模块通常具有高可靠性&#xff0c;以确保系统能够稳定运行&#xff0…

开学季哪个牌子的电容笔好?ipad2023手写笔推荐

到底是用苹果原装的电容笔&#xff0c;还是用平替的电容笔&#xff0c;这要根据自己的需要来决定&#xff0c;比如经常用在画画上&#xff0c;可以选择苹果原装笔&#xff1b;如果你一天里用来写东西的时间多于用来画画的时间&#xff0c;那你就该考虑一下&#xff0c;买一支更…

基于GBDT+Tkinter+穷举法按排队时间预测最优路径的智能导航推荐系统——机器学习算法应用(含Python工程源码)+数据集(一)

目录 前言总体设计系统整体结构图系统流程图 运行环境Python环境Pycharm 环境Scikit-learnt 模块实现1. 数据预处理1&#xff09;加载数据集2&#xff09;时间划分与保存3&#xff09;处理天气预报数据4&#xff09;增加特征5&#xff09;合并特征值 相关其它博客工程源代码下载…

pytest框架前后置设置,以及pytest默认规则

一、pytest框架的默认规则 1、模块名默认必须以test开头或者以test结尾 2、测试类必须以Test开头&#xff0c;并且不能有__init__方法 3、测试方法默认必须以test开头 当然以后的一些默认规则除测试类不能使用__init__方法外其余的都是可配置的&#xff0c;当然一般情况下我们…

TypeError: The view function did not return a valid response

TypeError: The view function did not return a valid response._白振峰的博客-CSDN博客

小程序中如何查看会员的优惠券领取记录

在小程序中&#xff0c;我们可以通过如下方式来查看会员的优惠券领取记录。下面是具体的操作流程&#xff1a; 1. 找到指定的会员卡。在管理员后台->会员管理处&#xff0c;找到需要查看优惠券记录的会员卡。也支持对会员卡按卡号、手机号和等级进行搜索。 2. 查看会员卡详…

华为OD机考算法题:篮球比赛

目录 题目部分 解读与分析 代码实现 题目部分 题目篮球比赛难度难题目说明篮球(5V5)比赛中&#xff0c;每个球员拥有一个战斗力&#xff0c;每个队伍的所有球员战斗力之和为该队伍的总体战斗力。现有 10 个球员准备分为两队进行训练赛&#xff0c;教练希望 2 个队伍的战斗力…

深度学习——卷积神经网络

卷积神经网络 1 计算机视觉 (Computer Vision)2 边缘检测示例 (Edge Detection Example)3 更多边缘检测内容 (More Edge Detection Example)4 Padding5 卷积步长 (Strided Convolutions)6 三维卷积 (Convolutions Over Volumes)7 单层卷积网络 (One Layer of a Convolutional N…

从速度到兼容性:一起来看看腾讯文档双核引擎的全面优势

文章目录 前言功能测评打开编辑本地文件本地文件云存储转多人在线编辑便捷的文件分享 测评感受 前言 在刚结束的腾讯数字生态大会上&#xff0c;腾讯文档发布了独家自主研发的双核编辑引擎。根据会上介绍&#xff0c;该引擎采用了统一的 OOXML 底层存储格式&#xff0c;实现了…

echarts-可视化地图防重叠文本框

我在第一篇可视化地图中&#xff0c;有一些基础介绍&#xff0c;本篇文章就是多展示一些效果&#xff0c;大家可以按需获取。 先直接上效果图 这里的配置项有用到 1、通过geo展示多层地图&#xff0c;这样可以像上图所示&#xff0c;通过错位有了一些3D效果&#xff1b; 2、北…

golang实现远程控制主机

文章目录 ssh原理使用golang远程下发命令使用golang远程传输文件 ssh原理 说到ssh原理个人觉得解释最全的一张图是这张华为画的 Connection establishment 这一步就是建立tcp连接 version negotiation 这一步是ssh客户端(连接者)和被ssh服务端(连接者)进行协议的交换&#xf…

湖南长沙石雕石质文物三维扫描数字化雕刻3D打印复刻文化遗产-CASAIM中科广电

石质文物主要包括石雕、石塔和古建筑等&#xff0c;颇具代表性的雕刻动物作品有&#xff1a;龙、凤、狮子、麒麟、貔貅、金蟾等。石雕是我国文化遗产的重要组成&#xff0c;在书写灿烂文明中扮演着重要角色&#xff0c;记载了我国文化和历史的变迁。 随着现代艺术的发展&#…

vscode快捷键大全中英文

vscode快捷键大全中英文 源文件下载链接

700多心理测试性格测试大全ACCESS\EXCEL数据库

这是一个关于心理测试、性格测试的ACCESS数据库&#xff0c;这个测试有一个测试项目一个问题选择后就有结果&#xff0c;也有一个测试项目有N多题需做完N多题根据各题得分得出总结果&#xff0c;所以ACCESS数据表的结构设计的很灵活。 测试项目表&#xff1a;为整个ACCESS数据…

DDoS攻击和CC攻击

DDoS是&#xff08;Distributed Denial of Service&#xff0c;分布式拒绝服务&#xff09;攻击和CC&#xff08;Challenge Collapsar&#xff0c;挑战黑洞) 攻击是两种常见且具有破坏性的攻击类型&#xff0c;它们可以对网络基础设施和在线业务造成重大损害。为了抵御这些攻击…

vscode编写前端提升效率的三个必不可缺的插件以及使用方法

直接官网下载这个软件就行&#xff0c;没什么操作的。 这里面有新建文件夹&#xff0c;你可以自己去建一个文件夹 然后点击那个小号&#xff0c;就可以新建一个文件&#xff0c;比如说demo01.html,⚠️后面的html是你需要自己手动输入的 第一个插件&#xff0c;就是这个她可以让…

Android 虚拟机

文章目录 Android 虚拟机Java虚拟机基于栈的虚拟机栈的执行流程 Dalvik虚拟机基于寄存器的虚拟机寄存器的执行流程Java虚拟机与Dalvik虚拟机区别 ART虚拟机Android 7.0的运行方式 Android 虚拟机 Java虚拟机 基于栈的虚拟机 每一个运行时的线程&#xff0c;都有一个独立的栈…