Java进阶全套教程(一)—— 数据框架Mybatis详解

Java进阶全套教程(一)------ 数据框架Mybatis详解

一、MyBatis核心认知与开发定位

在Java后端开发中,我们通过JDBC可以实现Java程序与MySQL数据库的交互,完成数据增删改查操作,但原生JDBC存在大量冗余代码、硬编码严重、维护性差等诸多问题。MyBatis作为一款轻量级半自动ORM持久层框架,专门用于解决Java程序与数据库的数据交互问题,是目前Java企业级开发的主流持久层框架。

简单来说,MyBatis的核心定位是:简化JDBC操作,统一持久层代码规范,实现Java代码与SQL语句解耦。相较于原生JDBC,它剔除了注册驱动、获取连接、释放资源、封装结果集等重复模板代码,让开发者只需专注核心SQL编写与业务数据处理,极大提升数据库开发效率。

同时MyBatis属于半自动ORM框架,区别于Hibernate等全自动框架:全自动框架无需手写SQL,灵活性差;而MyBatis支持开发者自主编写SQL语句,兼顾开发效率与SQL优化灵活性,完美适配互联网项目多变的业务场景,这也是其成为Java开发首选持久层框架的核心原因。

1.1 原生JDBC核心痛点(MyBatis解决的核心问题)

想要理解MyBatis的价值,首先要明确原生JDBC开发的短板,这也是框架诞生的核心意义:

  • 代码冗余繁琐:每次数据库操作都需要重复编写加载驱动、创建连接、创建Statement、关闭资源等固定模板代码,大量重复代码增加开发工作量。

  • SQL硬编码耦合:SQL语句直接写在Java代码中,一旦需要修改SQL逻辑,必须改动Java源码并重新编译、部署,维护成本极高。

  • 参数封装繁琐:向SQL语句传递参数时,需要手动调用setXXX方法逐个赋值,参数较多时代码臃肿且容易出错。

  • 结果集解析复杂:数据库查询后的ResultSet结果集,需要手动遍历、封装为Java实体对象,重复机械代码多,且容易出现类型转换异常。

  • 连接资源浪费:原生JDBC每次操作都要创建、销毁数据库连接,无法实现连接复用,频繁IO操作导致程序性能低下。

1.2 MyBatis核心优势与适用场景

MyBatis针对性解决了JDBC的所有痛点,同时适配Java项目各类开发场景,核心优势如下:

  • 代码极简,剔除冗余:自动完成数据库驱动加载、连接获取、资源关闭、结果集封装等通用操作,开发者仅需关注核心业务SQL与参数处理。

  • 解耦彻底,易于维护:支持将SQL语句统一配置在XML文件或注解中,与Java业务代码完全分离,SQL修改无需改动Java代码,后续迭代维护更便捷。

  • 参数自动映射:支持基本数据类型、实体对象、集合等多种参数自动封装,无需手动逐个赋值,大幅简化参数传递代码。

  • 结果自动封装:可通过配置实现数据库字段与Java实体类属性的自动映射,无需手动遍历结果集,自动封装为实体对象或集合。

  • 性能优异,灵活性高:框架轻量无冗余加载,支持手写优化SQL,适配高并发、海量数据场景,同时支持动态SQL,可根据业务条件灵活拼接查询语句。

  • 生态完善,适配性强:完美适配Spring、SpringBoot主流框架,拥有丰富的插件、工具与社区资源,是企业项目、面试、实战开发的核心技术。

二、MyBatis核心架构与执行流程

掌握MyBatis的底层架构与执行流程,是后续熟练使用框架、排查报错、优化性能的基础,能够帮助开发者理解框架底层运行逻辑,避免只会套模板不懂原理的开发问题。

2.1 MyBatis核心四大核心组件

MyBatis的整体运行依赖四大核心组件,各司其职、层层联动,构成完整的数据库交互体系:

1. SqlSessionFactory(会话工厂)

MyBatis的核心工厂类,是整个框架的入口,负责读取MyBatis全局配置文件、加载所有Mapper映射文件,初始化MyBatis运行环境,最终用于生产SqlSession对象。该对象全局只需创建一次,属于单例资源。

2. SqlSession(会话对象)

MyBatis的核心操作对象,相当于原生JDBC的Connection连接对象,是程序与数据库交互的桥梁。所有数据库增删改查操作都通过SqlSession完成,同时具备事务管理能力,可手动提交、回滚事务。SqlSession为线程私有,每次数据库操作需创建新对象,操作完成后关闭释放资源。

3. Mapper接口与Mapper映射文件

Mapper接口是Java定义的持久层方法接口,仅声明数据库操作方法,无具体实现;Mapper映射文件(XML文件)用于编写对应方法的SQL语句、配置参数映射规则、结果集封装规则。二者通过全限定名绑定,实现方法与SQL的一一对应

4. Executor(执行器)

MyBatis的底层执行核心,负责接收SqlSession的操作请求,调用JDBC底层API执行SQL语句,同时完成参数预处理、SQL执行、结果集封装、资源回收等底层操作,对上层开发者透明无感知。

2.2 MyBatis完整执行流程(通俗易懂版)

结合四大核心组件,MyBatis从程序发起请求到获取数据库结果的完整流程如下:

  1. 加载配置初始化:项目启动时,SqlSessionFactoryBuilder读取MyBatis全局配置文件,加载数据库连接参数、环境配置、Mapper文件路径等信息,创建SqlSessionFactory工厂对象。

  2. 获取会话对象:程序发起数据库操作请求时,通过SqlSessionFactory创建SqlSession会话对象。

  3. 绑定Mapper方法:SqlSession根据Mapper接口全限定名,匹配对应的Mapper映射文件,定位到目标方法对应的SQL语句。

  4. 底层执行SQL:Executor执行器对SQL语句进行参数填充、预处理,通过JDBC底层与数据库建立连接,执行增删改查操作。

  5. 结果自动封装:框架接收数据库返回的结果集,根据映射配置自动封装为对应的Java实体对象、List集合或基本数据类型。

  6. 资源释放收尾:SQL执行完成后,自动关闭数据库连接、释放Statement、ResultSet资源,关闭SqlSession会话,完成一次完整数据库交互。

三、MyBatis环境搭建与入门实战(可直接运行)

本节基于标准Maven项目搭建纯MyBatis开发环境,从零实现完整的增删改查操作,所有代码经过规范优化,无转义错误、无缺失,可直接复制运行,贴合企业开发标准。

3.1 项目依赖引入(Maven)

搭建MyBatis基础环境,只需引入核心依赖:MyBatis框架依赖、MySQL驱动依赖、单元测试依赖,完整pom.xml核心依赖配置如下:

xml 复制代码
<!-- 项目统一版本管理 -->
<dependencies>
    <!-- MyBatis核心依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>

    <!-- MySQL数据库驱动(适配MySQL8.0+) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
        <scope>runtime</scope>
    </dependency>

    <!-- Junit单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

3.2 数据库与实体类准备

本次实战使用用户积分表作为测试数据表,语句标准可直接执行,无语法报错:

sql 复制代码
CREATE TABLE user_score (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    username VARCHAR(30) NOT NULL COMMENT '用户名',
    score INT DEFAULT 0 COMMENT '用户积分',
    level TINYINT DEFAULT 1 COMMENT '用户等级',
    create_time DATETIME DEFAULT NOW() COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户积分表';

对应创建标准Java实体类UserScore,严格遵循驼峰命名、私有化属性、完整get/set、无参有参构造、重写toString,适配MyBatis自动映射:

java 复制代码
import java.util.Date;

/**
 * 用户积分实体类
 * 对应数据库user_score表
 * 规范:数据库下划线字段 <=> Java驼峰属性
 */
public class UserScore {
    // 主键ID
    private Integer id;
    // 用户名
    private String username;
    // 用户积分
    private Integer score;
    // 用户等级
    private Integer level;
    // 创建时间
    private Date createTime;

    // 无参构造(MyBatis反射实例化必须)
    public UserScore() {}

    // 有参构造
    public UserScore(String username, Integer score, Integer level, Date createTime) {
        this.username = username;
        this.score = score;
        this.level = level;
        this.createTime = createTime;
    }

    // 完整getter、setter方法
    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    // 重写toString,方便控制台打印查看结果
    @Override
    public String toString() {
        return "UserScore{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", score=" + score +
                ", level=" + level +
                ", createTime=" + createTime +
                '}';
    }
}

3.3 MyBatis全局核心配置文件(修复转义、标准DTD)

在resources目录下创建mybatis-config.xml,使用官方标准约束,修复所有标签转义异常,配置驼峰自动映射、SQL日志打印、数据库环境,可直接使用:

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>
    <!-- 全局参数配置 -->
    <settings>
        <!-- 开启驼峰命名自动映射:数据库create_time自动映射实体createTime -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启控制台SQL日志打印,开发调试必备 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!-- 多环境配置,默认开发环境 -->
    <environments default="development">
        <environment id="development">
            <!-- JDBC事务管理,手动控制事务提交回滚 -->
            <transactionManager type="JDBC"/>
            <!-- 内置连接池配置 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 注册所有Mapper映射文件 -->
    <mappers>
        <mapper resource="mapper/UserScoreMapper.xml"/>
    </mappers>
</configuration>

3.4 Mapper接口与完整映射文件(补齐CRUD)

优化原有接口,补齐增、删、改、查单条、查全部、条件查询常用方法,覆盖基础开发场景,代码规范完整。

第一步:完整UserScoreMapper持久层接口

java 复制代码
import org.apache.ibatis.annotations.Param;
import java.util.List;

/**
 * 用户积分持久层接口
 * 标准CRUD方法定义
 */
public interface UserScoreMapper {

    /**
     * 新增用户积分数据
     * @param userScore 实体参数
     * @return 受影响行数
     */
    int insertUserScore(UserScore userScore);

    /**
     * 根据ID删除数据
     * @param id 主键ID
     * @return 受影响行数
     */
    int deleteUserScoreById(@Param("id") Integer id);

    /**
     * 动态修改用户积分数据
     * @param userScore 待修改实体
     * @return 受影响行数
     */
    int updateUserScore(UserScore userScore);

    /**
     * 根据ID查询单条数据
     * @param id 主键ID
     * @return 实体对象
     */
    UserScore getUserScoreById(@Param("id") Integer id);

    /**
     * 查询全部用户积分数据
     * @return 数据集合
     */
    List<UserScore> listAllUserScore();

    /**
     * 根据等级、用户名、积分多条件动态查询
     * @param level 用户等级
     * @param username 用户名模糊匹配
     * @param score 最低积分
     * @return 条件匹配数据
     */
    List<UserScore> listUserScoreByCondition(@Param("level") Integer level,
                                              @Param("username") String username,
                                              @Param("score") Integer score);

    /**
     * 批量根据ID查询数据
     * @param idList ID集合
     * @return 数据集合
     */
    List<UserScore> listUserScoreByIds(@Param("idList") List<Integer> idList);
}

第二步:完整无报错UserScoreMapper.xml映射文件(修复所有转义、补齐全部SQL)

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接口全限定类名 -->
<mapper namespace="UserScoreMapper">

    <!-- 自定义结果映射(通用适配所有查询) -->
    <resultMap id="UserScoreResultMap" type="UserScore">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="score" property="score"/>
        <result column="level" property="level"/>
        <result column="create_time" property="createTime"/>
    </resultMap>

    <!-- 新增数据 -->
    <insert id="insertUserScore">
        INSERT INTO user_score(username,score,level,create_time)
        VALUES(#{username},#{score},#{level},#{createTime})
    </insert>

    <!-- 根据ID删除 -->
    <delete id="deleteUserScoreById">
        DELETE FROM user_score WHERE id = #{id}
    </delete>

    <!-- 动态更新数据 -->
    <update id="updateUserScore">
        UPDATE user_score
        <set>
            <if test="username != null and username != ''">
                username = #{username},
            </if>
            <if test="score != null">
                score = #{score},
            </if>
            <if test="level != null">
                level = #{level}
            </if>
        </set>
        WHERE id = #{id}
    </update>

    <!-- 根据ID查询单条数据 -->
    <select id="getUserScoreById" resultMap="UserScoreResultMap">
        SELECT id,username,score,level,create_time FROM user_score WHERE id = #{id}
    </select>

    <!-- 查询全部数据 -->
    <select id="listAllUserScore" resultMap="UserScoreResultMap">
        SELECT id,username,score,level,create_time FROM user_score
    </select>

    <!-- 多条件动态查询(where标签智能去重关键字) -->
    <select id="listUserScoreByCondition" resultMap="UserScoreResultMap">
        SELECT id,username,score,level,create_time FROM user_score
        <where>
            <if test="level != null">
                level = #{level}
            </if>
            <if test="username != null and username != ''">
                AND username LIKE CONCAT('%',#{username},'%')
            </if>
            <if test="score != null">
                AND score >= #{score}
            </if>
        </where>
    </select>

    <!-- 批量查询(foreach遍历集合) -->
    <select id="listUserScoreByIds" resultMap="UserScoreResultMap">
        SELECT id,username,score,level,create_time FROM user_score
        WHERE id IN
        <foreach collection="idList" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>

</mapper>

3.5 标准版MyBatis工具类(单例工厂、资源安全)

优化工具类代码,保证线程安全、工厂单例,封装重载方法,支持手动事务提交/回滚,适配更多业务场景:

java 复制代码
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;

/**
 * MyBatis通用工具类
 * 功能:全局单例SqlSessionFactory、快速获取会话、事务可控
 */
public class MyBatisUtil {
    // 全局唯一工厂对象(单例)
    private static final SqlSessionFactory SQL_SESSION_FACTORY;

    // 静态块初始化工厂,项目启动仅执行一次
    static {
        try {
            String configPath = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(configPath);
            SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("MyBatis配置文件加载失败!");
        }
    }

    /**
     * 获取SqlSession,自动提交事务
     * @return SqlSession会话
     */
    public static SqlSession getSqlSession() {
        return SQL_SESSION_FACTORY.openSession(true);
    }

    /**
     * 获取SqlSession,手动控制事务(增删改推荐使用)
     * @return SqlSession会话
     */
    public static SqlSession getSqlSessionManual() {
        return SQL_SESSION_FACTORY.openSession(false);
    }
}

3.6 完整单元测试类(全覆盖CRUD)

编写全套测试方法,覆盖所有接口功能,包含事务手动提交案例,代码可直接运行,结果可直观查看:

java 复制代码
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 全套MyBatis CRUD测试类
 */
public class UserScoreMapperTest {

    // 测试新增数据(自动提交事务)
    @Test
    public void testInsert() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        // 封装测试数据
        UserScore userScore = new UserScore("lisi", 260, 4, new Date());
        int rows = mapper.insertUserScore(userScore);
        System.out.println("新增成功,受影响行数:" + rows);

        sqlSession.close();
    }

    // 测试根据ID查询单条数据
    @Test
    public void testGetById() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        UserScore userScore = mapper.getUserScoreById(1);
        System.out.println("单条查询结果:" + userScore);

        sqlSession.close();
    }

    // 测试查询全部数据
    @Test
    public void testListAll() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        List<UserScore> scoreList = mapper.listAllUserScore();
        scoreList.forEach(System.out::println);

        sqlSession.close();
    }

    // 测试多条件动态查询
    @Test
    public void testListByCondition() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        // 查询4级、积分≥200、用户名含li的用户
        List<UserScore> scoreList = mapper.listUserScoreByCondition(4, "li", 200);
        scoreList.forEach(System.out::println);

        sqlSession.close();
    }

    // 测试动态更新数据(手动事务案例)
    @Test
    public void testUpdate() {
        // 手动事务:需手动提交
        SqlSession sqlSession = MyBatisUtil.getSqlSessionManual();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        // 仅更新积分和等级,用户名不修改
        UserScore userScore = new UserScore();
        userScore.setId(1);
        userScore.setScore(320);
        userScore.setLevel(5);

        int rows = mapper.updateUserScore(userScore);
        System.out.println("更新成功,受影响行数:" + rows);

        // 手动提交事务
        sqlSession.commit();
        sqlSession.close();
    }

    // 测试批量查询
    @Test
    public void testListByIds() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        List<Integer> idList = Arrays.asList(1,2,3);
        List<UserScore> scoreList = mapper.listUserScoreByIds(idList);
        scoreList.forEach(System.out::println);

        sqlSession.close();
    }

    // 测试删除数据
    @Test
    public void testDelete() {
        SqlSession sqlSession = MyBatisUtil.getSqlSessionManual();
        UserScoreMapper mapper = sqlSession.getMapper(UserScoreMapper.class);

        int rows = mapper.deleteUserScoreById(4);
        System.out.println("删除成功,受影响行数:" + rows);

        sqlSession.commit();
        sqlSession.close();
    }
}

四、MyBatis核心语法与映射规则(代码优化详解)

4.1 两种参数占位符完整对比(附实战代码)

MyBatis核心两种占位符是开发高频重点,本次补充实战案例,清晰区分使用场景:

1. #{参数名} 预编译占位符(推荐)

底层生成PreparedStatement,参数预编译、防SQL注入、自动类型适配,日常99%场景优先使用

2. ${参数名} 字符串拼接占位符(慎用)

直接字符串拼接,无预编译,存在注入风险,仅用于动态表名、动态排序等特殊场景,实战示例:

xml 复制代码
<!-- 动态排序场景:只能使用$ -->
<select id="listUserScoreOrderBy" resultMap="UserScoreResultMap">
    SELECT id,username,score,level,create_time 
    FROM user_score 
    ORDER BY ${sortField} ${sortType}
</select>

4.2 ResultMap手动映射完整实战

解决字段名与实体名不匹配、复杂关联查询场景,本次优化通用ResultMap,可全局复用,无需重复定义:

xml 复制代码
<!-- 全局通用结果映射 -->
<resultMap id="UserScoreResultMap" type="UserScore">
    <!-- id标签:主键字段,提升查询效率 -->
    <id column="id" property="id"/>
    <!-- result标签:普通字段映射 -->
    <result column="username" property="username"/>
    <result column="score" property="score"/>
    <result column="level" property="level"/>
    <result column="create_time" property="createTime"/>
</resultMap>

五、动态SQL完整优化代码(企业级写法)

动态SQL是MyBatis核心功能,优化所有标签代码,去除冗余、规避语法错误,适配真实业务场景。

5.1 if + where 组合动态查询(最优写法)

where标签自动去除多余AND/OR,无需手动写1=1,代码简洁无BUG,为企业标准写法:

xml 复制代码
<select id="listUserScoreByCondition" resultMap="UserScoreResultMap">
    SELECT id,username,score,level,create_time FROM user_score
    <where>
        <!-- 等级非空匹配 -->
        <if test="level != null">
            level = #{level}
        </if>
        <!-- 用户名非空模糊查询,空字符串不匹配 -->
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%',#{username},'%')
        </if>
        <!-- 最低积分筛选 -->
        <if test="score != null">
            AND score >= #{score}
        </if>
    </where>
</select>

5.2 set动态更新标签(防多余逗号)

set标签自动剔除SQL末尾多余逗号,只更新传入的非空字段,避免全覆盖更新数据:

xml 复制代码
<update id="updateUserScore">
    UPDATE user_score
    <set>
        <if test="username != null and username != ''">
            username = #{username},
        </if>
        <if test="score != null">
            score = #{score},
        </if>
        <if test="level != null">
            level = #{level}
        </if>
    </set>
    WHERE id = #{id}
</update>

5.3 foreach批量操作完整版

适配List集合参数,完整参数释义,支持批量查询、批量删除,企业高频使用:

xml 复制代码
<!-- 批量查询 -->
<select id="listUserScoreByIds" resultMap="UserScoreResultMap">
    SELECT id,username,score,level,create_time FROM user_score
    WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量删除扩展 -->
<delete id="batchDeleteByIds">
    DELETE FROM user_score WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

六、开发规范与代码避坑总结

6.1 代码优化核心亮点

  • 修复所有XML转义异常、DTD约束错误,所有配置文件零报错;

  • 补齐完整CRUD代码,覆盖基础、动态SQL、批量操作全场景;

  • 工具类优化为单例模式,线程安全、资源不浪费;

  • 新增手动事务控制案例,适配增删改严谨业务场景;

  • 所有代码遵循企业规范,命名语义化、结构统一、可直接上线使用。

6.2 高频代码避坑点

  • 实体类必须提供无参构造方法,否则MyBatis反射实例化报错;

  • 动态SQL查询必须使用where标签,禁止手动拼接1=1,避免语法冗余;

  • 增删改操作建议手动控制事务,防止数据提交异常;

  • 集合参数必须使用@Param注解绑定参数名,否则foreach无法识别;

  • 优先使用#{ }占位符,杜绝滥用${ },防止SQL注入漏洞。

相关推荐
RSTJ_16251 小时前
PYTHON+AI LLM DAY FIFITY-THREE
开发语言·人工智能·python
programhelp_1 小时前
Roblox Coding OA 面经分享|题量不小,但整体更偏工程思维
人工智能·算法·面试
王璐WL1 小时前
【C++进阶】多态,坑很多,面试常考!!!
c++·面试
UEBqbZvUB1 小时前
基于 Flask 框架开发的在线学习平台,集成人工智能技术,提供分类练习、随机练习、智能推荐等多种学习模式 HTTPS ECDHE 握手全解析
开发语言·flask·java-consul
JAVA学习通1 小时前
《大营销平台系统设计实现》 - 营销服务 第10节:不超卖库存规则实现
java·数据库·oracle·责任链模式·codex
qq_2518364571 小时前
基于java 安卓-RSS阅读系统毕业论文
android·java·开发语言
lili00121 小时前
Gemini 3.5发布后的AI格局:谷歌重新定义行业标准
java·人工智能·python·ai编程
之歆1 小时前
Day15_JavaScript DOM 事件完全指南:从基础到实战(上)
开发语言·javascript·ecmascript
JAVA社区1 小时前
Java进阶全套教程(八)—— Docker超详细实战详解
java·运维·开发语言·docker·容器·面试·职场和发展