Mybatis 入门及实践

news2024/11/17 15:34:41

Mybatis 入门及实践

文章目录

  • Mybatis 入门及实践
  • 前言
  • 一. 简介
  • 二. 入手案例
    • 2.1 前置准备
      • 2.1.1 Maven依赖
      • 2.1.2 sql准备
      • 2.1.3 Log4j2 配置
        • 1. Maven引入
        • 2. log4j2.xml
    • 2.2 代码流程构建步骤
      • 2.2.1 Mybatis前置知识
      • 2.2.2 步骤流程
      • 2.2.3 代码实现
  • 三. XML映射器
    • ==前置代码==
    • 3.1 select
      • 3.1.1 元素属性
      • 3.1.2 无参查询
        • 1. 查询全部记录,通过List形式回参
        • 2. 查询全部记录,通过Map形式回参
      • 3.1.3 传参查询
        • 1.单个参数类型传参
        • 2.多个基础数据类型map集合传参
        • 3.引用类型传参
    • 3.2 insert, update&delete
      • ==前置==
      • 3.2.1 元素属性
      • 3.2.2 insert
      • 3.2.3 update
      • 3.2.4 delete
    • 3.3 ResultMap - 关联查询
      • ResultMap里的标签
        • 1. constructor
        • 2. association(一对一)
        • 3.collection(一对多)
        • 4. discriminator
        • 5. 多对多查询
    • 3.4 存储过程调用
  • 四. Mybatis 代理模式开发
    • 4.1 简介
    • 4.2 代理接口编写
      • 4.2.1 步骤流程
      • 4.2.2 代码实现
    • 4.2 crud代码实现
    • 4.3 深入查询
      • 4.3.1 传参
        • 1. 通过单个基本数据类型查询
        • 2. 通过多个基本数据类型查询
        • 3. 通过实体类查询
        • 4. 通过多个实体类查询
        • 5. 通过map集合查询
      • 4.3.2 模糊查询
    • 4.4 动态sql
      • 4.4.1 if
      • 4.4.2 choose, when&otherwise
      • 4.4.3 trim, where&set
      • 4.4.4 foreach
      • 4.4.5 sql&bind
    • 4.5 注解 - 增删改查
      • 4.5.1 步骤流程
      • 4.5.2 代码实现
  • 五. 遇见的异常
    • 5.1 异常一:Type interface com.zhanghp.dao.mapper.DemoMapper is already known to the MapperRegistry.
    • 5.2 异常二:Cannot define both nestedQueryId and nestedResultMapId in property dept
    • 5.3 异常三:Mapped Statements collection does not contain value for queryMultiToMulti

前言

⭐️ 代码地址:https://gitee.com/zhp1221/java/tree/master/lab_04_mybatis


⭐️ 这里sql准备了2个文件,为什么分2个库来练习?

因为笔者来练习实现不同数据库之间的使用。原理是切换environment配置。所以大家请自行创建2个数据库,sql文件名为数据库的名称,分别是deep_practicetest

ConnectOptionalEnvironmentUtil :通过这个工具类的编写,传入environment指定的id(environment在mybatis-config.xml文件里配置),就可实现另一个数据库的使用

ResultMapSqlDemo :对ConnectOptionalEnvironmentUtil的使用

⭐️ 如果本文章对你们有帮助,记得给笔者点个赞,编写不易!

一. 简介

Mybatis - 官方文档

​ MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

二. 入手案例

2.1 前置准备

2.1.1 Maven依赖

	<dependencies>
        <!-- mysql数据库链接 -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <!-- mybatis orm层依赖 -->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.11</version>
        </dependency>

        <!-- junit 测试依赖 -->
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!-- 简化java臃肿代码依赖包 -->
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <!--hutool 工具集合引入-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.7</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>

2.1.2 sql准备

-- 建表
CREATE TABLE `demo` (
    `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
    `name` varchar(10) DEFAULT NULL COMMENT '姓名',
    `age` int DEFAULT NULL COMMENT '年龄',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='mybatis - demo表';

-- 测试数据
INSERT INTO `demo` (`id`, `name`, `age`) VALUES (1, '张三', 18);
INSERT INTO `demo` (`id`, `name`, `age`) VALUES (2, '李四', 20);

2.1.3 Log4j2 配置

用于打印sql,方便查看自己所构建的sql语句运行过程

1. Maven引入

		<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.12.1</version>
        </dependency>

2. log4j2.xml

resources包下创建

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <loggers>
        <Logger name="com.zhanghp" level="debug">
        </Logger>
    </loggers>
</Configuration>

2.2 代码流程构建步骤

2.2.1 Mybatis前置知识

Mybatis的核心类是SqlSessionFactory,我们需要通过此类来建立数据库连接,并实现增删改查的代码编写。

SqlSessionFactory实例需通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder则通过XML/Configuration的实例来构建。这里笔者采用的是XML构建SqlSessionFactoryBuilder

另一种Configuration实例化,读者可通过访问官网链接,进行详细的了解。

2.2.2 步骤流程

  1. SqlSessionFactory实例代码编写:数据库与java代码相连的桥梁
  2. mybatis-config.xml配置:进行SqlSessionFacotryBuilder实例创建
  3. 数据库对应的实体类Demo编写:接收及传递对象
  4. DemoMapper.xml编写:进行数据库的增删改查
  5. 通过Junit来测试编写的代码

2.2.3 代码实现

目录结构

在这里插入图片描述

  1. SqlSessionFactory实例代码编写

    /**
     * XML 中构建 SqlSessionFactory
     *
     * @author zhanghp
     * @date 2023/6/25 14:24
     */
    public class ConnectionDBWithXml {
    
        /**
         * xml配置路径
         */
        private static final String RESOURCE = "mybatis-config.xml";
        /**
         * 连接数据库的工厂类
         */
        private static final SqlSessionFactory SQL_SESSION_FACTORY;
    
        static {
            InputStream resourceAsStream = null;
            try {
                // MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
                resourceAsStream = Resources.getResourceAsStream(RESOURCE);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 初始化SqlSessionFactory
            SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(resourceAsStream);
        }
    
        public void connect() {
            // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
            try (SqlSession session = SQL_SESSION_FACTORY.openSession()) {
                // DemoMapper.xml中的select的id:selectOneRecord
                Demo record = session.selectOne("selectOneRecord");
                System.out.println(record);
                System.out.println("---------------------------------");
                List<Demo> recordList = session.selectList("selectDemo");
                if (ObjectUtil.isNotEmpty(record)) {
                    recordList.forEach(System.out::println);
                }
            }
        }
    }
    
  2. mybatis-config.xml 配置

    我这里创建了一个jdbc.properties文件,目的是为了方便统一管理数据库。

    在mybatis-config.xml文件中,可通过properties属性,来引用该文件,并在环境配置中,通过**${}**来引用jdbc.properties里的属性。

    jdbc.properties

    jdbc_driver=com.mysql.cj.jdbc.Driver
    jdbc_url=jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true
    jdbc_username=root
    jdbc_password=zhp.1221
    

    mybatis-config.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>
        <!-- 引入外部文件配置 -->
        <properties resource="jdbc.properties"/>
        <!-- 类型别名 -->
        <typeAliases>
            <package name="com.zhanghp.dao.pojo"/>
        </typeAliases>
        <!-- 环境配置 -->
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <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>
    
        <mappers>
            <!-- 扫描xml路径 -->
            <mapper resource="mappers/DemoMapper.xml"/>
        </mappers>
    </configuration>
    
  3. 实体类Demo

    /**
     * demo表 - 实体类
     *
     * @author zhanghp
     * @date 2023/6/25 14:50
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Demo implements Serializable {
        private Integer id;
    
        private String name;
    
        private Integer age;
    }
    
  4. DemoMapper.xml

    <?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.zhanghp.dao.pojo.Demo">
    
        <!-- 查询全部 -->
        <select id="selectDemo" resultType="demo">
            select * from demo
        </select>
    
        <!-- 查询单个 -->
        <select id="selectOneRecord" resultType="demo">
            select * from demo where id = 1
        </select>
    </mapper>
    
  5. UT测试用例

    DemoTest.java

    /**
     * @author zhanghp
     * @date 2023/6/25 15:00
     */
    public class DemoTest {
    
        @Test
        public void testConnectAndSelectOneRecord(){
            final ConnectionDBWithXml connectionDBWithXml = new ConnectionDBWithXml();
            connectionDBWithXml.connect();
        }
    }
    
  6. 结果打印

    Demo(id=1, name=张三, age=18)
    ---------------------------------
    Demo(id=1, name=张三, age=18)
    Demo(id=2, name=李四, age=20)
    

三. XML映射器

前置代码

ConnectUtil.java

抽离SqlSession实例化,及释放,方便外部类调用

/**
 * 连接数据库工具类
 *
 * @author zhanghp
 * @date 2023/6/25 23:53
 */
public class ConnectUtil {

    /**
     * xml配置路径
     */
    private static final String RESOURCE = "mybatis-config.xml";

    public ConnectUtil() {
    }

    /**
     * 获取SqlSession
     *
     * @return {@link org.apache.ibatis.session.SqlSession}
     */
    public static SqlSession getSqlSession() {
        InputStream resourceAsStream = null;
        try {
            // MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。
            resourceAsStream = Resources.getResourceAsStream(RESOURCE);
        } catch (IOException e) {
            e.printStackTrace();
        }
		// 初始化SqlSessionFactory:连接数据库的工厂类
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 增删改需要手动提交事务
        // openSession(true);设置是否自动提交事务,true为自动提交,false为不自动提交(例子:修改方法)
         // Spring整合后,就不在需要手动处理事务
        return sqlSessionFactory.openSession(true);
    }

    /**
     * 释放资源
     *
     * @param session {@link org.apache.ibatis.session.SqlSession}
     */
    public static void release(SqlSession session) {
        session.close();
    }
}

3.1 select

3.1.1 元素属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
parameterType用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。

3.1.2 无参查询

1. 查询全部记录,通过List形式回参

DemoMapper.xml

  	<!-- 查询全部 -->
    <select id="selectDemo" resultType="demo">
        select *
        from demo
    </select>

SelectDemo.java

    /**
     * 获取全部记录,通过List返回
     */
    public void queryAll() {
        try {
            List<Demo> demoList = SQLSESSION.selectList("selectDemo");
            if (IterUtil.isNotEmpty(demoList)) {
                demoList.forEach(System.out::println);
            }
        } finally {
            // 释放资源
            ConnectUtil.release(SQLSESSION);
        }
    }

UT测试用例 - SelectTest.java

  	@Test
    public void queryAll() {
        // select * from demo
        SELECT_DEMO.queryAll();
    }

2. 查询全部记录,通过Map形式回参

DemoMapper.xml

    <!-- 查询全部 返回Map集合 -->
    <select id="queryReturnMap" resultType="map">
        select *
        from demo
    </select>

SelectDemo.java

	/**
     * 获取全部记录,通过Map返回
     */
    public void queryReturnMap() {
        try {
            // 第二参数为mapKey:需指定主键名称
            Map<Integer, Demo> demoMap = SQLSESSION.selectMap("queryReturnMap", "id");
            if (MapUtil.isNotEmpty(demoMap)) {
                demoMap.entrySet().forEach(it -> {
                    System.out.println(it.getKey() + ":" + it.getValue());
                });
            }
        } finally {
            // 释放资源
            ConnectUtil.release(SQLSESSION);
        }
    }

UT测试用例 - SelectTest.java

	@Test
    public void queryReturnMap() {
        // sql: select * from demo
        SELECT_DEMO.queryReturnMap();
    }

3.1.3 传参查询

1.单个参数类型传参

DemoMapper.xml

	<!-- 通过主键id查询单个记录 -->
    <select id="queryById" parameterType="int" resultType="demo">
        select * from demo where id = #{id}
    </select>

SelectDemo.java

/**
 * select demo
 * 
 * @author zhanghp
 * @date 2023/6/25 23:30
 */
public class SelectDemo {

    /**
     * 获取SqlSession
     */
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();

    /**
     * 通过主键id传参获取对应的一条记录
     *
     * @param id 主键id
     */
    public void queryById(Integer id) {
        try {
            // select
            Demo demo = SQLSESSION.selectOne("queryById", 1);
            System.out.println(demo);
        } finally {
            // 释放资源
            ConnectUtil.release(SQLSESSION);
        }
    }
}

UT测试用例 - SelectTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 0:04
 */
public class SelectTest {

    private final SelectDemo SELECT_DEMO = new SelectDemo();

    @Test
    public void queryById() {
        SELECT_DEMO.queryById(1);
    }
}

2.多个基础数据类型map集合传参

DemoMapper.xml

	<!-- 通过map查询匹配记录集合 -->
    <select id="queryByMap" parameterType="map" resultType="demo">
        select * from demo where name = #{name} and age= #{age}
    </select>

SelectDemo.java

  	/**
     * 通过map传参获取记录集合
     *
     * @param map {@link java.util.Map}
     */
    public void queryByMap(Map<String, Object> map) {
        try {
            // select
            List<Demo> demoList = SQLSESSION.selectList("queryByMap", map);
            if (IterUtil.isNotEmpty(demoList)) {
                demoList.forEach(System.out::println);
            }
        } finally {
            // 释放资源
            ConnectUtil.release(SQLSESSION);
        }

    }

UT测试用例 - SelectTest.java

    @Test
    public void queryByMap(){
        Map<String, Object> map = new HashMap<>();
        map.put("name", "张三");
        map.put("age", 18);
        SELECT_DEMO.queryByMap(map);
    }

3.引用类型传参

DemoMapper.xml

    <!-- 通过对象demo查询匹配记录集合 -->
    <select id="queryByDemo" parameterType="demo" resultType="demo">
        select * from demo where age= #{age}
    </select>

SelectDemo.java

	/**
     * 通过对象传参获取集合
     *
     * @param demo {@link com.zhanghp.dao.pojo.Demo}
     */
    public void queryByDemo(Demo demo) {
        try {
            List<Demo> demoList = SQLSESSION.selectList("queryByDemo", demo);
            if (IterUtil.isNotEmpty(demoList)) {
                demoList.forEach(System.out::println);
            }
        } finally {
            // 释放资源
            ConnectUtil.release(SQLSESSION);
        }
    }

UT测试用例 - SelectTest.java

	@Test
    public void queryByDemo(){
        final Demo demo = new Demo();
        demo.setAge(20);
        SELECT_DEMO.queryByDemo(demo);
    }

3.2 insert, update&delete

前置

insert update&delete需要手动提交事务,来进行对数据库记录的操作,设置如下

ConnectUtil.java

// 增删改需要手动提交事务
// openSession(true);设置是否自动提交事务,true为自动提交,false为不自动提交(例子:修改方法)
// Spring整合后,就不在需要手动处理事务
// 或者不设置true,当执行完增删改,可使用sqlSession.commit(),手动提交事务
sqlSessionFactory.openSession(true);

3.2.1 元素属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。

3.2.2 insert

DemoMapper.xml

	 <!-- 插入一条记录 -->
    <insert id="addOne">
        insert into demo
        values (100, '测试', 30)
    </insert>

    <!-- 通过对象传参,插入一条记录 -->
    <insert id="addOneByDemo" parameterType="demo">
        insert into demo
        values (#{id}, #{name}, #{age})
    </insert>

    <!-- 通过对象传参,使用主键自增,插入一条记录 -->
    <insert id="addOneByDemoUseGenerateKey" parameterType="demo" useGeneratedKeys="true" keyProperty="id">
        insert into demo
        values (#{id}, #{name}, #{age})
    </insert>

InsertDemo.java

/**
 * 插入
 *
 * @author zhanghp
 * @date 2023/6/26 8:31
 */
public class InsertDemo {
    /**
     * 获取SqlSession
     */
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();

    /**
     * 插入固定数据
     */
    public void addOne() {
        try{
            final int rows = SQLSESSION.insert("addOne");
            System.out.println("插入成功的行数:" + rows);
        }finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    /**
     * 通过对象作为参数传递,新增一条记录
     */
    public void addOneByDemo(Demo demo) {
        try{
            final int rows = SQLSESSION.insert("addOneByDemo", demo);
            System.out.println("插入成功的行数:" + rows);
        }finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    /**
     * 通过对象作为参数传递,使用数据库主键自增策略,实现新增一条记录
     */
    public void addOneByDemoUseGenerateKey(Demo demo) {
        try{
            final int rows = SQLSESSION.insert("addOneByDemo", demo);
            System.out.println("插入成功的行数:" + rows);
        }finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
}

UT测试用例 - InsertTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 8:34
 */
public class InsertTest {
    private final InsertDemo insertDemo = new InsertDemo();

    @Test
    public void addOne(){
        // sql: insert into demo values (100, '测试', 30)
        insertDemo.addOne();
    }

    @Test
    public void addOneByDemo(){
        // sql: insert into demo values (4, '小胡', 30)
        insertDemo.addOneByDemo( new Demo(4, "小胡", 30));
    }

    @Test
    public void addOneByDemoUseGenerateKey(){
        // sql: insert into demo values (null, '小李', 30)
        insertDemo.addOneByDemoUseGenerateKey( new Demo(null, "小李", 40));
    }
}

3.2.3 update

DemoMapper.xml

 	 <!-- 更新一条记录 -->
    <update id="updateOne" parameterType="demo">
        update demo
        set name = '小陈',
            age  = 18
        where id = 1
    </update>

    <!-- 通过对象传参,更新记录 -->
    <update id="updateByDemo" parameterType="demo">
        update demo
        set name =#{name},
            age  = #{age}
        where id = #{id}
    </update>

UpdateDemo.java

/**
 * 更新
 *
 * @author zhanghp
 * @date 2023/6/26 8:31
 */
public class UpdateDemo {
    /**
     * 获取SqlSession
     */
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();

    /**
     * 固定更新一条数据
     */
    public void updateOne() {
        try {
            final int rows = SQLSESSION.insert("updateOne");
            System.out.println("更新成功的行数:" + rows);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    /**
     * 通过传入的对象,通过主键,更新其余的属性
     */
    public void updateByDemo(Demo demo) {
        try {
            final int rows = SQLSESSION.update("updateByDemo", demo);
            System.out.println("更新成功的行数:" + rows);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
}

UT测试用例 - UpdateTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 8:34
 */
public class UpdateTest {
    private final UpdateDemo updateDemo = new UpdateDemo();

    @Test
    public void updateOne() {
        // sql: update demo set name = '小陈', age = 18 where id = 1
        updateDemo.updateOne();
    }

    @Test
    public void updateByDemo(){
        // sql: update demo set name = '小胡', age = 50 where id = 4
        updateDemo.updateByDemo(new Demo(4, "小胡", 50));
    }
}

3.2.4 delete

DemoMapper.xml

	<!-- 删除一条记录 -->
    <delete id="deleteOne">
       delete from demo where id = 101
    </delete>

    <!-- 通过主键id删除一条记录 -->
    <delete id="deleteOneById" parameterType="int">
        delete from demo where id = #{id}
    </delete>

DeleteDemo.java

/**
 * 删除
 *
 * @author zhanghp
 * @date 2023/6/26 8:31
 */
public class DeleteDemo {
    /**
     * 获取SqlSession
     */
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();

    /**
     * 删除固定一条记录
     */
    public void deleteOne() {
        try{
            final int rows = SQLSESSION.delete("deleteOne");
            System.out.println("删除成功的行数:" + rows);
        }finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    /**
     * 通过主键id删除固定一条记录
     */
    public void deleteOneById(Integer id) {
        try{
            final int rows = SQLSESSION.delete("deleteOneById", id);
            System.out.println("删除成功的行数:" + rows);
        }finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
}

UT测试用例 - DeleteTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 8:34
 */
public class DeleteTest {
    private final DeleteDemo deleteDemo = new DeleteDemo();

    @Test
    public void deleteOne(){
        // sql:  delete from demo where id = 101
        deleteDemo.deleteOne();
    }

    @Test
    public void deleteOneById(){
        // sql:  delete from demo where id = 100
        deleteDemo.deleteOneById(100);
    }
}

3.3 ResultMap - 关联查询

ResultMap里的标签

属性功能
constructor用于在实例化类时,注入结果到构造方法中
association一个复杂类型的关联(一对一)
collection一个复杂类型的集合(一对多)
discriminator使用结果值来决定使用哪个 resultMap

resultmap简单使用

resultMapSqlDemo.java

/**
 * @author zhanghp
 * @date 2023/6/27 8:45
 */
public class ResultMapSqlDemo {
    private final String ENVIRONMENT = "deep_practice";
    private final SqlSession SQLSESSION = ConnectOptionalEnvironmentUtil.getSqlSession(ENVIRONMENT);

    /**
     * resultMap基本使用
     */
    public void queryResultMap() {
        try {
            // test1 : MyBatis 会在幕后自动创建一个 ResultMap,若属性名和列名不匹配,会返回null
            final List<Emp> results1 = SQLSESSION.selectList("queryResultType");
            CommonUtil.printList(results1);
            // test2 : resultMap 映射后,会重新自动转换名称不一致问题
            final List<Emp> results2 = SQLSESSION.selectList("queryResultMap");
            CommonUtil.printList(results2);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
}

mapper/EmpMapper.xml


    <resultMap id="deptResultMap" type="dept">
        <id property="deptno" column="DEPTNO"></id>
        <result property="dname" column="DNAME"></result>
        <result property="loc" column="LOC"></result>
    </resultMap>

    <resultMap id="empResultMap" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
    </resultMap>

    <!-- 查询全部 resultType -->
    <select id="queryResultType" resultType="emp">
        select *
        from emp
    </select>

    <!-- 查询全部 resultMap-->
    <select id="queryResultMap" resultMap="empResultMap">
        select *
        from emp
    </select>

UT用例 - ResultMapSqlTest.java

/**
 * @author zhanghp
 * @date 2023/7/2 21:47
 */
public class ResultMapSqlTest {

    private final ResultMapSqlDemo DEMO = new ResultMapSqlDemo();

    @Test
    public void queryResultMap(){
        DEMO.queryResultMap();
    }
}

1. constructor

用于在实例化类时,注入结果到构造方法中

可以这么理解,通过类里写的有参构造器,来进行参数的传递

ps:

要注意构造器入参的顺序,顺序和constructor指定的属性名顺序不同会导致数据为空

属性功能
idArgID 参数;标记出作为 ID 的结果可以帮助提高整体性能
arg将被注入到构造方法的一个普通结果

ResultMapSqlDemo.java

 	/**
     * 通过<constructor/>元素查询
     */
    public void queryConstructor() {
        try {
            // test1 : MyBatis 会在幕后自动创建一个 ResultMap,若属性名和列名不匹配,会返回null
            final List<Dept> results = SQLSESSION.selectList("queryConstructor");
            CommonUtil.printList(results);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

Dept.java

/**
 * @author zhanghp
 * @date 2023/6/28 9:25
 */
@Data
@ToString
@NoArgsConstructor
public class Dept implements Serializable {
    private Integer deptno;
    private String dname;
    private String loc;

    // 组合一个Emp的List集合作为属性
    private List<Emp> empList;

    public Dept(Integer deptno, String dname, List<Emp> empList) {
        this.deptno = deptno;
        this.dname = dname;
        this.empList = empList;
    }

    public Dept(Integer deptno, String dname, String loc) {
        this.deptno = deptno;
        this.loc = loc;
        this.dname = dname;
    }

}

mappers/DeptMapp.xml

  <resultMap id="deptResultMap" type="dept">
        <!-- 按照有参构造器,依次注入,若参数顺序不一致,可通过构造器调整参数位置,来达到效果-->
        <constructor>
            <idArg column="deptno" javaType="Integer"/>
            <arg column="dname" javaType="String"/>
            <!-- 通过deptno传参,传入constructorQuery方法,来进行参数-->
            <arg select="com.zhanghp.dao.mapper.EmpMapper.constructorQuery" javaType="List" column="deptno" />
        </constructor>
        <result property="loc" column="LOC"/>
    </resultMap>
    <!-- 查询全部 通过resultMap constructor标签 -->
    <select id="queryConstructor" resultMap="deptResultMap">
        select *
        from dept
    </select>

UT用例 - ResultMapSql.java

	@Test
    public void queryConstructor(){
        DEMO.queryConstructor();
    }

2. association(一对一)

关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。 你需要指定目标属性名以及属性的javaType(很多时候 MyBatis 可以自己推断出来),在必要的情况下你还可以设置 JDBC 类型,如果你想覆盖获取结果值的过程,还可以设置类型处理器。

关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

resultMapSqlDemo.java

	/**
     * 一对一
     */
    public void queryAssociation() {
        try {
            final List<Emp> results1 = SQLSESSION.selectList("queryAssociation");
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    /**
     * 一对一
     * 引用select,引用外部的id
     */
    public void queryAssociation2() {
        try {
            // association对应的select属性查询dept表,是一个一个sql查询,比较消耗时间
            final List<Emp> results1 = SQLSESSION.selectList("queryAssociation2");
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
    
    /**
     * 一对一
     * 引用select,引用内部的id
     */
    public void queryAssociation3() {
        try {
            // association对应的select属性查询dept表,是一个一个sql查询,比较消耗时间
            final List<Emp> results1 = SQLSESSION.selectList("queryAssociation3");
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

mapper/EmpMapper.xml

  <!-- 一对一查询 -->
    <resultMap id="oneToOne" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
        <association property="deptObject" javaType="dept" resultMap="deptResultMap"/>
    </resultMap>

    <resultMap id="oneToOne2" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
        <association property="deptObject" javaType="dept" column="DEPTNO"
                     select="com.zhanghp.dao.mapper.DeptMapper.queryByDeptno"/>
    </resultMap>

    <resultMap id="oneToOne3" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
        <association property="deptObject" javaType="dept" column="DEPTNO" select="queryDeptByDeptno"/>
    </resultMap>

    <select id="queryDeptByDeptno" resultMap="deptResultMap">
        select *
        from dept
        where DEPTNO = #{deptno}
    </select>

    <select id="queryAssociation" resultMap="oneToOne">
        select *
        from emp e
                 left join dept d on (e.DEPTNO = d.DEPTNO)
    </select>

    <select id="queryAssociation2" resultMap="oneToOne2">
        select *
        from emp
    </select>

    <select id="queryAssociation3" resultMap="oneToOne3">
        select *
        from emp
    </select>

UT用例 - ResultMapSqlTest.java

@Test
    public void oneToOne(){
        DEMO.queryAssociation();
        DEMO.queryAssociation2();
        DEMO.queryAssociation3();
    }

3.collection(一对多)

resultMapSqlDemo.java

 	/**
     * 一对多查询
     */
    public void queryOneToMulti() {
        try {
            final List<Dept> results1 = SQLSESSION.selectList("queryOneToMulti");
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

mapper/DeptMapper.xml

	<!-- 一对多查询 -->
    <resultMap id="oneToMulti" type="dept">
        <id property="deptno" column="DEPTNO"></id>
        <result property="dname" column="DNAME"></result>
        <result property="loc" column="LOC"></result>
        <collection property="empList" ofType="emp">
            <id property="empno" column="EMPNO"/>
            <result property="name" column="ENAME"/>
            <result property="job" column="JOB"/>
            <result property="mgr" column="MGR"/>
            <result property="hiredate" column="HIREDATE"/>
            <result property="sal" column="SAL"/>
            <result property="comm" column="COMM"/>
            <result property="deptno" column="DEPTNO"/>
        </collection>
    </resultMap>

    <select id="queryOneToMulti" resultMap="oneToMulti">
        select * from dept d left join emp e on (d.DEPTNO = e.DEPTNO)
    </select>

UT用例 - ResultMapSqlTest.java

	@Test
    public void oneToMulti(){
        DEMO.queryOneToMulti();
    }

4. discriminator

有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。 鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。

用的场景较少,没写相关示例,我这里查了相关文章示例,有需要的请自行观看

官网例子
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4yWJTuA-1688311124998)(Mybatis 入门及实践.assets/image-20230702223112677.png)]

https://blog.csdn.net/weixin_45799972/article/details/121657855

https://www.jianshu.com/p/e808b1cfb265


5. 多对多查询

Sql表准备

CREATE TABLE `project` (
  `pid` int NOT NULL AUTO_INCREMENT,
  `pname` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `money` int DEFAULT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

CREATE TABLE `project_record` (
  `empno` int NOT NULL,
  `pid` int NOT NULL,
  PRIMARY KEY (`empno`,`pid`) USING BTREE,
  KEY `fk_project_pro` (`pid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

实例查询原理:

一个project对应多个project_record,而每个project_record记录都对应一个emp表

resultMapSqlDemo.java

	/**
     * 多对多查询
     */
    public void queryMultiToMulti() {
        try {
            final List<Project> results1 = SQLSESSION.selectList("queryMultiToMulti");
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

mapper/ProjectMapper.xml

   <!-- 多对多查询 -->
    <resultMap id="empResultMap" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
    </resultMap>

    <resultMap id="multiToMulti" type="project">
        <id property="pid" column="pid"/>
        <result property="pname" column="pname"/>
        <result property="money" column="money"/>
        <collection property="projectRecords" ofType="projectRecord">
            <!-- 多主键必须都写id属性,否则查询的结果会是一对一-->
            <id property="pid" column="pid"/>
            <id property="empno" column="empno"/>
            <association property="empObject" javaType="emp" resultMap="empResultMap"/>
        </collection>
    </resultMap>

    <select id="queryMultiToMulti" resultMap="multiToMulti">
        select *
        from project p
                 left join project_record pr on (p.pid = pr.pid)
                 left join emp e on (pr.empno = e.empno)
    </select>

UT用例 - ResultMapSqlTest.java

	@Test
    public void multiToMulti(){
        DEMO.queryMultiToMulti();
    }

3.4 存储过程调用

sql准备

CREATE DEFINER=`root`@`localhost` PROCEDURE `getEmpAndDept`(IN deptno int(11))
BEGIN
select * from emp e left join dept d on (e.deptno = d.deptno) where e.deptno = deptno;
END

resultMapSqlDemo.java

	/**
     * 调用存储过程
     */
    public void callProcedure(Integer deptno) {
        try {
            // association对应的select属性查询dept表,是一个一个sql查询,比较消耗时间
            final List<Emp> results1 = SQLSESSION.selectList("callProcedure", deptno);
            CommonUtil.printList(results1);
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

mapper/EmpMapper.xml

jdbcType:指定数据库该字段类型,mybatis包含的jdbcType请自行上官网查看(在XML配置-结果映射章节)

mode:INOUTINOUT 参数。如果参数的 modeOUTINOUT,将会修改参数对象的属性值,以便作为输出参数返回。 如果 modeOUT(或 INOUT),而且 jdbcTypeCURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 引用来将结果集 ResultMap 映射到参数的类型上。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap

	<!--  调用存储过程  -->
    <select id="callProcedure" resultMap="oneToOne" statementType="CALLABLE">
        {call getEmpAndDept(#{dept,jdbcType=INTEGER,mode=IN})}
    </select>

UT用例 - ResultMapSqlTest.java

 	@Test
    public void callProcedure(){
        DEMO.callProcedure(20);
    }

四. Mybatis 代理模式开发

4.1 简介

传统的XML配置映射器的方式,实现对数据库的增删改查,是基于SqlSession实例中的Api进行完成,虽说基需都能满足,但还是有很多劣势

  1. selectOne(),selectAll(),seletMap(),update(),insert(),delete()等,都是基于SqlSession完成,规定的传参格式很单一,若传参多个参数,需要集成到map,对象等。
  2. 由xml配置,需要额外实例化SqlSession,而没直接提供数据库操作接口,不利于后期维护

Mybatis的代理模式开发,通过Mapper代理(或称接口绑定)的操作方式,相对传统的XML映射器,具有以下优点

  1. 接口规范统一,方便模块间的调用,利于维护
  2. 传参多样化,不拘束于传统单一的传参方式

4.2 代理接口编写

4.2.1 步骤流程

  1. 创建DemoMapper.java:用于Mapper接口代理开发,统一访问该类,调用api实现对数据库的增删改查
  2. 创建DemoMapper.xml(和DemoMapper.java同路径包下):用于sql的编写
  3. mybatis-config.xml增加对com.zhanghp.dao.mapper包路径下文件的扫描配置

这里需要注意几个点

  • Mapper接口绑定类的名称,需和映射文件名称保持一致(不包含拓展名)

  • Mapper映射文件的namespace为Mapper接口的全路径包名

  • sql语句对应的id,需和Mapper接口的方法名保持一致

  • Mapper接口和Mapper映射文件编译后会在target的同一目录下


ps1:

拥有两个DemoMapper.xml文件,并不冲突,这里说明一下:

  1. 包路径不同
  2. namespace的作用域对象不同,一个是作用的是Demo类,另一个作用的是DemoMapper接口类

ps2:

创建映射文件包路径com.zhanghp.dao.mapper时,需一个一个名称的文件夹创建,若直接文件夹名写成com.zhanghp.dao.mapper,并回车后,会出现找不到该包的异常,这里需要注意一下。

4.2.2 代码实现

目录结构

这里读者会发现,笔者创建的DemoMapper.java和DemoMapper.xml前有2个小图标,这里读者若也想实现该效果

请自行去"settings" -> “plugins”,搜索MyBatisX插件,下载并重启idea即可复现图标效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J6eWHiRE-1688311124999)(Mybatis 入门及实践.assets/image-20230626141024103.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xodzNeOv-1688311124999)(Mybatis 入门及实践.assets/image-20230626135315121.png)]

  1. 创建一个接口类DemoMapper.java

    /**
     * @author zhanghp
     * @date 2023/6/26 10:45
     */
    public interface DemoMapper {
    }
    
  2. 创建DemoMapper.xml

    <?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接口的全路径包名 -->
    <mapper namespace="com.zhanghp.dao.mapper.DemoMapper">
    
    </mapper>
    
  3. mybatis-config.xml添加配置

    	<mappers>
            <!-- 扫描xml路径 -->
            <mapper resource="mappers/DemoMapper.xml"/>
            <!-- 扫描代理接口模式类包路径 -->
            <package name="com.zhanghp.dao.mapper"></package>
        </mappers>
    

4.2 crud代码实现

所用到的CommonUtil.java代码

上面xml映射器的代码,一个一个手动写代码,打印集合数据,很费时间,这里笔者写了一个公共类,来进行统一编写实现List,Map的集合打印

/**
 * @author zhanghp
 * @date 2023/6/26 13:31
 */
public class CommonUtil {
    /**
     * List打印
     *
     * @param list 集合
     */
    public static void printList(List<?> list) {
        if (IterUtil.isNotEmpty(list)) {
            list.forEach(System.out::println);
        }
    }

    /**
     * Map打印
     *
     * @param map 集合
     */
    public static void printMap(Map<?, ?> map) {
        if (MapUtil.isNotEmpty(map)) {
            map.entrySet().forEach(it -> {
                System.out.println(it.getKey() + ":" + it.getValue());
            });
        }
    }
}

DemoMapper.java

/**
 * @author zhanghp
 * @date 2023/6/26 10:45
 */
public interface DemoMapper {
    /**
     * 查询全部记录
     *
     * @return {@link com.zhanghp.dao.pojo.Demo}
     */
    List<Demo> queryAll();

    /**
     * 通过Demo对象传参,新增一条记录
     *
     * @param demo 所需插入的对象信息
     * @return 插入成功的行数
     */
    int insertOneByDemo(@Param("demo") Demo demo);

    /**
     * 通过Demo对象的传参及指定的id,来进行更新记录
     *
     * @param demo 所需修改的对象信息
     * @return 修改成功的行数
     */
    int updateOneByDemo(@Param("demo") Demo demo);

    /**
     * 根据主键id删除指定的记录
     *
     * @param id 主键id
     * @return 删除成功的行数
     */
    int deleteOneById(@Param("id") Integer id);
}

DemoMapper.xml(com.zhanghp.dao.mapper路径下)

<?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接口的全路径包名 -->
<mapper namespace="com.zhanghp.dao.mapper.DemoMapper">

    <!-- 查询全部数据记录 -->
    <select id="queryAll" resultType="demo">
        select * from demo
    </select>

    <!-- 通过对象传参,新增记录 -->
    <insert id="insertOneByDemo" parameterType="demo" useGeneratedKeys="true" keyProperty="id">
        insert into demo values (#{demo.id}, #{demo.name}, #{demo.age})
    </insert>

    <!-- 通过对象传参,更新记录 -->
    <update id="updateOneByDemo" parameterType="demo">
        update demo
        set name =#{demo.name},
            age  = #{demo.age}
        where id = #{demo.id}
    </update>

    <!-- 通过主键id删除一条记录 -->
    <delete id="deleteOneById" parameterType="int">
        delete
        from demo
        where id = #{id}
    </delete>

</mapper>

SelectMapperDemo.java

/**
 * @author zhanghp
 * @date 2023/6/26 13:30
 */
public class SelectMapperDemo {
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();
    private final DemoMapper MAPPER = SQLSESSION.getMapper(DemoMapper.class);

    public void queryAll() {
        CommonUtil.printList(MAPPER.queryAll());
    }

    public void insertOneByDemo(Demo demo) {
        System.out.println(MAPPER.insertOneByDemo(demo));
    }

    public void updateOneByDemo(Demo demo) {
        System.out.println(MAPPER.updateOneByDemo(demo));
    }

    public void deleteOneById(Integer id) {
        System.out.println(MAPPER.deleteOneById(id));
    }
}

UT测试用例 - SelectMapperTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 13:35
 */
public class SelectMapperTest {
    private final SelectMapperDemo DEMO = new SelectMapperDemo();
    @Test
    public void queryAll(){
        // select * from demo
        DEMO.queryAll();
    }

    @Test
    public void insertOneByDemo(){
        // insert into demo values (null, '小刘', 22)
        DEMO.insertOneByDemo(new Demo(null, "小刘", 22));
    }

    @Test
    public void updateOneByDemo(){
        // update demo set name = '小贺', age = '11' where id = 1
        DEMO.updateOneByDemo(new Demo(1, "小贺", 11));
    }

    @Test
    public void deleteOneById() {
        // delete from demo where id = 1
        DEMO.deleteOneById(1);
    }
}

4.3 深入查询

4.3.1 传参

1. 通过单个基本数据类型查询

SelectByParamDemo.java

/**
 * 参数传递的几种查询方式
 *
 * @author zhanghp
 * @date 2023/6/26 21:09
 */
public class SelectByParamMapperDemo {
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();
    private final DemoMapper MAPPER = SQLSESSION.getMapper(DemoMapper.class);

    /**
     * 通过单个基本数据类型查询
     *
     * @param name 姓名
     */
    public void selectBySingleType(String name) {
        final List<Demo> demos = MAPPER.selectBySingleType(name);
        CommonUtil.printList(demos);
    }
}

DemoMapper.xml

 	<select id="selectBySingleType" parameterType="string" resultType="demo">
        select * from demo where name = #{name}
    </select>

UT测试用例 - SelectByParamMapperTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 23:22
 */
public class SelectByParamMapperTest {
    private final SelectByParamMapperDemo DEMO= new SelectByParamMapperDemo();

    @Test
    public void selectBySingleType(){
        DEMO.selectBySingleType("小白");
    }
}

2. 通过多个基本数据类型查询

SelectByParamDemo.java

 	/**
     * 通过多个基本数据类型查询
     *
     * @param name 姓名
     * @param age  年龄
     */
    public void selectByMultiType(String name, Integer age) {
        final List<Demo> demos = MAPPER.selectByMultiType(name, age);
        CommonUtil.printList(demos);
    }

DemoMapper.xml

   	<select id="selectByMultiType" resultType="demo">
        select * from demo where name = #{name} and age = #{age}
    </select>

UT测试用例 - SelectByParamMapperTest.java

    @Test
    public void selectByMultiType(){
        DEMO.selectByMultiType("小白", 10);
    }

3. 通过实体类查询

SelectByParamDemo.java

 	/**
     * 通过实体类查询
     *
     * @param demo 实体类
     */
    public void selectByBean(Demo demo) {
        final List<Demo> demos = MAPPER.selectByBean(demo);
        CommonUtil.printList(demos);
    }

DemoMapper.xml

 	<select id="selectByBean" parameterType="demo" resultType="demo">
        select * from demo where name = #{demo.name} and age = #{demo.age}
    </select>

UT测试用例 - SelectByParamMapperTest.java

	@Test
    public void selectByBean(){
        DEMO.selectByBean(new Demo(null, "小白", 10));
    }

4. 通过多个实体类查询

SelectByParamDemo.java

 	/**
     * 通过多个实体类查询
     *
     * @param demo01 实体类1
     * @param demo02 实体类2
     */
    public void selectByMultiBean(Demo demo01, Demo demo02) {
        final List<Demo> demos = MAPPER.selectByMultiBean(demo01, demo02);
        CommonUtil.printList(demos);
    }

DemoMapper.xml

  	<select id="selectByMultiBean"  resultType="demo">
        select * from demo where name = #{demo01.name} and age = #{demo02.age}
    </select>

UT测试用例 - SelectByParamMapperTest.java

	@Test
    public void selectByMultiBean(){
        DEMO.selectByMultiBean(new Demo(null, "小白", null),
                new Demo(null, null, 10));
    }

5. 通过map集合查询

SelectByParamDemo.java

	/**
     * 通过map集合查询
     *
     * @param map map集合
     */
    public void selectByMap(Map<String, Object> map) {
        final List<Demo> demos = MAPPER.selectByMap(map);
        CommonUtil.printList(demos);
    }

DemoMapper.xml

	<select id="selectByMap"  parameterType="map" resultType="demo">
        select * from demo where name = #{map.name} and age = #{map.age}
    </select>

UT测试用例 - SelectByParamMapperTest.java

 	@Test
    public void selectByMap(){
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("name", "小白");
            put("age", 10);
        }};
        DEMO.selectByMap(map);
    }

4.3.2 模糊查询

通过concat函数,进行实现模糊查询

FuzzySelectMapperDemo

/**
 * 模糊查询
 *
 * @author zhanghp
 * @date 2023/6/26 21:45
 */
public class FuzzySelectMapperDemo {
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();
    private final DemoMapper MAPPER = SQLSESSION.getMapper(DemoMapper.class);

    public void fuzzyQuery(String fuzzyName){
        final List<Demo> demos = MAPPER.fuzzyQuery(fuzzyName);
        CommonUtil.printList(demos);
    }
}

DemoMapper.xml

 	<select id="fuzzyQuery" parameterType="string" resultType="demo">
        select * from demo where name like concat('%', #{name}, '%')
    </select>

UT测试用例 - FuzzySelectMapperTest

/**
 * @author zhanghp
 * @date 2023/6/26 23:09
 */
public class FuzzySelectMapperTest {
    private final FuzzySelectMapperDemo DEMO = new FuzzySelectMapperDemo();

    @Test
    public void fuzzyQuery(){
        DEMO.fuzzyQuery("小");
    }
}

4.4 动态sql

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

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

4.4.1 if

判断字符串:1. 不为null 2. 不为空字符串

判断对象里的String类型属性:1. 对象不为空 2. 对象里的属性不为null 且 不是空字符串

DynamicSqlDemo.java

/**
 * @author zhanghp
 * @date 2023/6/27 18:54
 */
public class DynamicSqlDemo {
    private final SqlSession SQLSESSION = ConnectUtil.getSqlSession();
    private final DemoMapper MAPPER = SQLSESSION.getMapper(DemoMapper.class);

    public void dynamicIf() {
        try {
            // test1 select * from demo where 1=1 and name = ?
            CommonUtil.printList(MAPPER.dynamicIf("小胡"));
            // test2 select * from demo where 1=1
            CommonUtil.printList(MAPPER.dynamicIf(null));
            // test3 <if test = "name != null and name != ''">
            // select * from demo where 1=1
            CommonUtil.printList(MAPPER.dynamicIf(""));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }
    
     public void dynamicIfByDemo() {
        try {
            // select * from demo where 1=1 and name = ?
            CommonUtil.printList(MAPPER.dynamicIfByDemo(new Demo(null, "小胡", null)));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    public void dynamicIfFuzzy() {
        try {
            // select * from demo where 1=1 and name like concat('%','小','%')
            CommonUtil.printList(MAPPER.dynamicIfFuzzy("小"));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

}

DemoMapper.xml

  <select id="dynamicIf" parameterType="string" resultType="demo">
        select * from demo
        where 1=1
        <if test="name != null and name != ''">
            and name = #{name}
        </if>
    </select>

    <select id="dynamicIfByDemo" parameterType="demo" resultType="demo">
        select * from demo
        where 1=1
        <if test="demo != null and demo.name != null and demo.name != ''">
            and name = #{demo.name}
        </if>
    </select>

    <select id="dynamicIfFuzzy" parameterType="string" resultType="demo">
        select * from demo
        where 1=1
        <if test="name != null">
            and name like concat('%', #{name}, '%')
        </if>
    </select>

UT测试用例 - DynamicSqlTest.java

/**
 * @author zhanghp
 * @date 2023/6/27 19:07
 */
public class DynamicSqlTest {
    private final DynamicSqlDemo DEMO = new DynamicSqlDemo();

    @Test
    public void dynamicIf(){
        DEMO.dynamicIf();
    }

    @Test
    public void dynamicIfByDemo(){
        DEMO.dynamicIfByDemo();
    }

    @Test
    public void dynamicIfFuzzy(){
        DEMO.dynamicIfFuzzy();
    }
}

4.4.2 choose, when&otherwise

choose:前面的when条件成立 后面的 when就不再判断了

DynamicSqlDemo.java

 public void dynamicChooseWhenAndOtherwise() {
        try {
            // test1 select * from demo where 1=1 and age = ?
            CommonUtil.printList(MAPPER.dynamicChooseWhenAndOtherwise(new Demo(null, null, 10), ""));
            // test2 select * from demo where 1=1 and name = ?
            CommonUtil.printList(MAPPER.dynamicChooseWhenAndOtherwise(new Demo(null, null, null), "小胡"));
            // test3 select * from demo where 1=1 and name like concat('%','小','%')
            CommonUtil.printList(MAPPER.dynamicChooseWhenAndOtherwise(new Demo(null, null, null), ""));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

DemoMapper.xml

    <select id="dynamicChooseWhenAndOtherwise" resultType="demo">
        select * from demo where 1=1
        <choose>
            <when test="demo != null and demo.age != null">
                and age = #{demo.age}
            </when>
            <when test="name != null and name != ''">
                and name = #{name}
            </when>
            <otherwise>
                and name like concat('%','小','%')
            </otherwise>
        </choose>
    </select>

UT测试用例 - DynamicSqlTest.java

 	@Test
    public void dynamicChooseWhenAndOtherwise(){
        DEMO.dynamicChooseWhenAndOtherwise();
    }

4.4.3 trim, where&set

wherewhere 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。


trim

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>
属性功能
prefix增加的前缀
prefixOverrides去除的前缀
suffix增加的后缀
suffixOverrides去除的后缀

DynamicSqlDemo.java

	  public void dynamicWhere() {
        try {
            // select * from demo
            CommonUtil.printList(MAPPER.dynamicWhere(""));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    public void dynamicTrim() {
        try {
            // select * from demo where 1=1
            CommonUtil.printList(MAPPER.dynamicTrim());
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    public void dynamicSet() {
        try {
            // test1 update demo SET age = ? where id = ?
            System.out.println(MAPPER.dynamicSet(11, 2));
            //test2 报错:age=null 所以无法更新  update demo where id = 2
            System.out.println(MAPPER.dynamicSet(null, 2));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

DemoMapper.xml

  <select id="dynamicWhere"  parameterType="string" resultType="demo">
        select * from demo
        <where>
            <if test="name != null and name != ''">
                name = #{name}
            </if>
        </where>
    </select>

    <select id="dynamicTrim"  resultType="demo">
        select * from demo
        <trim prefix="where">
            1=1
        </trim>
    </select>

    <update id="dynamicSet">
        update demo
        <set>
            <if test="age != null">age = #{age}</if>
        </set>
        where id = #{id}
    </update>

UT测试用例 - DynamicSqlTest.java

	@Test
    public void dynamicWhere(){
        DEMO.dynamicWhere();
    }

    @Test
    public void dynamicTrim(){
        DEMO.dynamicTrim();
    }

    @Test
    public void dynamicSet(){
        DEMO.dynamicSet();
    }

4.4.4 foreach

属性功能
collection遍历的集合或者是数组,参数是数组。collection中名字指定为array,参数是List集合。collection中名字指定为list
separator遍历时,采用的分隔符
open以什么开头
close以什么结尾
item中间变量名

DynamicSqlDemo.java

   public void dynamicForeach() {
        try {
            // select * from demo where id in ( ? , ? , ? )
            CommonUtil.printList(MAPPER.dynamicForeach(Arrays.asList(1, 2, 3)));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

DemoMapper.xml

  <select id="dynamicForeach" parameterType="list" resultType="demo">
        select * from demo
        where id in
        <foreach collection="list" item="it" separator="," open="(" close=")">
            #{it}
        </foreach>
    </select>

UT测试用例 - DynamicSqlTest.java

	@Test
    public void dynamicForeach(){
        DEMO.dynamicForeach();
    }

4.4.5 sql&bind

sql:如果引用sql标签的话用的refid

bind

属性功能
name名称
value要使用绑定的值,通过#{name}来使用

DynamicSqlDemo.java

 	public void dynamicSql() {
        try {
            // select name, age from demo
            CommonUtil.printList(MAPPER.dynamicSql());
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

    public void dynamicBind() {
        try {
            // select * from demo where name like ?
            CommonUtil.printList(MAPPER.dynamicBind(new Demo(null, "小", null)));
        } finally {
            ConnectUtil.release(SQLSESSION);
        }
    }

DemoMapper.xml

    <sql id="sql_property">name, age</sql>
    <sql id="sql_select">select <include refid="sql_property"/> from demo</sql>
    <select id="dynamicSql" resultType="demo">
        <include refid="sql_select"></include>
    </select>

    <select id="dynamicBind" parameterType="demo" resultType="demo">
        <!-- 2种绑定方式都可获取到name值-->
        <bind name="pattern" value="'%' + demo.name + '%'"/>
        <bind name="pattern2" value="'%' + demo.getName() + '%'"/>
        select * from demo
        where name like #{pattern3}
    </select>

UT测试用例 - DynamicSqlTest.java

    @Test
    public void dynamicBind(){
        DEMO.dynamicBind();
    }

4.5 注解 - 增删改查

在Mapper接口绑定类上,通过@Select,@Insert,@Update,@Delete等注解进行完成增删改查,更详细的注解使用,访问官网。笔者这里编写的是基本的增删改查操作。

4.5.1 步骤流程

  1. 创建一个DemoAnnotationMapper.java
  2. 在该接口类里编写crud,并添加注释
  3. 测试用例

4.5.2 代码实现

DemoAnnotationMapper.java

/**
 * @author zhanghp
 * @date 2023/6/26 22:32
 */
public interface DemoAnnotationMapper {

    /**
     * 查询全部
     *
     * @return {@link com.zhanghp.dao.pojo.Demo}
     */
    @Select("select * from demo")
    List<Demo> queryAll();

    /**
     * 通过主键查询单条记录
     *
     * @return {@link com.zhanghp.dao.pojo.Demo}
     */
    @Select("select * from demo where id = #{id}")
    Demo queryById(@Param(value = "id") Integer id);

    /**
     * 通过map匹配对应的对象集合
     *
     * @return {@link com.zhanghp.dao.pojo.Demo}
     */
    @Select("select * from demo where name = #{map.name} and age = #{map.age}")
    List<Demo> queryByMap(@Param(value = "map") Map<String, Object> map);

    /**
     * 通过对象指定的信息查询所匹配的集合
     *
     * @return {@link com.zhanghp.dao.pojo.Demo}
     */
    @Select("select * from demo where name = #{demo.name} and age = #{demo.age}")
    List<Demo> queryByDemo(@Param(value = "demo") Demo demo);

    /**
     * 通过map插入
     *
     * @param map 信息
     * @return 插入成功的行数
     */
    @Insert("insert into demo values (#{map.id}, #{map.name}, #{map.age})")
    int insertByMap(@Param(value = "map") Map<String, Object> map);

    /**
     * 通过对象插入
     *
     * @param demo 实体类
     * @return 插入成功行数
     */
    @Insert("insert into demo values (#{demo.id}, #{demo.name}, #{demo.age})")
    int insertByDemo(@Param(value = "demo") Demo demo);

    /**
     * 通过指定的几个参数修改
     *
     * @param name 姓名
     * @param age  年龄
     * @param id   主键
     * @return 修改成功的行数
     */
    @Update("update demo set name = #{name}, age = #{age} where id = #{id}")
    int updateByParams(@Param(value = "name") String name,
                       @Param(value = "age") Integer age,
                       @Param(value = "id") Integer id);

    /**
     * 通过主键删除对应的记录
     *
     * @param id 主键
     * @return 删除成功行数
     */
    @Delete("delete from demo where id = #{id}")
    int deleteById(@Param(value = "id") Integer id);
}

UT测试用例 - DemoAnnotationTest.java

/**
 * @author zhanghp
 * @date 2023/6/26 11:21
 */
public class DemoTest {
    final SqlSession SQLSESSION = ConnectUtil.getSqlSession();
    final DemoAnnotationMapper mapper = SQLSESSION.getMapper(DemoAnnotationMapper.class);

    @Test
    public void queryAll() {
        CommonUtil.printList(mapper.queryAll());
    }

    @Test
    public void queryById() {
        System.out.println(mapper.queryById(1));
    }

    @Test
    public void queryByMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "小陈");
        map.put("age", 18);
        CommonUtil.printList(mapper.queryByMap(map));
    }

    @Test
    public void queryByDemo() {
        CommonUtil.printList(mapper.queryByDemo(new Demo(null, "小陈", 18)));
    }

    @Test
    public void insertByMap(){
        Map<String, Object> map = new HashMap<String, Object>(){{
            put("id", 200);
            put("name", "小白");
            put("age", 10);
        }};
        System.out.println(mapper.insertByMap(map));
    }

    @Test
    public void insertByDemo(){
        System.out.println(mapper.insertByDemo(new Demo(300, "小黑", 10)));
    }

    @Test
    public void updateByParams(){
        System.out.println(mapper.updateByParams("小红", 20, 300));
    }

    @Test
    public void deleteById(){
        System.out.println(mapper.deleteById(300));
    }

}

五. 遇见的异常

5.1 异常一:Type interface com.zhanghp.dao.mapper.DemoMapper is already known to the MapperRegistry.

问题原因:

这个问题就是接口无法Mapper注册,导致这个原因可能是在,我们MaBatis核心配置文件只的中的mapper和package扫包冲突导致的

解决方式:

mybatis-config.xml配置里,配置的

当指定使用的是接口绑定的方式,就不需要单独扫描xml了,改成扫描包路径

 	<mappers>
        <!--
        Exception:
          Type interface com.zhanghp.dao.mapper.DemoMapper is already known to the MapperRegistry.
<!--        <mapper resource="com/zhanghp/dao/mapper/DemoMapper2.xml"/>-->
        <!-- 扫描代理接口模式类包路径 -->
        <package name="com.zhanghp.dao.mapper"></package>
    </mappers>

5.2 异常二:Cannot define both nestedQueryId and nestedResultMapId in property dept

问题原因:

在标签里使用了seletct,指定调用DeptMapper.xml里的queryByDeptno方而在queryByDeptno也指定了resultMap,导致嵌套异常(即使queryByDeptno方法用的是resulttype也一样)

解决方式:

把association里的resultMap属性删除

下面2个文件为抛异常相关的文件,其中EmpMapper.xml是出错的文件

mappers/EmpMapper.xml

 <resultMap id="oneToOne2" type="emp">
        <id property="empno" column="EMPNO"/>
        <result property="name" column="ENAME"/>
        <result property="job" column="JOB"/>
        <result property="mgr" column="MGR"/>
        <result property="hiredate" column="HIREDATE"/>
        <result property="sal" column="SAL"/>
        <result property="comm" column="COMM"/>
        <result property="deptno" column="DEPTNO"/>
        <association property="dept" javaType="dept" column="DEPTNO" select="com.zhanghp.dao.mapper.DeptMapper.queryByDeptno" resultMap="deptResultMap"/>
    </resultMap>

mappers/DeptMapper.xml

    <select id="queryConstructor" resultMap="deptResultMap">
        select *
        from dept
    </select>

5.3 异常三:Mapped Statements collection does not contain value for queryMultiToMulti

问题原因:

映射语句集合不包含指定的queryMultiToMulti

通过这句话,可以看出,使用的是xml配置的扫描。

说明没有扫描到这个方法id,换句话说,mybatis-config.xml没有配置扫描该方法所在类的路径

解决方式:

在mybatis-config.xml中添加扫描该方法的类

 	<mappers>
        <!-- 扫描xml路径 -->
        <mapper resource="mappers/DemoMapper.xml"/>
        <mapper resource="mappers/EmpMapper.xml"/>
        <mapper resource="mappers/DeptMapper.xml"/>
        <mapper resource="mappers/ProjectMapper.xml"/>
    </mappers>

⭐️ 源码地址:https://gitee.com/zhp1221/java/tree/master/lab_04_mybatis

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

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

相关文章

【网络编程】网络编程套接字(一)

文章目录 网络编程预备知识认识端口号理解源端口号和目的端口号认识TCP协议和UDP协议网络字节序sockaddr结构1.socket 常见API2.sockaddr结构 网络编程预备知识 认识端口号 端口号(port)是传输层协议的内容. 端口号是一个2字节16位的整数; 端口号用来标识一个进程, 告诉操作系…

Linux :LNMP(源码包安装)

Linux 虚拟机 VMware&#xff1a;安装centos7_鲍海超-GNUBHCkalitarro的博客-CSDN博客 物理机 linux&#xff1a;真机安装centos linux&#xff08;突发事件&#xff1a;解决卡在安装界面&#xff09;{寻找镜像--u启制作--引导u盘--解决卡在安装界面--安装配置}_安装centos7卡…

Linux--文件/目录权限

看懂权限&#xff1a; 拥有者&#xff1a;文件属于谁 所属组&#xff1a;文件属于哪个组 other:不属于上面两种的任何一个&#xff0c;就是other 图示&#xff1a; 分析&#xff1a; ①第一列&#xff1a; d:目录 -:普通文件 p:管道文件 b:块设备 c:字符设备 l:链接…

数学建模概论

1、从现实对象到数学模型 我们常见的模型 玩具、照片......~实物模型水箱中的舰艇、风洞中的飞机......~物理模型地图、电路图、分子结构图......~符号模型 模型是为了一定目的&#xff0c;对客观事物的一部分进行简缩、抽象、提炼出来的原型的替代物&#xff0c;集中反映了原型…

【C++1】函数重载,类和对象,引用,string类,vector容器,类继承和多态,/socket

文章目录 1.函数重载&#xff1a;writetofile()&#xff0c;Ctrue和false&#xff0c;C0和非02.类和对象&#xff1a;vprintf2.1 构造函数&#xff1a;对成员变量初始化2.2 析构函数&#xff1a;一个类只有一个&#xff0c;不允许被重载 3.引用&#xff1a;C中&取地址&…

沁恒CH32V307VCT6最小系统板/开发板开源

沁恒CH32V307VCT6最小系统板&#xff0c;引出了所有IO口&#xff0c;一个Type-C连接到USB2.0全速OTG接口&#xff0c;一个Flash芯片 型号W25Q64 容量64Mbit 连接到SPI2接口&#xff0c;板上还有TL432电压基准1.25V(实测1.246V左右)可通过跳线连接到PC3的AD13&#xff0c;还有3.…

【c++】并行编程:cuda入门

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍cuda入门。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&#x1f95e…

测试在 4090 上运行 vicuna-33b 进行推理

测试在 4090 上运行 vicuna-33b 进行推理 今天尝试在 4090 上运行 vicuna-33b 进行推理&#xff0c;使用的是 8bit 量化。 运行命令如下&#xff0c; python3 -m fastchat.serve.cli --model-path lmsys/vicuna-33b-v1.3 --load-8bit结论&#xff0c;使用 8bit 量化在 4090 上…

HTTP概述以及Tomcat概述

HTTP 概念&#xff1a;Hyper Text Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。特点&#xff1a; 基于TCP协议&#xff1a;面向连接、安全基于请求-响应模型的&#xff1a;一次请求对应一次响应HTTP协议是无状态的协议…

Java面试题3

[TOC]目录 1. spring事务 事务简介 事务在逻辑上是一组操作&#xff0c;要么执行&#xff0c;要不都不执行。主要是针对数据库而言的&#xff0c;比如说 MySQL。 为了保证事务是正确可靠的&#xff0c;在数据库进行写入或者更新操作时&#xff0c;就必须得表现出 ACID 的 4 …

nexus搭建maven私有仓库

Nexus 在企业开发中还是比较常用的私有仓库管理工具&#xff0c;一般把公司内部的Maven jar 包或npm包上传到仓库中&#xff0c;有效的对包文件进行管理。 Nexus 至少需要 2G 以上的内存 安装nexus之前首先安装配置好 JDK 环境 和 Maven环境 1、Linux环境安装openJDK 2、Centos…

机器学习——掌握决策树ID3算法的原理,通过增益熵实现手工推导的过程。

文章目录 决策树介绍优缺点ID3算法原理举例 决策树的构建1、特征选择&#xff08;1&#xff09;香农熵&#xff08;2&#xff09;信息增益 2、决策树的生成3、决策树的修剪 总结&#xff1a;参考文献 决策树 介绍 决策树(decision tree)是一种基本的分类与回归方法。ID3是其中…

模拟实现atoi

函数简介 atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数&#xff0c;应用在计算机程序和办公软件中。int atoi(const char *nptr) 函数会扫描参数 nptr字符串&#xff0c;会跳过前面的空白字符&#xff08;例如空格&#xff0c;tab缩进&#xff09;等。如果 n…

socket 网络编程

socket UDP1、单播收发数据测试2、广播发送 TCP1、收发数据测试2、TCP 服务端 Socket是两个程序之间的双向通信链路。它是一种网络通信协议&#xff0c;用于不同计算机之间的进程间通信或同一计算机内部进程之间的通信。 import sockets socket.socket(AddressFamily, Type)…

初识【Matplotlib】

Matplotlib Matplotlib简单介绍Matploylib的分类学习Matploylib网站推荐Matplotlib用法Matplotlib中文字体显示Matplotlib的三层结构容器层图像层辅助显示层 Matplotlib简单介绍 Matplotlib是一个Python的数据可视化库&#xff0c;它提供了一种简单而有效的方式来创建各种类型…

02-独立按键控制LED状态

程序 #include <REGX52.H>void main() {while(1) {if(P3_1 0) //DOWN{P2_0 0xFE;}if(P3_1 1) //UP{P2_0 0XFF;}};}按键的抖动问题 软件实现解决-按键的抖动问题 通过延时解决 #include <REGX52.H>void Delay(unsigned int xms) {while(xms){unsigned cha…

【C语言】整型在内存中存储 赛场(来试试)

前言 &#x1f388;大家好&#xff0c;我是何小侠&#x1f388; &#x1f343;大家可以叫我小何或者小侠&#x1f343; &#x1f490;希望能通过写博客加深自己对于学习内容的理解&#x1f490; &#x1f338;也能帮助更多人理解和学习&#x1f338; 合抱之木&#xff0c;生于…

简单实用配置VScode的C# Framework环境

1. 设置window path环境csc变量 D:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\Roslyn 或者 C:\Windows\Microsoft.NET\Framework64\v4.0.30319 2. 设置"code-runner.executorMap" "csharp": "cd $dir &…

第五课—大学英语四六级备考—听力专项

Key words 1.implement vt.实施 "Implement" 在中文中的意思是「实施」或「执行」。以下是一些示例用法和搭配&#xff1a; 中文意思&#xff1a;实施、执行 形近字&#xff1a;implicate&#xff08;牵连&#xff09; 1. 用英文造句&#xff1a;The government …

【C++初阶(二)】缺省参数以及函数重载

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:C初阶之路⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 缺省参数&#xff06;函数重载 1. 前言2. 缺…