MyBatis

文章目录

1.什么是MyBatis

  • mybatis 是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
  • mybatis通过xml或注解的方式将要执行的各种 statement配置起来,并通过java对象和statement中sql的动态参数进行映射生成最终执行的sql语句。
  • 最后mybatis框架执行sql并将结果映射为java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc 进行了封装,屏蔽了jdbc api 底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。

2.MyBatis快速入门

MyBatis官网地址

MyBatis开发步骤:

  1. 添加MyBatis和mysql驱动的坐标
xml 复制代码
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
  1. 创建user数据表
  2. 编写User实体类
java 复制代码
package com.zhxd.domain;

public class User {
    private int id;
    private String username;
    private String password;

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", usernanme='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
  1. 编写映射文件UserMapper.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="UserMapper">
    <!--查询所有数据-->
    <select id="findAll" resultType="com.zhxd.domain.User">
        select * from user
    </select>
</mapper>
  1. 编写核心文件SqlMapConfig.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>
    <!--配置数据源的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"></property>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test"></property>
                <property name="username" value="root"></property>
                <property name="password" value="root"></property>
            </dataSource>
        </environment>
    </environments>

    <!--配置映射关系-->
    <mappers>
        <mapper resource="com/zhxd/mapper/UserMapper.xml"></mapper>
    </mappers>
</configuration>
  1. 编写测试类
java 复制代码
package com.zhxd.test;

import com.zhxd.domain.User;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class TestMyBatis {

    //查询所有数据
    @Test
    public void test1() throws IOException {
       //加载MyBatis核心配置类文件
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        //获得sqlSession工厂对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //获得sqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //执行sql语句
        List<User> userList = sqlSession.selectList("UserMapper.findAll");
        //打印结果
        System.out.println(userList);
        //释放资源
        sqlSession.close();
    }
}

3.MyBatis映射文件概述

  1. <mappers>:根标签
    • 属性:namespace: 命名空间,与语句id一起组成查询的标识
    • 子标签:<select> <insert> <delete> <update>,分别代表查询、插入、删除、更新操作
      • 上述四个标签拥有id属性,与namespace一起组成查询标识;在标签内进行sql语句的编写

4. MyBatis增删改操作:

4.1插入操作

  1. 在映射文件中编写sql语句:
xml 复制代码
    <!--添加数据-->
<insert id="addUser" parameterType="com.zhxd.domain.User">
    insert into user values(#{id}, #{username}, #{password});
</insert>
  1. 测试
java 复制代码
//数据库插入操作
@Test
public void test2() throws IOException {
    User user = new User();
    user.setUsername("zhxd");
    user.setPassword("12345678");

    //加载MyBatis核心配置类文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //获得sqlSession工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获得sqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql语句
    sqlSession.insert("UserMapper.addUser", user);
    //提交事务
    sqlSession.commit();
    //释放资源
    sqlSession.close();
}
  1. 插入操作注意问题
  • 插入语句使用insert标签
  • 在映射文件中使用parameterType属性指定要插入的数据类型
  • Sql语句中使用#{实体属性名}方式引用实体中的属性值
  • 插入操作使用的API是sqlSession.insert("命名空间.id",实体对象)
  • 插入操作涉及数据库数据变化,所以要使用sqlSession对象显示的提交事务,即sqlSession.commit()

4.2 修改操作

  1. 在映射文件中编写sql语句:
xml 复制代码
<!--修改数据-->
<update id="updateUser" parameterType="com.zhxd.domain.User">
    update user set username = #{username},password=#{password} where id = #{id}
</update>
  1. 测试
java 复制代码
//数据库更新操作
@Test
public void test3() throws IOException {
    User user = new User();
    user.setId(1);
    user.setUsername("marry");
    user.setPassword("10000");

    //加载MyBatis核心配置类文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //获得sqlSession工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获得sqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql语句
    sqlSession.update("UserMapper.updateUser", user);
    //提交事务
    sqlSession.commit();
    //释放资源
    sqlSession.close();
}
  1. 修改操作注意问题
  • 修改语句使用update标签
  • 修改操作使用的API是sqlSession.update("命名空间.id",实体对象)

4.3 删除操作

  1. 在映射文件中编写sql语句:
xml 复制代码
<!--删除数据-->
<delete id="delUser" parameterType="java.lang.Integer">
    delete from user where id = #{id}
</delete>
  1. 测试
java 复制代码
//删除数据操作
@Test
public void test4() throws IOException {
    int id = 1;

    //加载MyBatis核心配置类文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //获得sqlSession工厂对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //获得sqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql语句
    sqlSession.delete("UserMapper.delUser", id);
    //提交事务
    sqlSession.commit();
    //释放资源
    sqlSession.close();
}
  1. 删除操作注意问题
  • 删除语句使用delete标签
  • Sql语句中使用#{任意字符串}方式引用传递的单个参数
  • 删除操作使用的API是sqlSession.delete("命名空间.id",Object)

5. MyBatis常用核心配置文件概述

  1. < environments>:数据库环境的配置,支持多环境配置,子标签如下:
    • <environment id="">:id指定当前环境名称,environment拥有<transactionManager><dataSource>子标签
      • <transactionManager type="">:指定事务管理类型
        - <dataSource type="">:指定当前数据源类型
  • 其中,事务管理器(transactionManager)类型有两种:

    • JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
    • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE 应用服务器的上下文)。 默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。
  • 数据源(dataSource)类型有三种:

    • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
    • POOLED:这种数据源的实现利用"池"的概念将 JDBC 连接对象组织起来。
    • JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
  1. <mappers>:主要介绍它的子标签<mapper>
    该标签的作用是加载映射的,加载方式有如下几种:
  • 使用相对于类路径的资源引用,例如:<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  • 使用完全限定资源定位符(URL),例如:< mapper url="file:///var/mappers/AuthorMapper.xml"/ >
  • 使用映射器接口实现类的完全限定类名,例如:< mapper class="org.mybatis.builder.AuthorMapper"/ >
  • 将包内的映射器接口实现全部注册为映射器,例如:< package name="org.mybatis.builder"/ >
  1. <properties>:实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
    新建并编写数据源的参数:

    jdbc.driver = com.mysql.jdbc.Driver
    jdbc.url = jdbc:mysql://127.0.0.1:3306/test
    jdbc.username = root
    jdbc.password = root

在配置文件中进行加载:

xml 复制代码
<properties resource="jdbc.properties"></properties>
  1. <typeAliases>:类型别名是为Java 类型设置一个短的名字。原来的类型名称配置如下

    配置typeAliases,为com.itheima.domain.User定义别名为user:

    可以直接使用user作为参数类型
    上面我们是自定义的别名,mybatis框架已经为我们设置好的一些常用的类型的别名:

6. MyBatis相应API

  1. SqlSession工厂构建器SqlSessionFactoryBuilder,常用API:SqlSessionFactory build(InputStream inputStream),通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象
java 复制代码
 //加载MyBatis核心配置类文件
 InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
 //获得sqlSession工厂对象
 SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
 SqlSessionFactory sqlSessionFactory = factoryBuilder.build(resourceAsStream);
 //获得sqlSession对象

其中, Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下、文件系统或一个 web URL 中加载资源文件。

  1. SqlSession工厂对象SqlSessionFactory:SqlSessionFactory 有多个个方法创建 SqlSession 实例。常用的有如下两个:
方法 解释
openSession() 会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中
openSession(boolean autoCommit) 参数为是否自动提交,如果设置为true,那么不需要手动提交事务
java 复制代码
//获得sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();

//获得sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
  1. SqlSession会话对象:
    SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。执行语句的方法主要有:
  • <T> T selectOne(String statement, Object parameter):查询返回一个对象
  • <E> List<E> selectList(String statement, Object parameter):查询返回一个列表
  • int insert(String statement, Object parameter):插入数据
    - int update(String statement, Object parameter):更新数据
  • int delete(String statement, Object parameter):删除数据
  • void commit():提交事务
    - void rollback():回滚事务

7.MyBatis代理开发

采用 Mybatis 的代理开发方式实现 DAO 层的开发,这种方式是我们后面进入企业的主流。Mapper 接口开发方法只需要程序员编写Mapper 接口(相当于Dao 接口),由Mybatis 框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper 接口开发需要遵循以下规范:

  • Mapper.xml文件中的namespace与mapper接口的全限定名相同
  • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
  • Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

    定义Mapper接口(Dao):
java 复制代码
package com.zhxd.dao;

import com.zhxd.domain.User;

public interface UserMapper {
    User findById(int id);
}

编写映射文件:

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.zhxd.dao.UserMapper">
    <select id="findById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>
</mapper>

测试:

java 复制代码
@Test
public void test() throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //使用MyBatis生成的UserMapper接口的实现类
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.findById(2);
    System.out.println(user);
}

8.动态sql语句

8.1动态 SQL 之<if>

我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件,这种情况在我们的多条件组合查询中经常会碰到。

  • if 的语法:<if test="条件">sql语句<if>
  • 案例:根据条件进行数据查询
  1. 编写SQL语句
xml 复制代码
<select id="findByCondition" parameterType="user" resultType="user">
    select * from user
    <where>
        <if test="id != 0">
            and id = #{id}
        </if>
        <if test="username != null">
            and username = #{username}
        </if>
        <if test="password != null">
            and password = password
        </if>
    </where>
</select>
  1. 测试:
java 复制代码
@Test
public void test2() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    User user = new User();
    user.setUsername("liming");

    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = userMapper.findByCondition(user);
    System.out.println(userList);
}

8.2动态 SQL 之<foreach>

循环执行sql的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)。

  • 语法:<foreach collection=" " open=" " close=" " item=" " sparator=" ">
    • collection:代表要遍历的集合元素,注意编写时不要写#{},集合时写list,数组时写array
    • open:代表语句的开始部分
    • close:代表结束部分
    • item:代表遍历集合的每个元素,生成的变量名
    • sperator:代表分隔符
  • 案例:以用户ID作为集合查询用户
  1. 编写SQL语句
xml 复制代码
<select id="findByIds" resultType="user" parameterType="list">
    select * from user
    <where>
        <foreach collection= "list" open = "id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
  1. 测试
java 复制代码
@Test
public void test3() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    List<Integer> idList = new ArrayList<Integer>();
    idList.add(1); idList.add(2); idList.add(3);

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.findByIds(idList);
    System.out.println(userList);
}

8.2动态 SQL 之<sql>

Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的

8.MyBatis核心配置文件深入

8.1 typeHandlers标签

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。

typeHandlers标签可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。


为User添加birthday属性,类型为Date类型,为数据库添加birthday字段类型为bigint类型

开发步骤:

  1. 定义转换类继承类BaseTypeHandler<T>
  2. 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法。
java 复制代码
package com.zhxd.utils.typehandler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class MyDateTypeHandler extends BaseTypeHandler<Date>{
    //将Java数据类型转化成数据库的数据类型
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
        Long time = date.getTime();
        preparedStatement.setString(i, time+ "");
    }

    //将数据库的数据库类型转换成java的数据类型
    //String s参数 需要转换的字段名称
    //resultSet 查询出来的结果集
    public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
        Long time = resultSet.getLong(s);
        Date date = new Date(time);
        return date;
    }

    //将数据库的数据库类型转换成java的数据类型
    //int i参数代表的是第几个字段的意思
    public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
        Long time = resultSet.getLong(i);
        Date date = new Date(time);
        return date;
    }

    //将数据库的数据库类型转换成java的数据类型
    public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        Long time = callableStatement.getLong(i);
        Date date = new Date(time);
        return date;
    }
}
  1. 在MyBatis核心配置文件中进行注册
xml 复制代码
<!--配置类型转换器-->
<typeHandlers>
    <typeHandler handler="com.zhxd.utils.typehandler.MyDateTypeHandler"></typeHandler>
</typeHandlers>
  1. 测试转换是否正确
java 复制代码
@Test
public void test4() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    User user = new User();
    user.setUsername("jacky");
    user.setPassword("88888888");
    user.setBirthday(new Date(2000, 1, 19));

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.addUser(user);
    sqlSession.commit();
}

8.2plugins标签

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即

可获得分页的相关数据。

首先在UserMapper接口中添加查询用户的方法:

编写好Sql语句:

分页的开发步骤:

  1. 导入通用PageHelper的坐标
xml 复制代码
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>3.7.5</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.github.jsqlparser/jsqlparser -->
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>0.9.1</version>
</dependency>
  1. 在mybatis核心配置文件中配置PageHelper插件
xml 复制代码
<plugins>
    <!--分页助手插件配置-->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <!--指定方言 mysql oracle sqlserver-->
        <property name="dialect" value="mysql"></property>
    </plugin>
</plugins>
  • plugins>中嵌套<plugin>,在plugin的拦截器(interceptor)属性配置上分页插件,在分页插件中配置方言,指定数据库。
  1. 测试分页数据获取
java 复制代码
@Test
public void test5() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    PageHelper.startPage(1,3);
    List<User> userPage = mapper.findAllUser();
    for (User user:userPage) {
        System.out.println(user);
    }
    sqlSession.close();
}
  • PageHelp对象设置分页:

    • PageHelper.startPage(pageNum,pageSIze):pageNum:页数,pageSize:显示的条数
  • 获得分页相关的其他参数,需要用到PageInfo对象

    • PageInfo的构造方法:PageInfo<T>(List<T> list)
    • int getTotal():总条数
    • int getPages():总页数
    • int getPageNum():当前页
    • int getPageSize():每页显示长度
    • boolean isIsFirstPage():是否是第一页
    • boolean isIsLastPage():是否是最后一页
java 复制代码
@Test
public void test5() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    PageHelper.startPage(1,3);
    List<User> userPage = mapper.findAllUser();
    for (User user:userPage) {
        System.out.println(user);
    }
    //获取分页的参数
    PageInfo<User> userPageInfo = new PageInfo<User>(userPage);
    //总条数
    System.out.println("总条数:"+ userPageInfo.getTotal());
    //总页数
    System.out.println("总页数:"+userPageInfo.getPages());
    //当前页
    System.out.println("当前页:"+userPageInfo.getPageNum());
    //每页显示的长度
    System.out.println("每页显示长度:"+userPageInfo.getPageSize());
    //是否是第一页
    System.out.println("是否是第一页:"+userPageInfo.isIsFirstPage());
    //是否是最后一页
    System.out.println("是否是最后一页:"+userPageInfo.isIsLastPage());

    sqlSession.close();
}

9. MyBatis多表操作

9.1一对一关系查询

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户。

搭建对应的环境:

  1. 新建orders表,uid使用user表的id作为外键:


    新建Order实体类,使用引用对象User作为订单的关联关系:
java 复制代码
package com.zhxd.domain;

import java.util.Date;

public class Order {
    private int id;
    private Date orderTime;
    private double total;
    private User user;

    public int getId() {
        return id;
    }

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

    public Date getOrderTime() {
        return orderTime;
    }

    public void setOrderTime(Date orderTime) {
        this.orderTime = orderTime;
    }

    public double getTotal() {
        return total;
    }

    public void setTotal(double total) {
        this.total = total;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", orderTime=" + orderTime +
                ", total=" + total +
                ", user=" + user +
                '}';
    }
}

一对一查询的语句:

复制代码
select *,o.id oid from orders o join user u on u.id = o.uid;


问题的关键在于查询出来的数据如何解决数据表字段与实体没有一一对应的映射关系

  • 我们可以使用<ResultMap>标签进行映射处理:
    • 子标签<id>:对应实体中的id
    • 子标签<result>:使用两个属性进行关系映射
    • id和result拥有两个属性:
      • column:数据表字段名字
      • property: 实体属性名字
xml 复制代码
<!--
    resultMap手动指定数据表字段与实体属性的映射关系
    column:数据表字段名字
    property:实体属性名字
   -->
<resultMap id="orderMap" type="com.zhxd.domain.Order">
    <id column="oid" property="id"></id>
    <result column="ordertime" property="orderTime"></result>
    <result column="total" property="total"></result>
    <result column="uid" property="user.id"></result>
    <result column="username" property="user.username"></result>
    <result column="password" property="user.password"></result>
    <result column="birthday" property="user.birthday"></result>
</resultMap>

把查询结果(resultType)的类型改为resultMap的id值

xml 复制代码
<select id="findOrders" resultMap="orderMap">
    select *,o.id oid from orders o join user u on u.id = o.uid
</select>

还可以对上述代码做一个优化,使用association进行页映射处理,可以指定当前实体的属性名对应的属性类型

  • association有两个属性值:
    • property:当前实体(order)的属性名称
    • javaType:当前实体(order)中的属性类型(com.zhxd.domain.User)
xml 复制代码
<!--
    resultMap手动指定数据表字段与实体属性的映射关系
    column:数据表字段名字
    property:实体属性名字
   -->
<resultMap id="orderMap" type="com.zhxd.domain.Order">
    <id column="oid" property="id"></id>
    <result column="ordertime" property="orderTime"></result>
    <result column="total" property="total"></result>
    
    <!--association指定当前实体的属性名对应的属性类型
        property:当前实体(order)的属性名称
        javaType:当前实体(order)中的属性类型(com.zhxd.domain.User)
    -->
    <association property="user" javaType="user">
        <id column="uid" property="id"></id>
        <result column="username" property="username"></result>
        <result column="password" property="password"></result>
        <result column="birthday" property="birthday"></result>
    </association>
</resultMap>

9.2 一对多查询

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单。

为User新增一个orderList属性,代表该用户下的所有订单:

java 复制代码
package com.zhxd.domain;

import java.util.Date;
import java.util.List;

public class User {
    private int id;
    private String username;
    private String password;
    private Date birthday;
    //新增属性
    private List<Order> orderList;

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Date getBirthday() {
        return birthday;
    }

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

    //重写toString
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", birthday=" + birthday +
                ", orderList=" + orderList +
                '}';
    }
}

查询用户下的订单SQL语句:

复制代码
select *, o.id oid from user u left outer join orders o on u.id = o.uid;

修改映射文件UserMapper.xml中的findAllUser:

xml 复制代码
<resultMap id="userMap" type="user">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
    <result column="birthday" property="birthday"></result>
    <collection property="orderList" ofType="order">
        <id column="oid" property="id"></id>
        <result column="ordertime" property="orderTime"></result>
        <result column="total" property="total"></result>
    </collection>
</resultMap>

<select id="findAllUser" resultMap="userMap">
    select *, o.id oid from user u left outer join orders o on u.id = o.uid
</select>

使用<collection>标签封把结果封装成集合:

  • <collection property=" " ofType=" ">

  • property:集合名称

  • ofType:当前集合的类型

9.3多对多查询

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用,多对多查询的需求:查询用户同时查询出该用户的所有角色,多对多查询的做法和一对多相似,只不过多了张中间表

新建角色实体类:

java 复制代码
package com.zhxd.domain;

public class Role {
    private int id;
    private String roleName;
    private String roleDecs;

    public int getId() {
        return id;
    }

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

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDecs() {
        return roleDecs;
    }

    public void setRoleDecs(String roleDecs) {
        this.roleDecs = roleDecs;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", roleName='" + roleName + '\'' +
                ", roleDecs='" + roleDecs + '\'' +
                '}';
    }
}

为User添加属性

java 复制代码
public class User {
    private int id;
    private String username;
    private String password;
    private Date birthday;
    //新增roleList属性
    private List<Role> roleList;
	//省略get、set和toString方法
}

新建sys_role和sys_user_role表

查询用户被哪些角色所拥有,用户拥有哪些角色的SQL语句:

复制代码
select * from user u join sys_user_role ur on u.id = ur.userId join sys_role r on r.id=ur.roleId;

修改UserMapper.xml

xml 复制代码
<resultMap id="userMap" type="user">
    <id column="id" property="id"></id>
    <result column="username" property="username"></result>
    <result column="password" property="password"></result>
    <result column="birthday" property="birthday"></result>
    <collection property="roleList" ofType="role">
        <id column="roleid" property="id"></id>
        <result column="roleName" property="roleName"></result>
        <result column="roleDesc" property="roleDesc"></result>
    </collection>
</resultMap>

<select id="findAllUser" resultMap="userMap">
    select * from sys_user u join sys_user_role ur on u.id = ur.userId join sys_role r on r.id=ur.roleId
</select>

测试:

java 复制代码
@Test
public void test5() throws IOException {
   InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
   SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
   SqlSession sqlSession = sqlSessionFactory.openSession();

   UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   List<User> userList = mapper.findAllUser();
   for(User user:userList) {
       System.out.println(user);
   }
   sqlSession.close();
}

10.MyBatis注解开发

10.1 MyBatis的常用注解

  1. @Insert:实现新增
  2. @Update:实现更新
  3. @Delete:实现删除
  4. Select:实现查询
  5. Result:实现结果集封装
  6. Results:可以与@Result 一起使用,封装多个结果集
  7. One::实现一对一结果集封装
  8. @Many:实现一对多结果集封装

10.2利用注解实现基本的CRUD

新建一个UserMapper接口,在UserMapper中添加增删改查方法:

java 复制代码
public interface UserMapper{
    //查询所有用户
    public List<User> findAllUser();

    //根据id查询用户
    public User findUserById(long id);

    //插入用户
    public void addUser(User user);

    //更新用户信息
    public void updateUser(User user);

    //删除用户
    public void delUser(long id);
}

在方法上面添加@Select("SQL语句")/ @Insert("SQL语句")/ @Delete("SQL语句")/ @Update("SQL语句")

java 复制代码
public interface UserMapper{
    @Select("select * from user")
    //查询所有用户
    public List<User> findAllUser();

    @Select("select * from user where id = #{id}")
    //根据id查询用户
    public User findUserById(long id);

    @Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
    //插入用户
    public void addUser(User user);

    @Update("update user set username = #{username},password = #{password},birthday= #{birthday} where id = #{id}")
    //更新用户信息
    public void updateUser(User user);

    @Delete("delete from user where id = #{id}")
    //删除用户
    public void delUser(long id);
}

在MyBatis配置文件中配置需要扫描的包:

xml 复制代码
<!--配置映射关系-->
<mappers>
    <package name="com.zhxd.dao"></package>
</mappers>

测试代码:

java 复制代码
public class TestAnno {
    private UserMapper mapper;
    
    @Before//在执行测试代码前先执行Before中的内容
    public void before() throws IOException {
        InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSession sqlSession = new SqlSessionFactoryBuilder().build(resourceAsStream).openSession(true);
        mapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    //查询所有用户
    public void testSelectAll() {
        List<User> userList = mapper.findAllUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    //根据id查询用户
    public void testSelectById() {
        User user = mapper.findUserById(2);
        System.out.println(user);
    }

    @Test
    //插入用户
    public void insertUser() {
        User user = new User();
        user.setUsername("lingling");
        user.setPassword("12345678910");
        user.setBirthday(new Date(2008, 10, 29));

        mapper.updateUser(user);
    }

    @Test
    //更新用户信息
    public void updateUser() {
        User user = new User();
        user.setId(12);
        user.setUsername("lisa");
        user.setPassword("12345678910");
        user.setBirthday(new Date(2008, 10, 29));

        mapper.updateUser(user);
    }

    @Test
    //删除用户
    public void delUser() {
        mapper.delUser(12);
    }
}

10.3MyBatis的注解实现复杂映射开发

实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,使用注解开发后,我们可以使用@Results注解,@Result注解@One注解@Many注解组合完成复杂关系的配置

  • @Results:代替的是标签<resultMap>该注解中可以使用单个@Result注解,也可以使用@Result集
    合。使用格式:@Results({@Result(),@Result()})或@Results(@Result())
  • @Result:
    • 代替了<id>标签和<result>标签
    • @Result中属性介绍:
      • column:数据库的列名
      • property:需要装配的属性名
      • one:需要使用的@One 注解(@Result(one=@One)()))
      • many:需要使用的@Many 注解(@Result(many=@many)()))
  • @One (一对一):代替了<assocation> 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。@One注解属性介绍:
    • select: 指定用来多表查询的 sqlmapper
    • @Result(column=" ",property="",one=@One(select=""))
  • @Many(多对一):代替了<collection>标签, 是是多表查询的关键,在注解中用来指定子查询返回对象集合。使用格式:@Result(property="",column="",many=@Many(select=""))

10.3.1实现一对一关系

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户,一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户

编写OrderMap.java

java 复制代码
@Select("select *, o.id oid from orders o inner join user u on u.id = o.uid")
@Results({
    @Result(column = "oid", property = "id"),
    @Result(column = "ordertime", property = "orderTime"),
    @Result(column = "total", property = "total"),
    @Result(column = "uid", property = "user.id"),
    @Result(column = "username", property = "user.username"),
    @Result(column = "password", property = "user.password"),
    @Result(column = "brithday", property = "user.brithday"),
})
List<Order> findOrderByUser();

对上述代码进行优化,添加相当于<association>作用的功能:

  1. 在UserMapper中添加findById方法:
java 复制代码
@Select("select * from user where id = #{id}")
public User findById(long id);
  1. 在UserMapper中使用@one进行优化:
java 复制代码
@Select("select * from orders")
@Results({
    @Result(column = "id", property = "id"),
    @Result(column = "ordertime", property = "orderTime"),
    @Result(column = "total", property = "total"),
    @Result(
            property = "user",//要封装的属性名称
            column = "id",//根据那个字段去查询user表中的数据
            javaType = User.class,//要封装的实体类型
            //select属性 代表查询哪个接口的方法获取数据
            one = @One(select = "com.zhxd.dao.UserMapper.findById")
    )
})
List<Order> findOrderByUser();

测试结果:

java 复制代码
@Test
public void findOrderByUser() {
    List<Order> orderList = orderMapper.findOrderByUser();
    for (Order order : orderList) {
        System.out.println(order);
    }
}

10.3.2实现一对多的关系

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户,用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

对应的sql语句:

select * from user

select * from orders where uid=查询出用户的id

  1. 在UserMapper添加方法:
java 复制代码
@Select("select * from User")
@Results({
        @Result(column = "id", property = "id"),
        @Result(column = "username", property = "username"),
        @Result(column = "password", property = "password"),
        @Result(column = "birthday", property = "birthday"),
        @Result(
                property = "orderList",
                column = "id",
                javaType = List.class,
                many = @Many(select = "com.zhxd.dao.OrderMapper.findByUid")
        )
})
public List<User> findUserOrders();
  1. 在OrderMap添加findByUid方法
java 复制代码
@Select("select * from orders where uid = #{id}")
Order findByUid(long id);
  1. 测试结果:
java 复制代码
@Test
public void testUserOrders() {
    List<User> userList = mapper.findUserOrders();
    for (User user : userList) {
        System.out.println(user);
    }
}

10.3.3实现多对多的关系

用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用,多对多查询的需求:查询用户同时查询出该用户的所有角色

对应的sql语句:

select * from user;

select * from role r,user_role ur where r.id=ur.role_id and ur.user_id=用户的id

  1. 在UserMap添加方法:
java 复制代码
@Select("select * from user")
@Results({
        @Result(column = "id", property = "id"),
        @Result(column = "username", property = "username"),
        @Result(column = "password", property = "password"),
        @Result(column = "birthday", property = "birthday"),
        @Result(
                property = "roleList",
                column = "id",
                javaType = List.class,
                many=@Many(select = "com.zhxd.dao.RoleMapper.findByUid")
        )
})
public List<User> findUserRoles();
  1. 新增RoleMapper
java 复制代码
public interface RoleMapper {
    @Select("select * from sys_user_role ur join sys_role r where ur.roleid = r.id and ur.userid=#{id}")
    List<Role> findByUid(long id);
}
  1. 测试
java 复制代码
@Test
public void testUserRoles() {
    List<User> userList = mapper.findUserRoles();
    for (User user : userList) {
        System.out.println(user);
    }
}
相关推荐
Ashore11_1 分钟前
用户中心项目—需求分析
java
johnrui8 分钟前
springboot接口限流操作
java·spring boot·后端
Flittly18 分钟前
【SpringAIAlibaba新手村系列】(9)Text to Image 文本生成图像技术
java·spring boot·agent
Flittly19 分钟前
【SpringAIAlibaba新手村系列】(10)Text to Voice 文本转语音技术
java·spring boot·agent
诸葛大钢铁22 分钟前
Java实现Excel文件合并
java·windows·excel
黎明丶之前26 分钟前
Spring Cloud Gateway 升级与 Bucket4j 限流实践
java·spring cloud
程序员木圭31 分钟前
05-告别逻辑混乱!Java 流程控制让代码学会"判断和循环"
java·后端
yaaakaaang35 分钟前
三、抽象工厂模式
java·抽象工厂模式
kongba00735 分钟前
复刻 Claude Code 项目御马术缰绳系统 harness engineering 落地蓝图
java·linux·服务器
tERS ERTS37 分钟前
Spring Cloud gateway 路由规则
java