最全的MyBatis使用指南

1. 为啥要用 MyBatis

我们作为一个程序员,主要工作归根结底就是和数据打交道。而使用 java 操作数据库的原始方式就是 JDBC

先看看使用 JDBC 方式是如何操作数据库的:

// 1. 加载配置文件 Properties pro=new Properties(); pro.load(new FileReader("resource/jdbc.properties")); // 2. 获取配置文件中连接数据库的信息 String url=pro.getProperty("url"); String user=pro.getProperty("user"); String password=pro.getProperty("password"); String driver=pro.getProperty("driver"); // 3. 加载数据库的驱动 Class.forName(driver); // 4. 创建数据库的连接 Connection conn = DriverManager.getConnection(url, user, password); // 5. sql 语句 String sql = "select * from s_admin where username=? and password=?"; // 3. 创建执行sql的对象 ps = conn.prepareStatement(sql); // 4. 给 ?赋值 ps.setString(1, username); ps.setString(2, password); // 5. 执行sql ResultSet rs = ps.executeQuery(); // 6. 如果查询出数据,则返回该条数据 if (rs.next()) { Admin admin = new Admin(); admin.setUsername(rs.getString("username")); admin.setPassword(rs.getString("password")); return admin; // 7. 否则返回空 } else { return null; }

看完上面的代码,我们发现了 JDBC 存在的问题:

1.每次操作我们都要创建 connection、Statement 等一些对象,操作完还要关闭、销毁这些对象。

2.ResultSet 不能帮我们完成数据库和实体对象的自动转换,我们还要手动赋值。

3.代码冗余,而且业务操作竟然和数据库操作混在一起,开发效率太低了。

真是越来越不想用 JDBC 了,那有没有一个玩意能帮我解决上面 JDBC 遇到的问题呢?

有的,盖世英雄MyBatis踩着七彩祥云来了。

2. MyBatis 简介

官网地址:

https://mybatis.org/mybatis-3/

MyBatis 是一个基于 java 的持久层框架,它内部封装了 jdbc。

开发人员只需要关注 sql 语句,不需要处理加载驱动、创建连接等繁琐的过程。

MyBatis 通过 xml 或注解两种方式配置 sql 语句,然后执行 sql 并将结果映射为 java 对象并返回。

名词解释:

框架:框架是系统中可复用的设计,其实就相当于一个封装了很多功能的工具。

持久层:就是和数据库打交道的那一层。

3. 环境搭建

1.创建数据库和表

我们使用 Navicat 创建数据库 mybatis_demo,然后创建 user 表。

CREATE TABLE `user` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名', `sex` varchar(1) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '性别', `age` int DEFAULT NULL COMMENT '年龄', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;

2.创建 Maven 项目

File -> New -> Project -> Maven

引入依赖

<dependencies> <!-- junit 测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> </dependencies>

3.创建实体类

User 类:

public class User { private int id; private String name; private String sex; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }

4.创建 Dao 接口

public interface UserDao { /** * 获取所有用户信息 * @return */ List<User> getAll(); }

5.创建 sql 映射文件

这是一个 mybatis 使用的配置文件,专门用来写 sql 语句的。

它是一个 xml 文件,因为 sql 语句包含在 mapper 标签中,所以又叫 mapper 文件。一般来说一个表对应一个 xml 文件。

规定:文件名称要和接口保持一致。

UserDao.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.xxl.dao.UserDao"> <select id="getAll" resultType="com.xxl.model.User"> select * from user </select> </mapper>

6.创建 MyBatis 主配置文件

主配置文件也是 xml 文件。

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> <!-- 配置 mybatis 输出日志,可以打印 sql--> <settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings> <!-- 配置环境 --> <environments default="mysql"> <!-- 配置 mysql 的环境--> <environment id="mysql"> <!-- 事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源 POOLED:表示使用连接池 --> <dataSource type="POOLED"> <!-- 配置连接数据库的信息 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/> <property name="username" value="root"/> <property name="password" value="12345678"/> </dataSource> </environment> </environments> <!-- 指定映射配置文件的位置--> <mappers> <mapper resource="mapper/UserDao.xml"/> </mappers> </configuration>

7.编写测试类

public class UserTest { @Test public void testUser() throws IOException { // 1.读取配置文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); // 2.创建 SqlSessionFactory 工厂 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 3.获取 SqlSession 对象 SqlSession session = factory.openSession(); // 4.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 5.执行接口的方法 List<User> userList = userDao.getAll(); userList.forEach(user ->{ System.out.println("姓名:"+user.getName()+",性别:"+user.getSex()+",年龄:"+user.getAge()); }); } }

8.执行结果

9.完整目录结构

4. MyBatisUtil

上面测试代码中读取配置文件、获取 sqlsession 对象等都是一些重复性的操作,我们可以将这些代码封装到一个工具类中。

MyBatisUtil:

public class MyBatisUtil { // 定义 SqlSessionFactory private static SqlSessionFactory factory; // 使用静态块只创建一次 SqlSessionFactory static { try { // 读取配置文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); // 创建 SqlSessionFactory 对象 factory = new SqlSessionFactoryBuilder().build(in); } catch (Exception e) { e.printStackTrace(); } } // 获取 SqlSession 对象 public static SqlSession getSqlSession() { SqlSession sqlSession = factory.openSession(); return sqlSession; } // 提交事务 public static void commit(SqlSession sqlSession) { if (null != sqlSession) { sqlSession.commit(); } close(); } // 回滚事务 public static void rollBack(SqlSession sqlSession) { if (null != sqlSession) { sqlSession.rollback(); } close(); } // 关闭 SqlSession public static void close() { SqlSession sqlSession = getSqlSession(); if (null != sqlSession) { sqlSession.close(); } } }

5. 增删改查

这里我们使用 MyBatisUtil 实现用户的增删改查操作。

UserDao:

public interface UserDao { // 获取所有用户信息 List<User> getAll(); // 新增用户 boolean add(User user); // 修改用户 boolean update(User user); // 删除用户 boolean delete(int id); }

UserDao.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.xxl.dao.UserDao"> <select id="getAll" resultType="com.xxl.model.User"> select * from user </select> <insert id="add" parameterType="com.xxl.model.User"> insert into user(name,sex,age) values(#{name},#{sex},#{age}) </insert> <update id="update"> update user set name = #{name}, sex = #{sex},age = # {age} where id = #{id} </update> <delete id="delete"> delete from user where id = #{id} </delete> </mapper>

1.查询用户

@Test public void testGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 List<User> userList = userDao.getAll(); userList.forEach(user ->{ System.out.println("姓名:"+user.getName()+",性别:"+user.getSex()+",年龄:"+user.getAge()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

2.新增用户

@Test public void add(){ SqlSession session = null; try{ // 1.获取 SqlSession 对象 session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 User user = new User(); user.setName("张无忌"); user.setAge(31); user.setSex("男"); userDao.add(user); // 4.提交事务并关闭 SqlSession MyBatisUtil.commit(session); }catch (Exception e){ e.printStackTrace(); // 回滚事务 MyBatisUtil.rollBack(session); } }

3.修改用户

@Test public void update(){ SqlSession session = null; try{ // 1.获取 SqlSession 对象 session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 User user = new User(); user.setId(3); user.setName("张无忌"); user.setAge(31); user.setSex("男"); userDao.update(user); // 4.提交事务并关闭 SqlSession MyBatisUtil.commit(session); }catch (Exception e){ e.printStackTrace(); // 回滚事务 MyBatisUtil.rollBack(session); } }

4.删除用户

@Test public void delete(){ SqlSession session = null; try{ // 1.获取 SqlSession 对象 session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 userDao.delete(3); // 4.提交事务并关闭 SqlSession MyBatisUtil.commit(session); }catch (Exception e){ e.printStackTrace(); // 回滚事务 MyBatisUtil.rollBack(session); } }

6. MyBatist 核心配置文件

mybatis-config.xml 是 mybatis 的主配置文件,所有的配置都在 configuration 标签里面。

它主要包括:定义别名、配置数据源、配置 mapper 文件等。

1.定义别名

前面我们在 mapper 文件里面设置的 resultType 是全路径名。

<select id="getAll" resultType="com.xxl.model.User"> select * from user </select>

为了简化代码,MyBatis 允许我们给全路径名起一个简单的别名,一般是实体类的类名。我们可以在 mybatis-config.xml 里面这样配置:

<!--配置别名--> <typeAliases> <package name="com.xxl.model"/> </typeAliases>

上面 name 的值一般是实体类所在包的全路径,配置完之后我们就可以直接用 user 代替 com.xxl.model.User 了。

<select id="getAll" resultType="user"> select * from user </select>

2.配置数据源

我们使用 dataSource 标签配置连接数据库的各种参数,其中 dataSource 的 type 表示使用连接池配置数据源。

<!-- 配置环境 --> <environments default="mysql"> <!-- 配置 mysql 的环境--> <environment id="mysql"> <!-- 事务的类型--> <transactionManager type="JDBC"></transactionManager> <!-- 配置数据源 POOLED:表示使用连接池 --> <dataSource type="POOLED"> <!-- 配置连接数据库的信息 --> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo"/> <property name="username" value="root"/> <property name="password" value="12345678"/> </dataSource> </environment> </environments>

为了便于维护数据库的连接参数,我们一般会将这些参数存放在一个专门的文件中,MyBatis 主配置文件再从这个文件中读取连接数据库的参数数据。

在 resources 目录下面新建 jdbc.properties 文件

在主配置文件使用 properties 标签引入 jdbc.properties 文件

<properties resource="jdbc.properties"/>

修改 dataSource

<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>

3.配置查找 mapper 文件的路径

<mappers> <mapper resource="mapper/UserDao.xml"/> </mappers>

4.事务

Mybatis 框架是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,其实就是 JDBC 的 Connection 对象的 commit(), rollback() 。

<transactionManager type="JDBC" />

type=“JDBC” : 表示使用 JDBC 的事务管理机制。但是 MyBatis 默认将自动提交功能关闭了,改为了手动提交。

所以上面的测试代码中,我们在做完增删改之后还要手动提交事务。

自动提交事务

我们在获取 sqlSession 的时候,只需要将 openSession 的参数设置为 true,就能将 MyBatis 设置为自动提交事务。

// 读取配置文件 InputStream in = Resources.getResourceAsStream("mybatis-config.xml"); // 创建 SqlSessionFactory 对象 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); // 获取 SqlSession 对象 SqlSession sqlSession = factory.openSession(true);

7. mapper 映射文件

我们之前定义完 Dao 接口之后,还要定义一个它的实现类 DaoImpl。 而 Mybatis 框架根据 mapper.xml 文件帮助我们自动为接口生成了一个代理对象。

// 使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class);

然后我们就可以调用接口中的方法了,所以这个 mapper 文件很重要。

1.约定格式

① DAO 的全类名必须和对应的 mapper 文件中的namespace一致。

② DAO 中的方法名称必须和 mapper 文件中增删改查操作的id一致。

③ DAO 中的方法的返回值必须和 mapper 文件中操作的返回值类型一致。

④ DAO 中的方法的参数类型必须和 mapper 文件中的输入参数的类型一致。

注: mapper 文件中的 sql 语句不要加分号,id 不能重复。

2.封装输出结果

① resultType

执行 sql 之后得到 ResultSet 转换后的结果类型,使用类型的完全限定名或别名。如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用。

简单类型

接口方法

String getName(int id);

mapper 文件:

<select id="getName" resultType="java.lang.String"> select name from user where id = #{id} </select>

对象类型

接口方法

User getUser(int id);

mapper 文件:

<select id="getUser" resultType="user"> select * from user where id = #{id} </select>

Map 类型

接口方法

@MapKey("id") Map<String,Object> getUser();

mapper 文件:

<select id="getUser" resultType="java.util.Map"> select * from user </select>

② resultMap

当数据库表中的列名和实体类的属性名不一致的时候,我们可以使用 resultMap 定义 sql 的结果和 java 对象属性的映射关系。

例如:

<resultMap id="userMap" type="com.xxl.model.User"> <id column="s_id" property="id"/> <result column="s_name" property="name"/> <result column="s_age" property="age"/> <result column="s_sex" property="sex"/> </resultMap> <select id="getAll" resultMap="userMap"> select s_id,s_name,s_age,s_sex from user </select>

3.模糊查询

方式一:{} 中的值必须是 value

<select id="findByName" resultType="user"> select * from user where name like '%${value}%' </select>

方式二

<select id="findByName" resultType="user"> select * from user where name like "%"#{name}"%" </select>

4.添加/修改处理 null 值

Mybatis 在执行添加、修改操作时不允许出现空值,所以在添加修改 时,要想使某个列的值为空,必须添加 jdbcType 属性。

例如:

<insert id="add" > insert into user(name,sex,age) values(#{name,jdbcType=VARCHAR},#{sex,jdbcType=INTEGER},#{age,jdbcType=INTEGER}) </insert>

8. MyBatis 传参

在 Mapper 文件中,我们可以使用 #{param} 和 ${param} 获取接口中传递的参数。

#{param} 是使用占位符的方式,${param} 是采用拼接 sql 语句的方式。

使用 $ 有 sql 注入的风险,所以下面的例子中都会采用 #{param} 的方式。

一个参数

#{}:可以以任意的名字获取参数值。

例如:

<select id="getUser"> select * from user where id = #{uId} </select> <delete id="delete"> delete from user where id = #{user_id} </delete>

多个参数

1.传递多个参数

传输多个参数,Mybatis会将这些参数放到map集合里面,可以通过 @Param 指定mapper 文件中参数的名字。例如:

接口:

List<Student> getUser(@Param("userName") String name,@Param("userSex") int age);

mapper 文件:

<select id="getUser" resultType="user"> select id,name,sex,age from user where name = #{userName} and age = #{userSex} </select>

2.传递对象

传输参数为 JavaBean,#{} 可以通过属性名获取属性值。

接口:

// 修改用户 boolean update(User user);

mapper 文件:

<update id="update"> update user set name = #{name}, sex = #{sex},age = #{age} where id = #{id} </update>

3.传递 Map

传递的参数为 map 时,mapper 文件使用 # { key } 获取参数值。

例如:

Map<String,Object> data = new HashMap<String,Object>(); data.put("userName","知否君"); data.put("userAge",21);

接口:

List<User> getUser(Map<String,Object> map);

mapper 文件:

<select id="getUser" resultType="user"> select * from user where name = #{userName} and age = #{userAge} </select>

9. 动态 SQL

动态 SQL 是 MyBatis 强大特性之一,主要用于解决查询条件不确定的情况,它可以极大的简化我们拼装 SQL 的操作。

1.if 标签

If 标签用于完成简单的判断,当标签中 test 的值为 true 时,会将其包含的 SQL 片段拼接到其所在的 SQL 语句中。

语法格式:

<if test="条件"> sql 片段 </if>

例如:

<select id="getUser" resultType="user"> select id,name,age,sex from user where 1=1 <if test="name != null and name !='' "> and name = #{name} </if> </select>

2.where 标签

上面的例子中如果 name 参数存在,为了保证 sql 语句的正确性,我们不得不添加 where 1=1

Where 标签就是为了解决 sql 语句中 where 关键字以及条件中第一个 and 或者 or 的问题。

例如:

<select id="getUser" resultType="user"> select id,name,age,sex from user <where> <if test="name != null and name !='' "> and name = #{name} </if> </where> </select>

3.trim 标签

trim 标签可以在条件判断完的 sql 语句前后添加或者去掉指定的字符。

  • prefix: 添加前缀
  • prefixOverrides: 去掉前缀
  • suffix: 添加后缀
  • suffixOverrides: 去掉后缀

例如:

<select id="getUser" resultType="user"> select id,name,age,sex from user <trim prefix="where" suffixOverrides="and"> <if test="name != null and name !='' "> name = #{name} and </if> </trim> </select>

4.choose(when、otherwise) 标签

choose 标签主要是用于分支判断,类似于 java 中的 switch case,只会满足所有分支中的一个。

例如:

<select id="getUser" resultType="user"> select id,name,age,sex from user <where> <choose> <when test="id != null and id !='' "> id = #{id} </when> <when test="name != null and name !='' "> name = #{name} </when> <otherwise> sex = '男' </otherwise> </choose> </where> </select>

5.set 标签

set 标签主要是用于解决修改操作中 sql 语句中可能多出逗号的问题。

例如:

<update id="update"> update user <set> <if test="name != null and name !='' "> name = #{name}, </if> <if test="sex != null and sex !='' "> sex = #{sex}, </if> <if test="age != null and age !='' "> age = #{age} </if> </set> where id = #{id} </update>

6.foreach 标签

foreach 标签主要用于循环迭代。

  • collection: 要迭代的集合
  • item: 当前从集合中迭代出的元素
  • open: 开始字符
  • close:结束字符
  • separator: 元素与元素之间的分隔符
  • 迭代的是List集合: index表示的当前元素的下标
  • 迭代的Map集合: index表示的当前元素的key

例如:

<select id="getUserList" resultType="user"> select id,name,age,sex from user where id in <foreach collection="ids" item="userId" open="(" close=")" separator="," > #{userId} </foreach> </select>

7.sql 标签

sql 标签是用于抽取可重用的 sql 片段,将相同的、使用频繁的 sql 片段抽取出来,单独定义,方便多次引用。

例如:

<sql id="BaseSql"> id,name,age,sex </sql> <select id="getUserList" resultType="user"> select <include refid="BaseSql" /> from user </select>

10. 注解式开发

通过观察 Mapper 文件我们发现,Mapper 文件的核心就是与 Dao 做接口映射,然后里面写一些 sql 语句。

那我能不能不要 mapper 文件,直接在接口里面写 sql 语句?可以的,MyBatsi 为我们提供了注解式开发。

1.修改主配置文件

虽然不用写 mapper 文件了,但是我们要在接口里面写 sql 语句,你得让 MyBatis 知道吧。所以需要告诉 MyBatis 带有注解的接口在哪里。

<!-- 指定带有注解的 dao 接口所在的位置 --> <mappers> <package name="com.xxl.dao"/> </mappers>
  1. 注解式开发
public interface UserDao { // 获取所有用户信息 @Select("select \* from user") List<User> getUser(); // 新增用户 @Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age})") boolean add(User user); // 修改用户 @Update("update user set name = #{name}, sex = #{sex},age = #{age} where id = #{id}") boolean update(User user); // 删除用户 @Delete("delete from user where id = #{id}") boolean delete(int id); }

2.测试

@Test public void testGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 List<User> userList = userDao.getUser(); userList.forEach(user ->{ System.out.println("姓名:"+user.getName()+",性别:"+user.getSex()+",年龄:"+user.getAge()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

执行结果:

11. 关联关系

一对一

例如:一个人对应一张身份证,一张身份证对应一个人。在 MyBatis 的 mapper 文件中使用 association 处理一对一关系。

创建表:

A 表的一条记录,对应 B 表的一条记录,且 A 的主键作为 B 表的外键。这主要看以哪张表为中心。

下面例子中将用户信息表的主键(id)作为用户表的外键(info_id)。

用户表:user

CREATE TABLE `user` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `sex` varchar(1) DEFAULT NULL COMMENT '性别', `age` int(11) DEFAULT NULL COMMENT '年龄', `info_id` int(11) DEFAULT NULL COMMENT '用户信息id', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`user`(`id`, `name`, `sex`, `age`, `info_id`) VALUES (1, '张三', '男', 18, 1); INSERT INTO `mybatis_demo`.`user`(`id`, `name`, `sex`, `age`, `info_id`) VALUES (2, '李四', '男', 19, 2);

用户信息表:user_info

CREATE TABLE `user_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `number` varchar(20) DEFAULT NULL COMMENT '身份证编号', `address` varchar(50) DEFAULT NULL COMMENT '家庭地址', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`user_info`(`id`, `number`, `address`) VALUES (1, '411121200302174025', '上海外滩'); INSERT INTO `mybatis_demo`.`user_info`(`id`, `number`, `address`) VALUES (2, '411121200222154554', '北京三里屯');

用户实体类:User

public class User { private int id; private String name; private String sex; private int age; // 用户信息属性 private UserInfo userInfo; public UserInfo getUserInfo() { return userInfo; } public void setUserInfo(UserInfo userInfo) { this.userInfo = userInfo; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }

用户信息类:

public class UserInfo { private int id; private String number; private String address; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "UserInfo{" + "id=" + id + ", number='" + number + '\'' + ", address='" + address + '\'' + '}'; } }

UserDao:

public interface UserDao { /** * 获取所有用户信息,包括身份证信息 * @return */ List<User> getAll(); }

UserDao.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.xxl.dao.UserDao"> <resultMap type="com.xxl.model.User" id="userMap"> <id column="id" property="id"/> <result column="name" property="name"/> <result column="age" property="age"/> <result column="sex" property="sex"/> <!-- association: 用来处理一对一关系属性封装 property : 关系属性名 javaType: 关系属性的类型 --> <association property="userInfo" javaType="com.xxl.model.UserInfo" > <id column="id" property="id" /> <result column="number" property="number"/> <result column="address" property="address"/> </association> </resultMap> <select id="getAll" resultMap="userMap"> select user.id,user.name,user.age,user.sex, info.id,info.number,info.address from user left join user_info info on user.info_id = info.id </select> </mapper>

测试代码:

@Test public void testUserGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 List<User> userList = userDao.getAll(); userList.forEach(user ->{ System.out.println(user+" "+user.getUserInfo()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

测试结果:

一对多

例如:一个部门对应多个员工,一个员工属于一个部门。在 MyBatis 的 mapper 文件中使用 collection 处理一对多关系。

创建表:

A 表的一条记录,对应 B 表的多条记录,且 A 的主键作为 B 表的外键。

下面例子中将部门表的主键(id)作为员工表的外键(dep_id)。

部门表:department

CREATE TABLE `department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL COMMENT '部门名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`department`(`id`, `name`) VALUES (1, '研发部'); INSERT INTO `mybatis_demo`.`department`(`id`, `name`) VALUES (2, '人事部'); INSERT INTO `mybatis_demo`.`department`(`id`, `name`) VALUES (3, '销售部');

员工表:employee

CREATE TABLE `employee` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL COMMENT '员工姓名', `age` int(11) DEFAULT NULL COMMENT '员工年龄', `sex` varchar(1) DEFAULT NULL COMMENT '员工性别', `dep_id` int(11) DEFAULT NULL COMMENT '部门id', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`employee`(`id`, `name`, `age`, `sex`, `dep_id`) VALUES (1, '张无忌', 21, '男', 1); INSERT INTO `mybatis_demo`.`employee`(`id`, `name`, `age`, `sex`, `dep_id`) VALUES (2, '周芷若', 19, '女', 2); INSERT INTO `mybatis_demo`.`employee`(`id`, `name`, `age`, `sex`, `dep_id`) VALUES (3, '赵敏', 19, '女', 3); INSERT INTO `mybatis_demo`.`employee`(`id`, `name`, `age`, `sex`, `dep_id`) VALUES (4, '小昭', 20, '女', 2); INSERT INTO `mybatis_demo`.`employee`(`id`, `name`, `age`, `sex`, `dep_id`) VALUES (5, '蛛儿', 19, '女', 1);

部门实体类:

public class Department { private String id; private String name; private List<Employee> employeeList; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Employee> getEmployeeList() { return employeeList; } public void setEmployeeList(List<Employee> employeeList) { this.employeeList = employeeList; } @Override public String toString() { return "Department{" + "id='" + id + '\'' + ", name='" + name + '\'' + '}'; } }

员工实体类:

public class Employee { private int id; private String name; private String sex; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }

DepartmentDao:

public interface DepartmentDao { List<Department> getAll(); }

DepartmentDao.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.xxl.dao.DepartmentDao"> <resultMap type="com.xxl.model.Department" id="deptMap"> <id column="depId" property="id"/> <result column="depName" property="name"/> <!-- collection 用来处理集合类型的属性 ,用来处理一对多关系 property: 关系属性名 javaType: 关系属性类型 ofType : 集合中泛型类型:类的全路径名 --> <collection property="employeeList" javaType="list" ofType="com.xxl.model.Employee"> <id column="empId" property="id"/> <result column="empName" property="name"/> <result column="age" property="age"/> <result column="sex" property="sex"/> </collection> </resultMap> <select id="getAll" resultMap="deptMap"> select d.id depId,d.name depName,e.id empId,e.name empName,e.age,e.sex from department d left join employee e on d.id = e.dep_id </select> </mapper>

测试代码:

@Test public void testDepartmentGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 DepartmentDao departmentDao = session.getMapper(DepartmentDao.class); // 3.执行接口的方法 List<Department> departmentList = departmentDao.getAll(); departmentList.forEach(department ->{ System.out.println("部门:"+department+" 员工:"+department.getEmployeeList()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

测试结果:

多对多

例如:例如一个学生可以有多门课程,一门课程可以属于多个学生。多对多可以理解为是一对多和多对一的组合。要实现多对多,一般都需要有一张中间表(也叫关联表),形成多对多的形式。

创建表:

学生表:student

CREATE TABLE `student` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `sex` varchar(1) DEFAULT NULL COMMENT '性别', `age` int(11) DEFAULT NULL COMMENT '年龄', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`student`(`id`, `name`, `sex`, `age`) VALUES (1, '张三', '男', 18); INSERT INTO `mybatis_demo`.`student`(`id`, `name`, `sex`, `age`) VALUES (2, '李四', '女', 21); INSERT INTO `mybatis_demo`.`student`(`id`, `name`, `sex`, `age`) VALUES (3, '王五', '男', 19);

课程表:course

CREATE TABLE `course` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) DEFAULT NULL COMMENT '课程名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`course`(`id`, `name`) VALUES (1, 'java开发'); INSERT INTO `mybatis_demo`.`course`(`id`, `name`) VALUES (2, '数据结构'); INSERT INTO `mybatis_demo`.`course`(`id`, `name`) VALUES (3, '大数据开发'); INSERT INTO `mybatis_demo`.`course`(`id`, `name`) VALUES (4, '云原生开发');

学生-课程关联表:student_course

CREATE TABLE `student_course` ( `student_id` int(11) NOT NULL, `course_id` int(11) NOT NULL, PRIMARY KEY (`student_id`,`course_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (1, 2); INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (1, 3); INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (2, 1); INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (2, 2); INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (3, 3); INSERT INTO `mybatis_demo`.`student_course`(`student_id`, `course_id`) VALUES (3, 4);

学生实体类:

public class Student { private int id; private String name; private String sex; private int age; private List<Course> courseList; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<Course> getCourseList() { return courseList; } public void setCourseList(List<Course> courseList) { this.courseList = courseList; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }

课程实体类:

public class Course { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Course{" + "id=" + id + ", name='" + name + '\'' + '}'; } }

StudentDao:

public interface StudentDao { List<Student> getAll(); }

StudentDao.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.xxl.dao.StudentDao"> <resultMap type="com.xxl.model.Student" id="studentMap"> <id column="studentId" property="id"/> <result column="studentName" property="name"/> <result column="age" property="age"/> <result column="sex" property="sex"/> <!-- collection 用来处理集合类型的属性 ,用来处理一对多关系 property: 关系属性名 javaType: 关系属性类型 ofType : 集合中泛型类型:类的全路径名 --> <collection property="courseList" javaType="list" ofType="com.xxl.model.Course"> <id column="courseId" property="id"/> <result column="courseName" property="name"/> </collection> </resultMap> <select id="getAll" resultMap="studentMap"> select s.id studentId,s.name studentName,s.age,s.sex, c.id courseId,c.name courseName from student s left join student_course sc on s.id = sc.student_id left join course c on sc.course_id = c.id </select> </mapper>

测试代码:

@Test public void testStudentGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 StudentDao studentDao = session.getMapper(StudentDao.class); // 3.执行接口的方法 List<Student> studentList = studentDao.getAll(); studentList.forEach(student ->{ System.out.println("学生:"+student+" 课程:"+student.getCourseList()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

测试结果:

12. 分页插件

我们之前在写分页的时候,不仅要自定义一个 Page 类,还要拼接 sql 语句,最后还要封装数据。真的是”恶心他妈妈给恶心开门——恶心到家了。“

为了解决这个问题,MyBatis 为我们提供了一个通用的分页工具:PageHelper。

使用步骤:

1.引入依赖

<!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.4</version> </dependency>

2.修改主配置文件

在 environments 标签之前添加:

<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor" /> </plugins>

3.准备数据

4.测试代码

@Test public void testGetAll(){ // 1.获取 SqlSession 对象 SqlSession session = MyBatisUtil.getSqlSession(); // 2.使用 SqlSession 创建 Dao 接口的代理对象 UserDao userDao = session.getMapper(UserDao.class); // 3.执行接口的方法 Page page = PageHelper.startPage(2, 3); List<User> userList = userDao.getUser(); System.out.println("当前页:"+page.getPageNum()); System.out.println("每页条数:"+page.getPageSize()); System.out.println("总条数:"+page.getTotal()); System.out.println("总页数:"+page.getPages()); System.out.println("-------------------------"); userList.forEach(user ->{ System.out.println("姓名:"+user.getName()+",性别:"+user.getSex()+",年龄:"+user.getAge()); }); // 4.关闭 SqlSession MyBatisUtil.close(); }

5.执行结果

本文转载自:https://zhuanlan.zhihu.com/p/457069011

鲸之声为您拼命加载中...