mybatis的第四天学习笔记中

9. MyBatis小技巧

9.1 #{}和${}

9.1.1 基本概念
  • #{}:先编译SQL语句,再给占位符传值,底层使用PreparedStatement实现。可以防止SQL注入,比较常用。
  • ${}:先进行SQL语句拼接,然后再编译SQL语句,底层使用Statement实现。存在SQL注入风险。只有在需要进行SQL语句关键字拼接的情况下才会用到。
9.1.2 使用场景对比
9.1.2.1 普通查询场景
xml 复制代码
<!-- 使用#{} -->
<select id="selectByCarType" resultType="Car">
    select * from t_car where car_type = #{carType}
</select>

<!-- 使用${} -->
<select id="selectByCarType" resultType="Car">
    select * from t_car where car_type = '${carType}'
</select>

执行结果对比:

  • 使用#{}时,SQL语句中会带有?占位符,例如:where car_type = ?
  • 使用${}时,SQL语句会直接拼接值,例如:where car_type = '燃油车'
9.1.2.2 排序场景
xml 复制代码
<!-- 使用${}进行排序 -->
<select id="selectAll" resultType="Car">
    select * from t_car order by carNum ${key}
</select>

注意事项:

  • 排序场景必须使用${},因为asc/desc是SQL关键字
  • 使用#{}会导致SQL语法错误,例如:order by carNum 'desc'
9.1.2.3 动态表名场景
xml 复制代码
<!-- 使用${}拼接表名 -->
<select id="selectAllByTableName" resultType="Car">
    select * from ${tableName}
</select>

使用场景:

  • 分表存储的场景,如按日期分表:t_user20220108
  • 使用#{}会导致表名被引号包围,例如:select * from 't_car'
9.1.2.4 批量删除场景
xml 复制代码
<!-- 使用${}进行批量删除 -->
<delete id="deleteBatch">
    delete from t_car where id in(${ids})
</delete>

注意事项:

  • 使用#{}会导致SQL语法错误:where id in('1,2,3')
  • 使用${}才能正确执行:where id in(1,2,3)
9.1.2.5 模糊查询场景
xml 复制代码
<!-- 使用${}进行模糊查询 -->
<select id="selectLikeByBrand" resultType="Car">
    select * from t_car where brand like '%${brand}%'
</select>

<!-- 使用#{}进行模糊查询(方式1:使用concat函数) -->
<select id="selectLikeByBrand" resultType="Car">
    select * from t_car where brand like concat('%',#{brand},'%')
</select>

<!-- 使用#{}进行模糊查询(方式2:使用双引号) -->
<select id="selectLikeByBrand" resultType="Car">
    select * from t_car where brand like "%"#{brand}"%"
</select>
9.1.3 使用原则
  • 优先使用 #{},能使用 #{} 就不用 ${}
  • 只有在以下场景使用 ${}
    1. 需要动态拼接SQL关键字(如排序)
    2. 需要动态拼接表名
    3. 需要动态拼接列名
    4. 需要批量删除时拼接in条件
    5. 需要模糊查询时拼接通配符
9.1.4 安全注意事项
  1. 使用${}时要注意SQL注入风险
  2. 对用户输入进行严格验证
  3. 必要时对参数进行转义处理
  4. 避免直接使用用户输入拼接SQL语句
9.1.5 性能考虑
  1. #{}使用预编译,性能更好
  2. ${}需要每次重新编译SQL语句
  3. 大量使用${}会影响系统性能
  4. 建议在必须使用${}的场景才使用

9.2 TypeAliases(类型别名)

9.2.1 配置方式
9.2.1.1 单个类型别名配置
xml 复制代码
<typeAliases>
    <typeAlias type="com.powernode.mybatis.pojo.Car" alias="Car"/>
</typeAliases>

特点:

  • type属性:指定要起别名的类
  • alias属性:别名(可选)
    • 如果不指定alias,则使用类的简类名作为别名
    • 别名不区分大小写
9.2.1.2 包级别别名配置
xml 复制代码
<typeAliases>
    <package name="com.powernode.mybatis.pojo"/>
</typeAliases>

特点:

  • 自动为包下所有类创建别名
  • 别名就是简类名
  • 别名不区分大小写
  • 可以配置多个包

9.3 Mappers(映射器)

9.3.1 配置方式
9.3.1.1 resource方式
xml 复制代码
<mappers>
    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
</mappers>

特点:

  • 从类路径加载配置文件
  • SQL映射文件必须放在resources目录下或其子目录
9.3.1.2 url方式
xml 复制代码
<mappers>
    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>

特点:

  • 使用绝对路径
  • 对SQL映射文件位置无要求
9.3.1.3 class方式
xml 复制代码
<mappers>
    <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>

特点:

  • SQL映射文件和mapper接口必须在同一目录
  • SQL映射文件名必须和mapper接口名一致
9.3.1.4 package方式
xml 复制代码
<mappers>
    <package name="com.powernode.mybatis.mapper"/>
</mappers>

特点:

  • 自动注册包下所有mapper接口
  • 需要满足class方式的条件

9.4 IDEA配置文件模板

建议在IDE中创建以下模板:

  1. mybatis-config.xml

    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"/>
        <settings>
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <setting name="lazyLoadingEnabled" value="true"/>
        </settings>
        <typeAliases>
            <package name=""/>
        </typeAliases>
        <environments default="dev">
            <environment id="dev">
                <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>
            <package name=""/>
        </mappers>
    </configuration>
  2. SqlMapper.xml

    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="">
    </mapper>

9.5 获取自动生成的主键

9.5.1 配置方式
xml 复制代码
<insert id="insertUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
    insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) 
    values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
9.5.2 使用场景
  • 主键是自动生成的
  • 需要在插入数据后立即使用生成的主键
  • 常见于一对多关系的数据插入
9.5.3 配置说明
  • useGeneratedKeys="true":启用自动生成主键
  • keyProperty="id":指定主键属性名

9.6 完整示例

9.6.1 项目结构
复制代码
mybatis-005-antic/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── powernode/
│   │   │           └── mybatis/
│   │   │               ├── mapper/
│   │   │               │   └── CarMapper.java
│   │   │               ├── pojo/
│   │   │               │   └── Car.java
│   │   │               └── utils/
│   │   │                   └── SqlSessionUtil.java
│   │   └── resources/
│   │       ├── com/
|   |       |    └── powernode/
|   |       |     		└── mybatis/
|		|			  |            ├── mapper/
| 	|				|								 └── CarMapper.xml
│   │       ├── jdbc.properties
│   │       ├── logback.xml
│   │       └── mybatis-config.xml
│   └── test/
│       └── java/
│           └── com/
│               └── powernode/
│                   └── mybatis/
│                       └── test/
│                           └── CarMapperTest.java
9.6.2 核心代码
9.6.2.1 pom.xml
xml 复制代码
<?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.powernode</groupId>
    <artifactId>mybatis-005-antic</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <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>
        <!--junit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <!--logback依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
</project>
9.6.2.2 jdbc.properties
properties 复制代码
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=root
9.6.2.3 SqlSessionUtil.java
java 复制代码
package com.powernode.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;

public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

    static {
        try {
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SqlSession openSession() {
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }

    public static void close(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.close();
        }
        local.remove();
    }
}
9.6.2.4 Car.java
java 复制代码
package com.powernode.mybatis.pojo;

public class Car {
    private Long id;
    private String carNum;
    private String brand;
    private Double guidePrice;
    private String produceTime;
    private String carType;
    
    // 构造方法、getter、setter、toString方法
}
9.6.2.5 CarMapper.java
java 复制代码
package com.powernode.mybatis.mapper;

import com.powernode.mybatis.pojo.Car;
import java.util.List;

public interface CarMapper {
    List<Car> selectByCarType(String carType);
    List<Car> selectAll(String ascOrDesc);
    List<Car> selectAllByTableName(String tableName);
    int deleteBatch(String ids);
    List<Car> selectLikeByBrand(String likeBrand);
    void insertUseGeneratedKeys(Car car);
}
9.6.2.6 mybatis-config.xml
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.powernode.mybatis.pojo"/>
    </typeAliases>
    <environments default="dev">
        <environment id="dev">
            <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>
        <package name="com.powernode.mybatis.mapper"/>
    </mappers>
</configuration>
9.6.2.7 CarMapper.xml
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.powernode.mybatis.mapper.CarMapper">
    <select id="selectByCarType" resultType="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>

    <select id="selectAll" resultType="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 carNum ${key}
    </select>

    <select id="selectAllByTableName" resultType="Car">
        select
            id,car_num as carNum,brand,guide_price as guidePrice,
            produce_time as produceTime,car_type as carType
        from
            ${tableName}
    </select>

    <delete id="deleteBatch">
        delete from t_car where id in(${ids})
    </delete>

    <select id="selectLikeByBrand" resultType="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>

    <insert id="insertUseGeneratedKeys" useGeneratedKeys="true" keyProperty="id">
        insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
        values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
    </insert>
</mapper>
9.6.2.8 CarMapperTest.java
java 复制代码
package com.powernode.mybatis.test;

import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;
import java.util.List;

public class CarMapperTest {
    @Test
    public void testSelectByCarType() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        List<Car> cars = mapper.selectByCarType("燃油车");
        cars.forEach(car -> System.out.println(car));
    }

    @Test
    public void testSelectAll() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAll("desc");
        cars.forEach(car -> System.out.println(car));
    }

    @Test
    public void testSelectAllByTableName() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        List<Car> cars = mapper.selectAllByTableName("t_car");
        cars.forEach(car -> System.out.println(car));
    }

    @Test
    public void testDeleteBatch() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        int count = mapper.deleteBatch("1,2,3");
        System.out.println("删除了几条记录:" + count);
        SqlSessionUtil.openSession().commit();
    }

    @Test
    public void testSelectLikeByBrand() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        List<Car> cars = mapper.selectLikeByBrand("奔驰");
        cars.forEach(car -> System.out.println(car));
    }

    @Test
    public void testInsertUseGeneratedKeys() {
        CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
        Car car = new Car();
        car.setCarNum("5262");
        car.setBrand("BYD汉");
        car.setGuidePrice(30.3);
        car.setProduceTime("2020-10-11");
        car.setCarType("新能源");
        mapper.insertUseGeneratedKeys(car);
        SqlSessionUtil.openSession().commit();
        System.out.println(car.getId());
    }
}

9.7 常见问题及解决方案

9.7.1 SQL注入问题
  • 问题:使用${}时可能引发SQL注入
  • 解决方案:
    1. 优先使用#{}
    2. 必须使用${}时,确保参数安全
    3. 对用户输入进行严格验证
9.7.2 配置文件位置问题
  • 问题:找不到配置文件
  • 解决方案:
    1. 确保配置文件在正确的位置
    2. 使用正确的配置方式(resource/url/class/package)
    3. 检查文件路径是否正确
9.7.3 类型别名问题
  • 问题:找不到类型别名
  • 解决方案:
    1. 检查typeAliases配置是否正确
    2. 确保包路径正确
    3. 检查类名是否正确
9.7.4 主键生成问题
  • 问题:无法获取自动生成的主键
  • 解决方案:
    1. 确保数据库支持自增主键
    2. 正确配置useGeneratedKeys和keyProperty
    3. 检查实体类属性名是否正确

9.8 性能优化建议

9.8.1 SQL优化
  1. 合理使用索引
  2. 避免使用select *
  3. 优化复杂查询
  4. 使用批量操作
9.8.2 缓存优化
  1. 合理使用一级缓存
  2. 配置二级缓存
  3. 注意缓存更新策略
9.8.3 连接池优化
  1. 合理配置连接池参数
  2. 及时释放连接
  3. 避免连接泄漏
相关推荐
geneculture1 天前
融智学体系图谱(精确对应版)
大数据·人工智能·学习·融智学的重要应用·信智序位
凢en1 天前
初始Infinity Fabric
笔记
秋深枫叶红1 天前
嵌入式第三十六篇——linux系统编程——线程
linux·运维·服务器·学习
songyuc1 天前
论文发表信息笔记
笔记
走在路上的菜鸟1 天前
Android学Dart学习笔记第十七节 类-成员方法
android·笔记·学习·flutter
Jaising6661 天前
Mybatis Plus 主键生成器实现思路分析
数据库·spring boot·mybatis
程芯带你刷C语言简单算法题1 天前
Day30~实现strcmp、strncmp、strchr、strpbrk
c语言·学习·算法·c
x_lrong1 天前
正则化笔记
笔记
Miqiuha1 天前
关注feed流系统设计学习
学习
老王熬夜敲代码1 天前
Linux信号量
linux·笔记·面试