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. 避免连接泄漏
相关推荐
yuhouxiyang几秒前
学习海康VisionMaster之平行线查找
学习·计算机视觉
佩奇的技术笔记5 分钟前
Java学习手册:Java集合框架详解
java·学习
噗噗呼呼哈哈29 分钟前
mybatis 中if标签内容 判断详解
mybatis
使一颗心免于哀伤32 分钟前
《重构》笔记摘录 - 6.重新组织数据
笔记
发誓要做读书人38 分钟前
生物信息Rust-01
开发语言·笔记·rust
贾亚超41 分钟前
Git 实践笔记
笔记·git
丰锋ff41 分钟前
考研单词笔记 2025.04.10
笔记
虾球xz1 小时前
游戏引擎学习第213天
c++·windows·学习·游戏引擎
FAREWELL000751 小时前
C#核心学习(十五)面向对象--关联知识点(1)命名空间
学习·c#·命名空间
zhaoyqcsdn1 小时前
Eigen库的core模块源码阅读笔记
人工智能·经验分享·笔记·算法