MyBatis(一)

发布于:2025-05-19 ⋅ 阅读:(16) ⋅ 点赞:(0)

一、MyBatis 框架概述

1.1 框架定位与核心优势

MyBatis 是基于 Java 的持久层框架,通过封装 JDBC 简化数据库操作,开发者只需专注 SQL 语句编写,无需处理连接创建、参数设置、结果集解析等底层逻辑。其核心优势包括:

  • ORM 思想集成:通过 XML 或注解实现 SQL 与 Java 对象的映射,减少手动数据转换代码。
  • 灵活性与可控性:保留 SQL 编写的自由度,适合复杂查询场景。
  • 动态 SQL 支持:通过 <if><foreach> 等标签实现条件拼接,提升 SQL 复用性。

1.2 核心组件

  • SqlSessionFactory:构建会话工厂,基于配置文件(如 SqlMapConfig.xml)创建 SqlSession
  • SqlSession:执行 SQL 的核心会话对象,提供 selectListselectOne 等方法,并管理事务。
  • Mapper 接口与 XML 映射文件:通过接口定义方法,XML 配置 SQL 语句及参数/结果映射。

二、入门实战:从环境搭建到首个查询

2.1 数据库准备

创建数据库与表结构(mybatis_db 数据库,user 表):

CREATE DATABASE mybatis_db;
USE mybatis_db;

CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(32) NOT NULL COMMENT '用户名称',
  `birthday` DATETIME DEFAULT NULL COMMENT '生日',
  `sex` CHAR(1) DEFAULT NULL COMMENT '性别',
  `address` VARCHAR(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user` VALUES 
(1,'老王','2018-02-27 17:47:08','男','北京'),
(2,'熊大','2018-03-02 15:09:37','女','上海'),
(3,'熊二','2018-03-04 11:34:34','女','深圳'),
(4,'光头强','2018-03-04 12:04:06','男','广州');

2.2 Maven 依赖配置

pom.xml 中引入核心依赖:

<dependencies>
    <!-- MyBatis 核心包 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>
    <!-- JUnit 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
        <scope>test</scope>
    </dependency>
    <!-- Log4j 日志 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>

2.3 代码实现步骤

2.3.1 定义实体类 (User)
package cn.tx.domain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable{

    private static final long serialVersionUID = 525400707336671154L;

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    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 Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

2.3.2 编写 Mapper 接口 (UserMapper)
package cn.tx.mapper;

import java.util.List;
import cn.tx.domain.User;

public interface UserMapper {
    List<User> findAll(); // 查询所有用户
}
2.3.3 配置 Mapper XML 文件(UserMapper.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="cn.tx.mapper.UserMapper">
    <select id="findAll" resultType="cn.tx.domain.User">
        SELECT * FROM user;
    </select>
</mapper>
2.3.4 主配置文件 (SqlMapConfig.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>
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis_db"/>
                <property name="username" value="root"/>
                <property name="password" value="12345"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/UserMapper.xml"/>
    </mappers>
</configuration>
2.3.5 编写测试代码
package cn.tx.test;

import java.io.InputStream;
import java.util.List;
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 org.junit.Test;
import cn.tx.domain.User;
import cn.tx.mapper.UserMapper;

public class UserTest {
    @Test
    public void testFindAll() throws Exception {
        // 加载主配置文件,目的是构建SqlSessionFactory的对象
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 使用SqlSessionFactory工厂对象创建SqlSession对象
        SqlSession session = factory.openSession();
        // 通过session创建UserMapper接口的代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        // 调用查询所有的方法
        List<User> list = mapper.findAll();
        // 遍历集合
        for (User user : list) {
            System.out.println(user);
        }
        // 释放资源
        session.close();
        in.close();
    }

	@Test
    public void run2() throws Exception {
        // 加载配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 构建SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取到session对象
        SqlSession session = factory.openSession();
        // 查询所有的数据
        List<User> list = session.selectList("cn.tx.mapper.UserMapper.findAll");
        // 变量集合
        for (User user : list) {
            System.out.println(user);
        }
        // 关闭资源
        session.close();
        inputStream.close();
    } 
}

三、代理 Dao 方式实现 CRUD

3.1 Mapper 接口扩展方法

public interface UserMapper {
	public List<User> findAll(); // 查询所有用户
    public User findById(Integer userId); // 根据 ID 查询
    public void insert(User user); // 插入用户
    public void update(User user); // 更新用户
    public void delete(Integer userId); // 删除用户
    public List<User> findByName(String username); // 模糊查询
    public Integer findByCount(); // 统计总数
}

3.2 Mapper XML 完整配置(UserMapper.xml)

<mapper namespace="cn.tx.mapper.UserMapper">
    <select id="findAll" resultType="cn.tx.domain.User">
        SELECT * FROM user
    </select>

	<!-- 
        通过id查询 
        SQL语句使用#{占位符的名称,名称可以任意},仅限于基本数据类型和String类型
    -->
    <select id="findById" resultType="cn.tx.domain.User" parameterType="int">
        SELECT * FROM user WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="cn.tx.domain.User">
    	/*
          keyProperty表示要返回的属性名称
          order取值AFTER表示插入数据后的行为
          resultType表示返回值的类型
        */
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user (username, birthday, sex, address) 
        VALUES (#{username}, #{birthday}, #{sex}, #{address})
    </insert>

    <update id="update" parameterType="cn.tx.domain.User">
        UPDATE user 
        SET username=#{username}, birthday=#{birthday}, 
            sex=#{sex}, address=#{address} 
        WHERE id=#{id}
    </update>

    <delete id="delete" parameterType="Integer">
        DELETE FROM user WHERE id=#{id}
    </delete>

	<!-- 模糊查询 -->
    <select id="findByName" resultType="cn.tx.domain.User" parameterType="string">
    	<!-- 第一种方式的SQL语句 
        select * from user where username  like #{username}
        -->
        <!-- 第二种方式的SQL语句 强调:'%${value}%'不能修改,固定写法(不推荐使用)  -->
        SELECT * FROM user WHERE username LIKE '%${value}%' <!-- 注意:$ 可能存在 SQL 注入风险 -->
    </select>

    <select id="findByCount" resultType="int">
        SELECT COUNT(*) FROM user
    </select>
</mapper>

3.3 测试代码示例

public class UserTest {
    
    private InputStream in;
    private SqlSession session;
    private UserMapper mapper;
    
    @Before
    public void init() throws Exception {
        // 加载配置文件
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        // 创建工厂对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 创建Session对象
        session = factory.openSession();
        // 获取到代理对象
        mapper = session.getMapper(UserMapper.class);
    }
    
    @After
    public void destory() throws IOException {
        in.close();
        session.close();
    }
    
    /**
     * 测试查询所有的方法
     * @throws Exception 
     */
    @Test
    public void testFindAll() throws Exception {
        List<User> list = mapper.findAll();
        // 遍历
        for (User user : list) {
            System.out.println(user);
        }
        in.close();
    }
    
    @Test
    public void testFindById() throws Exception {
        User user = mapper.findById(41);
        System.out.println(user);
        in.close();
    }@Test
    public void testInsert() throws Exception {
        User user = new User();
        user.setUsername("美美");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("顺义");
        mapper.insert(user);
        session.commit();
        System.out.println(user.getId());
    }
    
    @Test
    public void testUpdate() throws Exception {
        User user = mapper.findById(41);
        user.setUsername("小凤");
        mapper.update(user);
        session.commit();
    }
    
    @Test
    public void testDelete() throws Exception {
        mapper.delete(48);
        session.commit();
    }
    
    // 第一种
    @Test
    public void testFindByName1() throws Exception {
        List<User> list = mapper.findByName("%王%");
        for (User user : list) {
            System.out.println(user);
        }
    }
    // 第二种
    @Test
    public void testFindByName2() throws Exception {
        List<User> list = mapper.findByName("王");
        for (User user : list) {
            System.out.println(user);
        }
    }
    
    @Test
    public void testFindByCount() throws Exception {
        Integer count = mapper.findByCount();
        System.out.println("总记录数:"+count);
    }
    
}

四、参数与结果映射详解

4.1 参数类型(parameterType)

4.1.1 简单类型

支持 intStringlong 等基本类型及包装类,XML 中使用 #{参数名}#{value} 取值:

<select id="findById" resultType="User" parameterType="int">
    SELECT * FROM user WHERE id=#{id} <!-- 参数为简单类型时,#{} 内可任意命名 -->
</select>
4.1.2 POJO 对象

直接传入实体类,XML 中通过 #{属性名} 取值:

public void insert(User user); // 传入 User 对象
<insert id="insert" parameterType="User">
    INSERT INTO user (username) VALUES (#{username})
</insert>
4.1.3 包装对象(QueryVo)

用于封装多个查询条件或关联对象:

public class QueryVo implements Serializable {
    
    // 自己属性
    private String name;
    // user属性
    private User user;
    // role属性
    private Role role;public String getName() {
        return name;
    }public void setName(String name) {
        this.name = name;
    }public User getUser() {
        return user;
    }public void setUser(User user) {
        this.user = user;
    }public Role getRole() {
        return role;
    }public void setRole(Role role) {
        this.role = role;
    }
}
<!--包装类测试查询-->
<select id="findByVo" parameterType="cn.tx.domain.QueryVo" resultType="cn.tx.domain.User">
    SELECT * FROM user WHERE username=#{user.username}
</select>

4.2 结果类型(resultType 与 resultMap)

4.2.1 resultType 直接映射

当数据库字段名与实体类属性名一致时,直接指定 resultType 即可:

<select id="findAll" resultType="cn.tx.domain.User">
    SELECT id, username, birthday FROM user
</select>
4.2.2 resultMap 自定义映射

字段名与属性名不匹配时,通过 resultMap 配置映射关系:

<select id="findUsers" resultMap="userMap">
    select id _id,username _username,birthday _birthday,sex _sex,address _address from user
</select>

<!--
        配置resultMap,用来进行数据封装
        id="唯一的名称,用来被引用的"
        type="进行封装数据的类型"
-->
<resultMap id="userMap" type="cn.tx.domain.User">
	<!-- property:属性名,column:字段名 -->
    <result property="id" column="_id"/>
  	<result property="username" column="_username" />
  	<result property="birthday" column="_birthday" />
  	<result property="sex" column="_sex" />
  	<result property="address" column="_address" />
</resultMap>

五、核心配置文件(SqlMapConfig.xml)优化

5.1 外部属性文件管理

将数据库配置提取到 jdbc.properties 文件中:

# jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///mybatis_db
jdbc.username=root
jdbc.password=12345

SqlMapConfig.xml 中引用:

<configuration>
    <!-- 一、定义property标签 -->
    <!--<properties>-->
        <!--<property name="jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>-->
        <!--<property name="jdbc.url" value="jdbc:mysql:///mybatis_db"/>-->
        <!--<property name="jdbc.username" value="root"/>-->
        <!--<property name="jdbc.password" value="12345"/>-->
    <!--</properties>-->
    <!-- 二、加载外部文件 -->
    <properties resource="jdbc.properties"/> 
    
    <environments default="mysql">
        <environment id="mysql">
            <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>
</configuration>

5.2 类型别名(typeAliases)

简化全类名引用,两种配置方式:

5.2.1 单个类别名
<typeAliases>
	<!-- 别名不区分大小写 -->
    <typeAlias type="cn.tx.domain.User" alias="user"/> 
</typeAliases>

使用时:

<select id="findAll" resultType="user"/> <!-- 替代全路径 -->
5.2.2 包扫描自动别名
<typeAliases>
    <package name="cn.tx.domain"/> <!-- 包下所有类以类名小写作为别名 -->
</typeAliases>

例如 User 类别名为 userOrder 类别名为 order

六、关键细节与注意事项

6.1 SQL 注入防范

  • 优先使用 #{}:预处理语句(PreparedStatement)会对参数进行转义,防止 SQL 注入。
  • 谨慎使用 $:直接拼接字符串,仅在动态表名/列名场景使用,需手动过滤特殊字符:
    <select id="findByColumn" resultType="User">
        SELECT * FROM user WHERE ${column} = #{value} <!-- 危险!需确保 column 可控 -->
    </select>
    

6.2 事务管理

  • MyBatis 默认不自动提交事务,增删改操作需调用 session.commit()
  • 可通过配置 SqlSessionautoCommit 参数开启自动提交:
    SqlSession session = factory.openSession(true); // 自动提交事务
    

6.3 日志配置

添加 log4j.properties 文件配置日志输出,方便调试 SQL:

log4j.rootLogger=DEBUG, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
# 打印 MyBatis 执行的 SQL
log4j.logger.cn.tx.mapper=DEBUG

七、总结

MyBatis 通过 ORM 映射动态 SQL 机制,在保留 SQL 灵活性的同时减少了重复代码,适用于各类 Java 项目的持久层开发。核心学习路径包括:

  1. 掌握入门流程:环境搭建 → 实体类 → Mapper 接口与 XML → 配置文件 → 测试。
  2. 深入 CRUD 操作:理解参数传递、结果映射、事务管理。
  3. 优化配置:使用外部属性、类型别名、日志系统提升开发效率。
  4. 安全与性能:避免 SQL 注入,合理使用连接池(POOLED)和缓存。

通过以上实战与知识点总结,可快速上手 MyBatis 并应用于实际项目中。


网站公告

今日签到

点亮在社区的每一天
去签到