MyBatis 的在使用上的注意事项及其辨析

1. MyBatis 的在使用上的注意事项及其辨析

文章目录

  • 1. MyBatis 的在使用上的注意事项及其辨析
  • 2. 准备工作
  • 3. #{ } 与 ${ } 的区别和使用
    • 3.1 什么情况下必须使用 ${ }
      • 3.1.1 拼接表名
      • 3.1.2 批量删除
      • 3.1.3 模糊查询
        • 3.1.3.1 使用 ${ }的方式
        • 3.1.3.2 使用 #{ } 的方式
  • 4. typeAliases 别名定义的使用
    • 4.1 typeAliases 的第一种方式:typeAlias
    • 4.2 typeAliases 的第二种方式:package
  • 5. mappers 路径设置的使用
    • 5.1 mapper 标签下的 resource 属性的使用
    • 5.2 mapper 标签下的 url 属性的使用
    • 5.3 mapper 标签下的 class 属性的使用
    • 5.4 package 标签的使用
  • 6. 在 IDEA 中自定义配置文件模板
  • 7. 补充:插入数据时获取自动生成的主键的值
  • 8. 总结:
  • 9. 最后:


2. 准备工作

数据表结构的设计,数据表名为:t_car

在这里插入图片描述

t_car 表中的数据信息:

在这里插入图片描述

pom.xml 文件当中配置相关的依赖的 jar 包如下:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.rainbowsea</groupId>
    <artifactId>mybatis-005-crud-blog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <!--        mybatis 的依赖-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

        <!--        mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!--        引入 logback的依赖,这个日志框架实现了slf4j 规范-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

</project>

配置 logback 的配置文件,用于打印显示,我们的日志信息,方便我们查看我们的运行过程,效果。

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

配置 MyBatis 的核心配置文件,

在这里插入图片描述

<?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>
    <!--    起别名-->
    <typeAliases>
        <!--  使用 <package>	还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写 -->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 没有用第三框架管理的话,都是会被提交的,没有事务上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
        <mapper resource="CarMapper.xml"></mapper>
    </mappers>
</configuration>

对照 t_car 创建的ORM 映射的 Car 类

注意:在MyBatis 当中对应的ORM ,一般在框架里对应的 Bean实体类,一定要实现该 set 和 get 方法以及无参数构造方法,无法框架无法使用反射机制,进行操作

建议用包装类,这样可以防止 Null的问题,因为(简单类型 int num = null ,是不可以赋值为 null)的编译无法通过

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;

public class Car {
    // 数据库表当中的字段应该和pojo类的属性一一对应
    // 建议使用包装类,这样可以防止null的问题
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;

    public Car() {
    }

    public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
        this.id = id;
        this.carNum = carNum;
        this.brand = brand;
        this.guidePrice = guidePrice;
        this.produceTime = produceTime;
        this.carType = carType;
    }

    @Override
    public String toString() {
        return "Car{" +
                "id=" + id +
                ", carNum='" + carNum + '\'' +
                ", brand='" + brand + '\'' +
                ", guidePrice=" + guidePrice +
                ", produceTime='" + produceTime + '\'' +
                ", catType='" + carType + '\'' +
                '}';
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCarNum() {
        return carNum;
    }

    public void setCarNum(String carNum) {
        this.carNum = carNum;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Double getGuidePrice() {
        return guidePrice;
    }

    public void setGuidePrice(Double guidePrice) {
        this.guidePrice = guidePrice;
    }

    public String getProduceTime() {
        return produceTime;
    }

    public void setProduceTime(String produceTime) {
        this.produceTime = produceTime;
    }

    public String getcarType() {
        return carType;
    }

    public void setcarType(String catType) {
        this.carType = catType;
    }
}

对应操作实现CRUD(增删改查)的接口(这里是:CarMapper接口),在MyBtis 当中 ,关于 CRUD(增删改查)操作的接口/实现类,都是 mapper 结尾的作为持久层,而在 MVC的三层架构中,则是以 dao 为后缀作为CRUD(增删改查)操作的接口/实现类。

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

public interface CarMapper {


    /**
     * 新增 Car
     * @param car
     * @return
     */
    int insert(Car car);


    /**
     * 根据id 删除 Car
     * @param id
     * @return
     */
    int deleteById(Long id);


    /**
     * 修改汽车信息
     * @param car
     * @return
     */
    int update(Car car);


    /**
     * 根据id查询汽车信息
     * @param id
     * @return
     */
    Car selectById(Long id);


    /**
     * 获取所有的汽车信息
     * @return
     */
    List<Car> selectAll();
}

获取 Sqlsession 对象的工具类的编写。

在这里插入图片描述

package com.rainbowsea.mybatis.utils;


import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;

public class SqlSessionUtil {
    // 工具类的构造方法一般都是私有话化的
    // 工具类中所有的方法都是静态的,直接类名即可调用,不需要 new 对象
    // 为了防止new对象,构造方法私有化。

    private SqlSessionUtil() {

    }



    private static SqlSessionFactory sessionFactory = null;

    // 静态代码块,类加载时执行
    // SqlSessionUtil 工具类在进行第一次加载的时候,解析mybatis-config.xml 文件,创建SqlSessionFactory对象。
    static {
        // 获取到  SqlSessionFactoryBuilder 对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();

        // 获取到SqlSessionFactory 对象
        // SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库
        try {
            sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"),
                    "mybatis");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取会话对象
     * @return SqlSession
     */
    public static SqlSession openSession() {
        // 获取到 SqlSession 对象
        SqlSession sqlSession = sessionFactory.openSession();
        return sqlSession;
    }
}

3. #{ } 与 ${ } 的区别和使用

  • #{ } :先编译 SQL 语句,再对占位符传值,底层是 PrepareedStatement 实现,可以防止 SQL 注入,比较常用
  • ${ }:先进行SQL语句的拼接,然后再对SQL语句进行编译,底层是 Statement 实现,这种方式存在 SQL注入现象,SQL注入的风险,简单的说就是,直接将传入的值拼接为了SQL语句,然后再执行的)。只有在需要进行SQL语句关键字拼接的情况下才会用到。
  • 简单的说一个区别就是:#{} 传的值是带有 ‘’ 单引号的,而 ${} 传的值是(直接就是值,没有单引号,或者是双引号,两个都没有)

检验一下。

这里我们根据汽车的 car_type 查询,为新能源汽车的。多条记录

首先这里我们先使用 #{} ,传的是带有 ‘’ 单引号的值 ——> ‘新能源’

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封装汽车相关信息的pojo类,普通的Java类
 */
public interface CarMapper {

    /**
     * 根据汽车类型获取汽车信息
     * @param carType
     * @return
     */
    List<Car> selectByCarType(String carType);

}

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectByCarType" resultType="com.rainbowsea.mybatis.pojo.Car">
        select id,
               car_num      as carNum,
               brand,
               guide_price  as guidePrice,
               produce_time as produceTime,
               car_type     as carType
        from t_car
        where car_type = #{carType}
    </select>

</mapper>

运行Java程序:

在这里插入图片描述

在这里插入图片描述

查询成功。

下面我们再实验用 ${} ,直接就是新能源的值,没有单引号,也没有双引号,就是直接就是——》新能源的值。运行结果如下:

在这里插入图片描述

比较上述#{}和 ?{} 的运行结果:

#{}

在这里插入图片描述

${}

在这里插入图片描述

通过执行可以清楚的看到,sql 语句中是带有 ? 的,这个 ? 就是大家在JDBC中所学的占位符,专门用来接收值的。

把“新能源”以 String 类型的值,传递给 ? ,加上 ‘’ 单引号,作为字符串传入进行,执行SQL语句

这就是 #{},它会先进行sql语句的预编译,然后再给占位符传值。

而 ${} 是直接将我们的 新能源 作为值,传入给SQL语句的,注意 ${} 的方式是不会加单引号/双引号的,而是作为值,直接拼接到 SQL语句当中去了,但是,在我们这个查询的SQL当中,新能源就是必须要为字符串才行的,不然是无法执行SQL语句,是无法识别出来的。

在这里插入图片描述

3.1 什么情况下必须使用 ${ }

当需要进行 sql 语句关键字拼接的时候,简单的说就是当我们要使用 SQL语句当中的关键字的时候,以及要传的值,不要单引号/双引号的值的时候,就必须使用${ }

需求:通过向sql语句中注入asc或desc关键字,来完成数据的升序或降序排列。

根据数据表中的 produce_time日期时间进行排序。

因为这里我们使用到了 ASC / DESC 这两者都是 SQL语句当中的关键字,所以我们需要使用 ${} 的方式。

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封装汽车相关信息的pojo类,普通的Java类
 */
public interface CarMapper {

    /**
     * 查询所有的汽车信息,然后通过 asc 升序,desc 降序
     * @param ascOrDesc
     * @return
     */
    List<Car> selectAllByAscOrDesc(String ascOrDesc);


}

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectAllByAscOrDesc" resultType="com.rainbowsea.mybatis.pojo.Car">
        select id,
               car_num      as carNum,
               brand,
               guide_price  as guidePrice,
               produce_time as produceTime,
               car_type     as carType
        from t_car
        order by produce_time ${ascOrDesc}
    </select>

</mapper>

Java程序运行测试:

在这里插入图片描述

public class TestCarMapper {

    @Test
    public void testSelectAllByAscOrDesc() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAllByAscOrDesc("asc");
        cars.forEach(car -> {
            System.out.println(car);
        });

        sqlSession.close();
    }

}

如果换成 #{} ,将SQL 语句的关键字 asc 作为字符串的形式拼接到 SQL语句当中是会编译失败的了。

在这里插入图片描述

3.1.1 拼接表名

业务背景:实际开发中,有的表数据量非常庞大,可能会采用分表方式进行存储,比如每天生成一张表,表的名字与日期挂钩,例如:2022年8月1日生成的表:t_user20220108。2000年1月1日生成的表:t_user20000101。此时前端在进行查询的时候会提交一个具体的日期,比如前端提交的日期为:2000年1月1日,那么后端就会根据这个日期动态拼接表名为:t_user20000101。有了这个表名之后,将表名拼接到sql语句当中,返回查询结果。那么大家思考一下,拼接表名到sql语句当中应该使用#{} 还是 ${} 呢?

使用#{}会是这样:select * from ‘t_car’

使用${}会是这样:select * from t_car

向SQL语句当中拼接表名,就需要使用${}
现实业务当中,可能存在分表存储的数据的情况,因为一张表存的话,数据量太大了,查询效率比较低。
可以将这些数据有规律的分表存储,这样在查询的时候效率就比较高,因为扫描的数据量变小了
日志表,专门存储日志信息,如果t_long 只有一张表,这张表中每一天都会产生很多的log,慢慢的,这个表中数据会很多,
怎么解决呢
可以每天生成一个新表,每张表以当天日期作为名称,例如:
t_log_202209801
t_log_20220902
你想知道某一天的日志信息怎么办呢?
假设今天20220901,那么直接查:t_log-20220901的表即可。

下面是我们的t_log_20220902 的数据表信息

在这里插入图片描述

在这里插入图片描述

对应数据表的 POJO 类设计:

在这里插入图片描述

package com.rainbowsea.mybatis.pojo;


public class Log {
    private Integer id;
    private String log;
    private String time;

    public Log() {
    }


    public Log(Integer id, String log, String time) {
        this.id = id;
        this.log = log;
        this.time = time;
    }


    @Override
    public String toString() {
        return "Log{" +
                "id=" + id +
                ", log='" + log + '\'' +
                ", time='" + time + '\'' +
                '}';
    }


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLog() {
        return log;
    }

    public void setLog(String log) {
        this.log = log;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }
}

对应的LogMapper 的接口信息编写。

首先我们使用 ${} 的方式测试运行。

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;


import com.rainbowsea.mybatis.pojo.Log;

import java.util.List;

public interface LogMapper {

    /**
     * 根据日期查询不同的表,获取表中所有的日志
     *
     * @param date
     * @return
     */
    List<Log> selectAllByTable(String date);
}
<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.LogMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log">-->
    <select id="selectAllByTable" resultType="com.rainbowsea.mybatis.pojo.Log">
        select * from t_log_${date};
    </select>

</mapper>

在这里插入图片描述

package com.rainbowsea.test;

import com.rainbowsea.mybatis.mapper.LogMapper;
import com.rainbowsea.mybatis.pojo.Log;
import com.rainbowsea.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class LogMapperTest {

    @Test
    public void testSelectAllByTable() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        LogMapper mapper = sqlSession.getMapper(LogMapper.class);
        List<Log> logs = mapper.selectAllByTable("20220902");
        logs.forEach(log -> {
            System.out.println(log);
        });
    }
}

使用 ${} 拼接上的结果为:select * from t_log_20220902; 直接就是 20220902 的值,没有加单引号/双引号。

使用 #{ } 的方式是会带上 ‘’ 单引号的。

在这里插入图片描述

使用 #{} 拼接上的结果为:select * from t_log_‘20220902’; 加上的单引号后拼接上去,t_log_20220902是一个数据表名的,加了单引号后,MySQL 就找不到该数据表了,编译无法通过了。

3.1.2 批量删除

业务背景:一次删除多条记录。

在这里插入图片描述

批量删除:一次删除多条记录
批量删除的SQL语句有两种写法:

第一种:or: delete form t_car where id =1 or id = 2 or id = 3
第二种:delete from t_car where id in(1,2,3)

假设现在使用in的方式处理,前端传过来的字符串:1, 2, 3

如果使用mybatis处理,应该使用#{} 还是 ${}

使用#{} :delete from t_user where id in(‘1,2,3’) 会加上单引号, 执行错误:1292 - Truncated incorrect DOUBLE value: ‘1,2,3’

使用${} :delete from t_user where id in(1, 2, 3),执行成功

所以我们要采用 ${} ,不加单引号的形式。

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封装汽车相关信息的pojo类,普通的Java类
 */
public interface CarMapper {

    /**
     * 批量删除,根据id
     * @param ids
     * @return
     */
    int deleteBath(String ids);
}

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">

    <!--	id 要是 namespace 对应接口上的方法名: -->
    <delete id="deleteBath">
        delete from t_car where id in(${ids})
    </delete>

</mapper>

Java程序测试,

注意因为我们这里是对数据表进行的修改操作的,所以需要提交一下数据给数据库。

这里我们删除 id为(121,122,123) 三条记录。

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.test;

import com.rainbowsea.mybatis.mapper.CarMapper;
import com.rainbowsea.mybatis.pojo.Car;
import com.rainbowsea.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class TestCarMapper {

    @Test
    public void testDeleteBath() {
        SqlSession sqlSession = SqlSessionUtil.openSession();
        CarMapper mapper = sqlSession.getMapper(CarMapper.class);
        int count = mapper.deleteBath("121,122,123");
        sqlSession.commit();
        sqlSession.close();

    }

}

如果使用 #{} 是将 使用#{} :delete from t_user where id in(‘121,122,123’) 会加上单引号, 执行错误:1292 - Truncated incorrect DOUBLE value: ‘121,122,123’

在这里插入图片描述

3.1.3 模糊查询

需求:查询小米系列的汽车。【只要品牌brand中含有小米两个字的都查询出来。】

关于模糊查询有四种方案:

  1. 方案一:‘%${brand}%’
  2. 方案二: concat(‘%’,‘${brand}’,‘%’)
  3. 方案三: count函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接的 concat(‘%’,#{brand},‘%’)
  4. 方案四: “%”#{brand}“%”, 这种方式比较常用,也避免了SQL注入的问题。
方案一:'%${brand}%'
方案二: concat('%','${brand}','%')
方案三: count函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接的
        concat('%',#{brand},'%')
方案四: "%"#{brand}"%"

四种方式:总的来说,都是为了拼接成字符串,把不是字符串的想办法拼接成字符串

3.1.3.1 使用 ${ }的方式

方案一:‘%${brand}%’

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封装汽车相关信息的pojo类,普通的Java类
 */
public interface CarMapper {




    /**
     * 根据汽车品牌进行模糊查询
     * @param brand
     * @return
     */
    List<Car> selectByBrandLike(String brand);



}

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like '%${brand}%'
    </select>

</mapper>

Java程序运行结果:

在这里插入图片描述

方案二: concat(‘%’,‘${brand}’,‘%’)

在这里插入图片描述

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like concat('%','${brand}','%')
    </select>
</mapper>

在这里插入图片描述

3.1.3.2 使用 #{ } 的方式

方案三: count函数,这个是mysql数据库当中的一个函数,专门进行字符串拼接的 concat(‘%’,#{brand},‘%’)

在这里插入图片描述

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like concat('%',#{brand},'%')
    </select>
</mapper>

在这里插入图片描述

方案四: “%”#{brand}“%”, 这种方式比较常用,也避免了SQL注入的问题。

在这里插入图片描述

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--	id 要是 namespace 对应接口上的方法名: -->
    <!--    这样因为:数据表的字段名和我们定义POJO类的属性名不一致,要将二者保持一致,用 关键字 as 定义别名的方式保持一致-->
    <select id="selectByBrandLike" resultType="com.rainbowsea.mybatis.pojo.Car">
        select
            id,
            car_num as carNum,
            brand,
            guide_price as guidePrice,
            produce_time as produceTime,
            car_type as carType
        from t_car
        where brand like "%"#{brand}"%"
    </select>
</mapper>

测试运行:

在这里插入图片描述

4. typeAliases 别名定义的使用

我们来观察一下CarMapper.xml中的配置信息:

在这里插入图片描述

resultType 属性用来指定查询结果集的封装类型,这个名字太长,可以起别名吗?可以。

mybatis-config.xml 文件中使用 typeAliases 标签来起别名,包括两种方式:

4.1 typeAliases 的第一种方式:typeAlias

在这里插入图片描述

    <!--    起别名-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias>
    </typeAliases>
<?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>
    <!--    起别名-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" alias="Car"></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log" alias="Log"></typeAlias>
    </typeAliases>

    <environments default="mybatis">

        <environment id="mybatis">
            <!--            MANAGED 没有用第三框架管理的话,都是会被提交的,没有事务上的管理了 -->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="MySQL123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
        <mapper resource="CarMapper.xml"></mapper>
        <mapper resource="LogMapper.xml"></mapper>
    </mappers>
</configuration>
  • 首先要注意typeAliases标签的放置位置,如果不清楚的话,可以看看错误提示信息。

  • typeAliases标签中的typeAlias可以写多个。

  • typeAlias:

    • type属性:指定给哪个类起别名
    • alias属性:别名。
      • alias属性不是必须的,如果缺省的话,type属性指定的类型名的简类名作为别名。
      • alias是大小写不敏感的。也就是说假设alias=“Car”,再用的时候,可以CAR,也可以car,也可以Car,都行、

注意:<namespace = “”>接口,一定要为全限定类名(带有包名),不可以用别名机制

测试:

在这里插入图片描述

在这里插入图片描述

特别的: 省略 alias 属性之后,别名就是类的简名了,比如 :com.rainbowsea.mybatis.pojo.Car 的别名就是 Car/CAR/cAR,不区分大小写的。

在这里插入图片描述

    <!--    起别名-->
    <!--            省略alias 属性之后,别名就是类的简名了,比如:com.rainbowsea.mybatis.pojo.Car 的别名就是 Car/CAR/cAR
     不区分大小写的。-->
    <typeAliases>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Car" ></typeAlias>
        <typeAlias type="com.rainbowsea.mybatis.pojo.Log"></typeAlias>
    </typeAliases>

4.2 typeAliases 的第二种方式:package

如果一个包下的类太多,每个类都要起别名,会导致 typeAlias 标签配置较多,所以mybatis用提供package的配置方式,只需要指定包名,该包下的所有类都自动起别名,别名就是简类名。并且别名不区分大小写。

注意:<namespace = “”>接口,一定要为全限定类名(带有包名),不可以用别名机制

这种方式是最常用的。

在这里插入图片描述

<!--    别名 -->
    <typeAliases>
        <!--使用 <package>	还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写-->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>

测试:

在这里插入图片描述

注意:

使用 <package> 还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写

,所有的别名不区分大小写。但是:namespace 不能使用别名机制

同时需要按照一定的顺序放置,放到指定的顺序当中去

5. mappers 路径设置的使用

SQL映射文件的配置方式包括四种:

  • resource:从类路径中加载
  • url:从指定的全限定资源路径中加载
  • class:使用映射器接口实现类的完全限定类名
  • package:将包内的映射器接口实现全部注册为映射器

5.1 mapper 标签下的 resource 属性的使用

这种方式是从类路径中加载配置文件,所以这种方式要求SQL映射文件必须放在resources目录下或其子目录下。

<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

在这里插入图片描述

5.2 mapper 标签下的 url 属性的使用

这种方式是一种绝对路径的方式,这种方式不要求配置文件必须放到类的路径当中,哪里都行,只要提供了一个绝对路径就行,这种方式使用极少,因为移植性太差了(并不是所以的系统都有盘符的说法的)。

在这里插入图片描述

**需要注意的是:要三个\\\ ,才表示两个 \\ **

<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>

5.3 mapper 标签下的 class 属性的使用

Class: 这位置提供的是 mapper 接口的全限定接口名,必须带有包名(就是要一定要和对应接口的路径是一致的,一致的,一致的)

如果使用这种方式必须满足以下条件:

  • SQL映射文件和mapper接口放在同一个目录下。
  • SQL映射文件的名字也必须和mapper接口名一致。
        <mapper class="com.rainbowsea.mybatis.mapper.CarMapper"><mapper>
        如果你class指定是:com.rainbowsea.mybatis.mapper.CarMapper
        那么mybatis框架会自动去com/rainbowsea/mybatis/mapper/CarMapper/的目录下找,注意是 / 
注意:也就是说,如果你采用这种方式,那么你必须保证:CarMapper.xml文件和CarMapper接口必须在同一个目录下,并且名字也是一致的

CarMapper接口——》CarMapper.xml
LogMapper接口——> LogMapper.xml

提醒:
在IDEA的resources 目录下新建多重目录的话,必须是这样创建:
com/rianbowsea/mybatis/mapper/
不然是
com.rianbowsea.mybatis.mapper 这是建包了

在这里插入图片描述

在这里插入图片描述

保持一致的同时,名称也要是一致的

在这里插入图片描述

    <mappers>
        <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
       <mapper class="com.rainbowsea.mybatis.mapper.CarMapper"></mapper>
       <mapper class="com.rainbowsea.mybatis.mapper.LogMapper"></mapper>
    </mappers>

测试运行:

运行程序:正常!!!

在这里插入图片描述

5.4 package 标签的使用

如果class较多,可以使用这种package的方式,但前提条件和上一种(mapper 标签下的class 属性的使用)方式一样。

如果使用这种方式必须满足以下条件:

  • SQL映射文件和mapper接口放在同一个目录下。
  • SQL映射文件的名字也必须和mapper接口名一致。

这种方式是最常用的。

    <!-- 这里也是可以使用 package 包名扫描,但是同样的:对应接口路径要一致,接口名一致-->
    <package name="com.rainbowsea.mybatis.mapper"></package>

在这里插入图片描述

运行测试:

在这里插入图片描述

6. 在 IDEA 中自定义配置文件模板

mybatis-config.xml 和 SqlMapper.xml ,logback 文件可以在IDEA中提前创建好模板,以后通过模板创建配置文件。

在这里插入图片描述

在这里插入图片描述

mybatis 核心配置文件的模板内容

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

logback 的日志配置模板内容

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

SqLMapper的配置模板内容:

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="">

<!--	id 要是 namespace 对应接口上的方法名: -->
	
</mapper>

7. 补充:插入数据时获取自动生成的主键的值

**前提是:主键是自动生成的。 **

业务背景:一个用户有多个角色。

在这里插入图片描述

插入一条新的记录之后,自动生成了主键,而这个主键需要在其他表中使用时。

插入一个用户数据的同时需要给该用户分配角色:需要将生成的用户的id插入到角色表的user_id字段上。

第一种方式:可以先插入用户数据,再写一条查询语句获取id,然后再插入user_id字段。【比较麻烦】

第二种方式:mybatis提供了一种方式更加便捷。

<!--
	userGeneratedKeys = "true" 使用自动生成的主键值
	keyProperty="id" 指定主键值赋值给对象的哪个属性,这个就表示将主键值给Car对象的 id属性。
	注意:这个 keyProperty 指定的值,一定要和对应上的 pojo 对象类上的属性一致,不然,不行的
	-->
	<insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" >
		insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
	</insert>

userGeneratedKeys = “true” 使用自动生成的主键值,false 则是不使用自动生成的主键值了
keyProperty=“id” 指定主键值赋值给POJO类(ORM映射)对象的哪个属性,这个就表示将主键值给Car对象的 id属性。
注意:这个 keyProperty 指定的值,一定要和对应上的 pojo 对象类上的属性一致,不然,不行的。

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.mybatis.mapper;

import com.rainbowsea.mybatis.pojo.Car;

import java.util.List;

/**
 * 封装汽车相关信息的pojo类,普通的Java类
 */
public interface CarMapper {

    /**
     * 插入 Car 信息,并且使用生成的主键值
     * @param car
     * @return
     */
    int insertCarUserGeneratedKey(Car car);

}

<?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">

<!--namespace 一定要是:对应的接口的全限定类名-->
<mapper namespace="com.rainbowsea.mybatis.mapper.CarMapper">
    <!--
	userGeneratedKeys = "true" 使用自动生成的主键值
	keyProperty="id" 指定主键值赋值给对象的哪个属性,这个就表示将主键值给Car对象的 id属性。
	注意:这个 keyProperty 指定的值,一定要和对应上的 pojo 对象类上的属性一致,不然,不行的
	-->
    <insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id">
        insert into t_car
        values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
    </insert>
</mapper>

运行测试:

在这里插入图片描述

在这里插入图片描述

8. 总结:

  1. #{ } 与 ${ } 的区别和使用
- #{ } :先编译 SQL 语句,再对占位符传值,底层是 PrepareedStatement 实现,可以防止 SQL 注入,比较常用
- ${ }:先进行SQL语句的拼接,然后再对SQL语句进行编译,底层是 Statement 实现,这种方式存在 SQL注入现象,SQL注入的风险,简单的说就是,直接将传入的值拼接为了SQL语句,然后再执行的)。只有在需要进行SQL语句关键字拼接的情况下才会用到。
- 简单的说一个区别就是:#{} 传的值是带有 '' 单引号的,而 ${} 传的值是(直接就是值,没有单引号,或者是双引号,两个都没有)

  1. 什么情况下必须使用 ${ }
拼接表名
批量删除
模糊查询
  1. typeAliases 别名定义的使用,注意:<namespace = “”>接口,一定要为全限定类名(带有包名),不可以用别名机制 ,建议采用第二种 package 方式,,同时注意:typeAliases 的正确顺序和位置,可以参考报错信息,进行修正。
<!--    别名 -->
    <typeAliases>
        <!--使用 <package>	还可以将这个包下的所有的类的全部自动起别名,别名就是简名,不区分大小写-->
        <package name="com.rainbowsea.mybatis.pojo"/>
    </typeAliases>
  1. mappers 路径设置的使用 ,建议采用 package 标签的方式,注意条件,两个条件的一致性。
- SQL映射文件和mapper接口放在同一个目录下。
- SQL映射文件的名字也必须和mapper接口名一致。
  1. 在 IDEA 中自定义配置文件模板
  2. 插入数据时获取自动生成的主键的值,**前提是:在对应数据表中的主键是自动生成的(自增的方式才行)。 **
<!--
	userGeneratedKeys = "true" 使用自动生成的主键值
	keyProperty="id" 指定主键值赋值给对象的哪个属性,这个就表示将主键值给Car对象的 id属性。
	注意:这个 keyProperty 指定的值,一定要和对应上的 pojo 对象类上的属性一致,不然,不行的
	-->
	<insert id="insertCarUserGeneratedKey" useGeneratedKeys="true" keyProperty="id" >
		insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
	</insert>

9. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

童心与美食的邂逅,蒙自源邀你共绘梦想画卷

激情夏日所带来的热情如同孩子们的梦想一样炽热而澎湃。为了庆祝六一儿童节&#xff0c;从5月25日起&#xff0c;蒙自源旗下各大门店准备了一系列的活动&#xff0c;以迎接这个属于孩子们的特别日子。 特别活动期间&#xff0c;蒙自源特意为孩子们推出了一系列独具特色的美食。…

Cobalt_Strike(CS)渗透工具安装使用到免杀上线

Cobalt_Strike&#xff08;CS&#xff09;安装到免杀上线 原文链接&#xff1a; cs免杀上线 点我 https://mp.weixin.qq.com/s?__bizMzkxNDY5NzMxNw&amp;mid2247483862&amp;idx1&amp;snc6b4da3ce5772a075431098227397baa&amp;chksmc16b3cdcf61cb5ca06f61513…

Flutter开发效率提升1000%,Flutter Quick教程之在特定位置插入Widget

当我们要将Widget插入一个Column,Row或者Listview等有多个子元素的Widget的时候&#xff0c;有两种情况&#xff0c;一种是顺序插入&#xff0c;一种是非顺序插入。顺序插入就是Widget的排列顺序和插入顺序相同&#xff0c;非顺序插入则不是。 一&#xff0c;顺序插入。如图所…

树莓派通过PCA9685控制FT2331M舵机(Python)

很久之前整过PWM舵机&#xff0c;刚好最近师弟需要&#xff0c;并且网上现有教程不是很完整&#xff0c;就整理一下。方便交流以及后面回顾。 首先要明确&#xff0c;在这个控制方式中需要用到哪些方面&#xff1a; 1、树莓派与PCA9685之间使用I2C通信 2、PCA9685通讯协议 3…

牛客网刷题 | BC113 数字三角形

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

【Text2SQL 论文】DBCopilot:将 NL 查询扩展到大规模数据库

论文&#xff1a;DBCopilot: Scaling Natural Language Querying to Massive Databases ⭐⭐⭐⭐ Code: DBCopilot | GitHub 一、论文速读 论文认为目前的 Text2SQL 研究大多只关注具有少量 table 的单个数据库上的查询&#xff0c;但在面对大规模数据库和数据仓库的查询时时却…

RHEL7.9修改分区

系统RHEL7.9 他因为安装软件&#xff0c;需要修改分区 进入超级用户root&#xff0c;输入lsblk 查看分区&#xff0c;可见465.8G系统盘sda下有三个物理卷&#xff0c;其中sda3下/home有410.6G&#xff0c;需要这部分拆分出200G软件和100G的数据库分区 备份/home 目录下文件 c…

自动化办公02 用openpyxl库操作excel.xlsx文件(新版本)

目录 一、文件读操作 二、文件写操作 三、修改单元格样式 openpyxl 是一个处理Excel表格的第三方库。openpyxl 库可以处理Excel2010以后的电子表格格式&#xff0c;包括&#xff1a;xlsx/xlsm/xltx/xltm。 openpyxl教程 一、文件读操作 工作簿(workbook): excel文件 工作表…

LNMP网站架构部署

目录 一、LNMP架构部署&#xff08;源码编译安装&#xff09; ①实验准备 ②安装nginx服务 ③安装mysql服务&#xff0c;配置文件 ④安装php服务&#xff0c;修改配置文件 ⑤验证 静态页面测试访问 动态页面测试访问 调用数据库测试 二、LNMP架构应用实例 1.论坛网站…

南京观海微电子---简单驱动电路设计用NMOS防反接,性价比比较高?

来看看NMOS的防反保护电路有什么不同&#xff1f; 简单的栅极驱动电路设计&#xff0c;我们会使用NMOS来作防反电路&#xff0c;原因是成本较低。 PMOS一般会放置在电路的高边&#xff0c;NMOS则是在低边放置。两者的功能类似。不过&#xff0c;NMOS的防反结构&#xff0c;它…

..\MYLIB\modbus.c(49): error: #84: invalid combination of type specifiers

在keil中添加相应的文件出现以下问题时 ..\MYLIB\modbus.c(49): error: #84: invalid combination of type specifiers 是由于&#xff1a;在定义的函数体的前面有一个变量类型

攻防世界---misc---心仪的公司

1、题目描述 2、下载附件是一个流量包 方法一&#xff1a; 1、用winhex分析&#xff0c;ctrlf搜索flag 2、尝试将搜索到的flag拿去提交&#xff0c;但是不对 3、担心flag不是长flag&#xff0c;做题多了你就会发现有些flag会是fl4g这种&#xff0c;为了可以稍微全面一点&…

笔试训练2

牛客.单词搜索 刚开始我就想是搜索&#xff0c;但是不清楚bfs还是dfs更好&#xff0c;我尝试了bfs但是队列存东西&#xff0c;没有我想象的那么好写&#xff0c;所以我决定试试dfs import java.util.*;public class Solution {static int m 0;static int n 0;static int […

【AIGC】FaceChain:发挥生成式内容的无限可能性

基于图像生成的个性化肖像框架 摘要 FaceChaine提供了一系列的生成方案&#xff0c;通过少量的图像输入&#xff0c;就能生成逼真的个性化肖像。它是一个个性化肖像生成框架&#xff0c;包含丰富的人脸感知相关的模型&#xff0c;例如人脸检测&#xff0c;深度人脸向量提取&am…

【算法】合并两个有序链表(easy)——递归算法

题解&#xff1a;合并两个有序链表(easy)——递归求解 目录 1.题目2.题解3.参考代码4.总结 1.题目 题目链接&#xff1a;LINK 2.题解 本题有两种解法&#xff0c; 一是用循环去处理 链接&#xff1a;【刷题记录】合并两个有序数组、移除元素二是用递归去处理 将在下面中说…

23、linux系统文件和日志分析

linux文件系统与日志分析 文件时存储在硬盘上的&#xff0c;硬盘上的最小存储单位是扇区&#xff0c;每个扇区大大小是512字节。 inode&#xff1a;元信息&#xff08;文件的属性 权限&#xff0c;创建者&#xff0c;创建日期等&#xff09; block&#xff1a;块&#xff0c…

Java 22的FFM API,比起Java 21的虚拟线程

哪个对Java未来的发展影响更大&#xff1f;两个 Java 版本中的重要特性&#xff1a;Java 21 的虚拟线程和 Java 22 的 FFM API。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给…

fintuning chatglm3

chatglm3介绍 ChatGLM3-6B 是 ChatGLM 系列最新一代的开源模型&#xff0c;在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上&#xff0c;ChatGLM3-6B 引入了如下特性&#xff1a; 更强大的基础模型&#xff1a; ChatGLM3-6B 的基础模型 ChatGLM3-6B-Base 采用…

2、浮动的用法特点,解决父元素高度塌陷解决

一、浮动 用法&#xff1a;浮动就是使用float样式&#xff0c;使元素脱离文档流。属性值有三个&#xff1a;none默认left right 特点&#xff1a; 常用于文字环绕图片浮动的元素脱离文档流影响其他元素排列造成父元素高度塌陷 1、一旦元素设置了浮动&#xff0c;元素就会脱离…

【教程】20个高级 Python 函数,让你编程更高效

在Python的编程世界中,函数是我们编写代码的重要工具之一。除了常见的内置函数外,Python还提供了许多强大而有趣的高级函数,它们可以帮助我们简化代码、提升效率,甚至在某些情况下让编程变得更加有趣。让我们一起来探索这些高级函数的奇妙之处吧! 1.enumerate() – 枚举函…