Mybatis快速入门

入门

简介

  1. 原始jdbc操作的缺点
    • 数据库连接创建、释放频繁造成系统资源浪费从而影响系统性能
    • sql语句在代码中硬编码,造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码
    • 查询操作时,需要手动将结果集中的数据手动封装到实体中,插入操作时,需要手动将实体的数据设置到sql语句的占位符位置
  2. 优化方案
    • 使用数据库连接池初始化连接资源
    • 将sql语句抽取到xml配置文件中
    • 使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
  3. 什么是mybatis?
    • mybatis是一个优秀的基于Java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程
    • mybatis通过xml或注解的方式将要执行的各种statement配置起来,并通过Java对象和statement中sql的动态参数进行映射生成最终执行的sql语句
    • 最后mybatis框架执行sql并将结果映射为Java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对jdbc进行了封装,屏蔽了jdbc api底层访问细节,使我们不用与jdbc api打交道,就可以完成对数据库的持久化操作

基本操作

  1. 开发步骤

    • 添加MyBatis的坐标

    • 创建数据表

    • 编写实体类

    • 编写映射文件XxxMapper.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="pers.zero.domain.User">
              SELECT * FROM user
          </select>
      </mapper>
      
    • 编写核心文件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="development">
              <environment id="development">
                  <transactionManager type="JDBC"></transactionManager>
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=UTC"/>
                      <property name="username" value="root"/>
                      <property name="password" value="xxx"/>
                  </dataSource>
              </environment>
          </environments>
          
          <!-- 加载映射文件 -->
          <mappers>
              <mapper resource="pers/zero/mapper/UserMapper.xml"></mapper>
          </mappers>
      
      </configuration>
      
    • 测试(使用mybaits原生API)

      public void test1() throws IOException {
          //获得核心配置文件
          InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
          //获得session工厂对象
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
          //获得session会话对象
          SqlSession sqlSession = sqlSessionFactory.openSession();
          //执行操作
          List<User> userList = sqlSession.selectList("userMapper.findAll");
          //打印数据
          System.out.println(userList);
          //释放资源
          sqlSession.close();
      }
      
  2. 增删改查操作

    • <!-- 插入 -->
      <insert id="save" parameterType="pers.zero.domain.User">
          <!-- #{}里的是实体类的属性名 -->
          INSERT INTO user VALUES(#{id},#{username},#{password})
      </insert>
      
      int result = sqlSession.insert("userMapper.save", user);
      sqlSession.commit();    //注意:mybatis默认使用事务,执行修改操作需要提交事务
      
    • <!-- 删除 -->
      <delete id="delete" parameterType="java.lang.Integer">
          <!-- 当参数只有一个且是简单类型时#{}里的名字可以任意取 -->
          DELETE FROM user WHERE id=#{id}
      </delete>
      
      int result = sqlSession.delete("userMapper.delete", 4);
      sqlSession.commit();
      
    • <!-- 修改 -->
      <!-- 若参数类型是List<Xxx>之类的,parameterType应该设置为list等 -->
      <update id="update" parameterType="pers.zero.domain.User">
          UPDATE user SET username=#{username},password=#{password} WHERE id=#{id}
      </update>
      
      int result = sqlSession.update("userMapper.update", user);
      sqlSession.commit();
      
    • <!-- 查询 -->
      <!-- 注意:尽管该语句是为了获得多条数据,但resultType应该设置为查询一行数据后返回的类型 -->
      <select id="findAll" resultType="pers.zero.domain.User">
          SELECT * FROM user
      </select>
      
      List<User> userList = sqlSession.selectList("userMapper.findAll");
      

详解

核心配置文件

  1. 核心配置文件层级关系

    • configuration 配置
      • properties 属性
      • settings 设置
      • typeAliases 类型别名
      • typeHandlers 类型处理器
      • objectFactory 对象工厂
      • plugins 插件
      • environments 环境
        • environment 环境变量
          • transactionManager 事务管理器
          • dataSource 数据源
      • databaseidProvider 数据库厂商标识
      • mappers 映射器
  2. 标签放置顺序

    properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

  3. environments标签

    数据库环境配置,支持多环境配置

    • 结构:image-20210216171059708
    • 事务管理器(transactionManager):
      • JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域
      • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下他会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为
    • 数据源(dataSource):
      • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接
      • POOLED:这种数据源的实现利用”池“的概念将JDBC连接对象组织起来
      • JNDI:这个数据源的实现是为了能在EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用
  4. mapper标签

    改标签的作用是加载映射,加载方式有如下几种

    • 使用相对于类路径的资源引用,如<mapper resource=“org/mybatis/builder/XxxMapper.xml”>
    • 使用完全限定资源定位符(URL),如<mapper class=“file:///var/mappers/XxxMapper.xml”>
    • 使用映射器接口实现类的完全限定类名,如<mapper class=“org.mybatis.builder.XxxMapper”>
    • 将包内的映射器接口实现全部注册为映射器,如<package name=“org.mybatis.builder”>
  5. Properties标签

    实际开发中,我们习惯将数据源的配置信息单独抽取成一个properties文件,改标签可以加载额外配置的properties文件

    image-20210216184049257

  6. typeAliases标签

    • 在核心配置文件中为类设置别名后我们就可以在映射文件中使用别名来配置映射

      image-20210216185037512

    • mybatis框架为我们设置的一些常用别名

      image-20210216185214061

  7. typeHandlers标签

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

      image-20210217222644203

    • 我们可以重写类型转换器或创建自己的类型处理器来处理不支持的或非标准的类型,例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成Java的Date,即Java的Date与数据库的varchar毫秒值之间转换。

    • 开发步骤:

      • 定义转换类继承类BaseTypeHandler<T>

      • 覆盖4个未实现的方法,其中setNonNullParameter为Java程序设置到数据库的回调方法,getNullableResult为查询时mysql的字符串类型转换成java的Type类型的方法

        public class DateTypeHandler extends BaseTypeHandler<Date> {
            //将java类型转为数据库需要的类型
            //int是字段位置
            @Override
            public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date date, JdbcType jdbcType) throws SQLException {
                long time = date.getTime();
                preparedStatement.setLong(i,time);
            }
        
            //将数据库中类型转为java类型
            //String是要转换的字段名称,ResultSet是结果集
            @Override
            public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
                long aLong = resultSet.getLong(s);
                Date date = new Date(aLong);
                return date;
            }
        
            //将数据库中类型转为java类型
            //int是字段位置
            @Override
            public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
                long aLong = resultSet.getLong(i);
                Date date = new Date(aLong);
                return date;
            }
        
            //将数据库中类型转为java类型
            @Override
            public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
                long aLong = callableStatement.getLong(i);
                Date date = new Date(aLong);
                return date;
            }
        }
        
      • 在mybatis核心配置文件中进行注册

        <typeHandlers>
            <typeHandler handler="pers.zero.handler.DateTypeHandler"/>
        </typeHandlers>
        
      • 测试

  8. plugins标签

    • mybatis可以使用第三方的插件来对功能进行扩展,例如插件——分页助手PageHelper可以将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

    • 开发步骤:

      • 导入PageHelper的坐标

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>3.7.5</version>
        </dependency>
        <dependency>
            <groupId>com.github.jsqlparser</groupId>
            <artifactId>jsqlparser</artifactId>
            <version>0.9.1</version>
        </dependency>
        
      • 在mybatis核心配置文件中配置PageHelper插件

        <plugins>
            <plugin interceptor="com.github.pagehelper.PageHelper">
                <!-- 指定方言,不同数据库的分页关键字可能不同 -->
                <property name="dialect" value="mysql"/>
            </plugin>
        </plugins>
        
      • 测试分页数据获取

        //设置分页相关参数:当前页、每页显示的条数
        PageHelper.startPage(2,2);
        List<User> userList = mapper.findAll();
        for (User user:userList)
            System.out.println(user);
        
        //获得与分页相关的参数:如当前页、上一页
        PageInfo<User> pageInfo = new PageInfo<User>(userList);
        System.out.println("当前页:"+pageInfo.getPageNum());
        System.out.println("每页条数:"+pageInfo.getPageSize());
        System.out.println("总条数:"+pageInfo.getTotal());
        System.out.println("总页数:"+pageInfo.getPages());
        System.out.println("上一页:"+pageInfo.getPrePage());
        System.out.println("下一页:"+pageInfo.getNextPage());
        System.out.println("是否是第一页:"+pageInfo.isIsFirstPage());
        System.out.println("是否是最后一页:"+pageInfo.isIsLastPage());
        

映射文件

  1. 基本标签

    • mapper标签:根标签,最好指定命名空间
    • select、insert、delete、update标签:查增删改
  2. 动态sql语句

    • sql标签:用于抽取sql语句

      <sql id="selectUser">SELECT * FROM user</sql>
      
    • include标签:用于引用被抽取的sql语句

      <include refid="selectUser"></include>
      
    • where标签:会动态判断标签体中是否存在待添加语句,若存在则在语句中添加where与待添加语句,否则不添加where

    • if标签:若test通过则向语句添加if标签体中的语句,一般与where一起使用,注意标签体中的每个条件之前应该有一个and(第一个条件前有个and并不会导致错误),例如:

      <select id="findByCondition" parameterType="user" resultType="user">
          SELECT * FROM user
          <where>
              <!-- 达到支持0~3个条件的动态查询效果 -->
              <if test="id!=0">    <!-- 如果id!=0,将id值加入查询条件中 -->
                  AND id=#{id}
              </if>
              <if test="username!=null">
                  AND username=#{username}
              </if>
              <if test="password!=null">
                  AND password=#{password}
              </if>
          </where>
      </select>
      
    • foreach标签:循环执行sql的拼接操作,例如:

      <!-- SELECT * FROM user WHERE id IN(id1,id2,id3) -->
      <select id="findByIds" parameterType="list" resultType="user">
          SELECT * FROM user
          <where>
              <foreach collection="list" open="id IN(" close=")" item="id" separator=",">
                  #{id}
              </foreach>
          </where>
      </select>
      
  3. 多表操作

    • resultMap标签:手动指定字段名与类属性的映射关系
      • association标签:指定复杂类属性的映射关系
      • collection标签:指定集合属性的元素的映射关系

API

  1. SqlSessionFactoryBuilder的实例方法SqlSessionFactory build(InputStream inputStream)

    • 作用:使用SqlSession工厂构建器创建工厂对象,通过加载mybatis的核心文件的输入流形式构建一个SqlSessionFactory对象

    • 示例

      //获得核心配置文件
      InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
      //获得session工厂对象
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
      
  2. SqlSessionFactory的实例方法openSession()

    • 作用:获得SqlSession会话对象
    • 2种常用重载
      • openSession():会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中
      • openSession(boolean autoCommit):参数为是否自动提交,如果设置为true,那么不需要手动提交事务
  3. SqlSession的多个方法

    • 代理开发方式:
      • getMapper(Class<T> class):获得Mapper接口的动态代理对象
    • 执行语句:
      • <T> T selectOne(String statement,Object parameter):查询一个对象
      • <E> List<E> selectList(String statement,Object parameter):查询多个对象并封装到List中
      • int insert(String statement,Object parameter):插入
      • int update(String statement,Object parameter):更新
      • int delete(String statement,Object parameter):删除
    • 操作事务:
      • void commit():事务提交
      • void rollback():事务回滚

基于MyBatis的Dao层实现

  1. 传统实现:接口+实现类

  2. 代理开发方式:

    • 采用mybatis的代理开发方式实现DAO层的开发是一种主流方式,Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。这种开发方式相当于将配置文件看作是实现类,我们依然应该先设计接口

    • Mapper接口开发需要遵循以下规范(第1条相当于导入接口所在包,剩余3条相当于实现方法):

      • Mapper.xml文件中的namespace与mapper接口的全限定名相同
      • Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
      • Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同,注意:当接口的返回值是集合时配置文件中的返回值应该是集合中元素的类型
      • Mapper接口方法的输出参数类型和mapper,xml中定义的每个sql的resultType的类型相同
    • 图解(订正:接口UserDao应该是UserMapper)

      image-20210217105308940

    • 示例

      public interface UserMapper {
          public List<User> findAll() throws IOException;
          public User findById(int id);
      }
      
      <?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="pers.zero.dao.UserMapper">
          <!-- 查询所有 -->
          <select id="findAll" resultType="user">
              SELECT * FROM user
          </select>
          <!-- 按id查询 -->
          <select id="findById" parameterType="int" resultType="user">
              SELECT * FROM user WHERE id=#{id}
          </select>
      </mapper>
      
      public static void main(String[] args) throws IOException {
          InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
          SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
          SqlSession sqlSession = sqlSessionFactory.openSession();
          //获得动态生成的实现类,相当于传统的UserMapper userMapper = new UserMapperImpl();
          UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
          System.out.println(userMapper.findAll());
          System.out.println(userMapper.findById(1));
          sqlSession.close();
      }
      

多表操作(存在外键)

  1. 一对一查询

    • 情景:一个订单对应一个用户

    • 模型:订单表、用户表,其中订单表的uid被用户表的id约束

    • 需求:查询订单,同时查询对应用户

    • sql语句执行效果

      image-20210218155845320

    • 实体类

      public class Order {
          private int id;
          private Date ordertime;
          private double total;
          private User user;
      }
      public class User {
          private int id;
          private String username;
          private String password;
          private List<Order> orderList;
      }
      
    • OrderMapper映射文件(重点,手动设置查询结果的映射关系)

      <resultMap id="orderMap" type="order">
          <!-- 手动指定字段与实体属性的映射关系,column是字段名,property是实体属性名 -->
          <id column="oid" property="id"/>    <!-- 主键用id,其余用result -->
          <result column="total" property="total"/>
          <result column="ordertime" property="ordertime"/>
      	<association property="user" javaType="user">	<!-- 对复杂类属性最好这样写 -->
      		<id column="uid" property="id"/>	<!-- 主键用id,其余用result -->
              <id column="username" property="username"/>
              <id column="password" property="password"/>
      	</association>
      </resultMap>
      <select id="findAll" resultMap="orderMap">
          SELECT *,o.id oid FROM orders o,user u WHERE o.uid=u.id
      </select>
      
    • 查询结果

      Order{id=1, ordertime=Thu Feb 18 18:58:38 CST 2021, total=3000.0, user=User{id=1, username=‘中文测试’, password=‘a123456’, orderList=null}}
      Order{id=2, ordertime=Wed Feb 17 18:59:11 CST 2021, total=5800.0, user=User{id=1, username=‘中文测试’, password=‘a123456’, orderList=null}}
      Order{id=3, ordertime=Thu Feb 11 18:59:28 CST 2021, total=323.0, user=User{id=2, username=‘zhangSan’, password=‘123’, orderList=null}}

  2. 一对多查询

    • 情景:一个用户可以对应多个订单

    • 模型:订单表、用户表,其中订单表的uid被用户表的id约束

    • 需求:查询用户,同时查询对应订单

    • sql语句执行效果

      image-20210218155806206

    • 实体类

      public class User {
          private int id;
          private String username;
          private String password;
          private List<Order> orderList;
      }
      public class Order {
          private int id;
          private Date ordertime;
          private double total;
          private User user;
      }
      
    • UserMapper映射文件

      <resultMap id="userMap" type="user">
          <id column="uid" property="id"/>
          <result column="username" property="username"/>
          <result column="password" property="password"/>
          <!-- 集合属性,ofType是集合中元素的类型 -->
          <collection property="orderList" ofType="order">
              <id column="oid" property="id"/>
              <result column="total" property="total"/>
              <result column="ordertime" property="ordertime"/>
          </collection>
      </resultMap>
      <select id="findAll" resultMap="userMap">
          SELECT *,o.id oid FROM user u,orders o WHERE u.id=o.uid
      </select>
      
    • 查询结果

      User{id=1, username=‘中文测试’, password=‘a123456’,

      orderList=[

      ​ Order{id=1, ordertime=Thu Feb 18 18:58:38 CST 2021, total=3000.0, user=null},

      ​ Order{id=2, ordertime=Wed Feb 17 18:59:11 CST 2021, total=5800.0, user=null}

      ]}

      User{id=2, username=‘zhangSan’, password=‘123’, orderList=[Order{id=3, ordertime=Thu Feb 11 18:59:28 CST 2021, total=323.0, user=null}]}

  3. 多对多查询(与一对多sql语句不同,其余相同)

    • 情景:一个用户有多个角色,一个角色被多个用户使用

    • 模型:用户表、角色表、用户角色映射表(中间表),其中用户角色映射表存在两个外键约束(user_id与role_id)

    • 需求:查询用户同时查询出该用户的所有角色

    • sql语句执行效果

      image-20210218155606014

    • 实体类

      public class User {
          private int id;
          private String username;
          private String password;
          private List<Role> roleList;
      }
      public class Role {
          private int id;
          private String rolename;
      }
      
    • UserMapper映射文件

      <resultMap id="userRoleMap" type="user">
          <id column="userid" property="id"/>
          <result column="username" property="username"/>
          <result column="password" property="password"/>
          <collection property="roleList" ofType="role" >
              <id column="roleid" property="id"/>
              <result column="rolename" property="rolename"/>
          </collection>
      </resultMap>
      <select id="findUserAndRoleAll" resultMap="userRoleMap">
          SELECT * FROM user u,user_role ur,role r WHERE u.id=ur.userid AND ur.roleid=r.id
      </select>
      
    • 查询结果

      User{id=1, username=‘中文测试’, password=‘a123456’,

      roleList=[

      ​ Role{id=1, rolename=‘教授’},

      ​ Role{id=2, rolename=‘研究员’}

      ]}
      User{id=2, username=‘zhangSan’, password=‘123’,

      roleList=[

      ​ Role{id=1, rolename=‘教授’},

      ​ Role{id=3, rolename=‘讲师’}

      ]}

注解开发(复杂操作时不推荐使用)

必要配置

在核心配置文件中指定使用了注解的接口所在的包

<!-- 加载映射关系 -->
<mappers>
    <!-- 指定使用了注解的接口所在的包 -->
    <package name="pers.zero.mapper"/>
</mappers>

基本操作

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

public interface UserMapper {
    @Insert("INSERT INTO user VALUES(#{id},#{username},#{password},NULL)")
    public void save(User user);

    @Delete("DELETE FROM user WHERE id=#{id}")
    public void delete(int ids);

    @Select("SELECT * FROM user")
    public List<User> findAll();
    @Select("SELECT * FROM user WHERE id=#{id}")
    public User findById(int id);

    @Update("UPDATE user SET username=#{username},password=#{password} WHERE id=#{id}")
    public void update(User user);
}

多表查询

  1. 常用注解

    @Results:可以与@Result一起使用,封装多个结果集

    @Result:实现结果集封装

    @One:实现一对一结果集封装

    @Many:实现一对多结果封装

    image-20210218165311589

    image-20210218165422280

  2. 示例

    • 一对一查询

      //较灵活的方式:先查询一张表,再根据查询结果中受外键约束的字段去查询另一张表,返回拼接结果,相当于左连接
      @Select("SELECT * FROM orders")
      @Results({
              @Result(column = "id",property = "id"),
              @Result(column = "ordertime",property = "ordertime"),
              @Result(column = "total",property = "total"),
              @Result(
                  	//要封装的属性名
                      property = "user",
                  	//使用哪个方法来查询user表,注意该方法的返回值类型要与javaType一致
                      one = @One(select = "pers.zero.mapper.UserMapper.findById"),
                      //根据哪个字段去查user表的数据(方法实参)
                      column = "uid",
                  	//要封装属性的实体类型(方法返回值类型)
                      javaType = User.class
              )
      })
      public List<Order> findAll();
      
      /*
      相当于:
      	SELECT * FROM orders
      	SELECT * FROM user WHERE uid=xxx
      	拼接
      */
      
      //另一种方式
      @Select("SELECT *,o.id oid FROM orders o,user u WHERE o.uid=u.id")
      @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"),
      })
      public List<Order> findAll();
      
    • 一对多查询

      //会查出orderList为空的user,相当于左连接
      @Select("SELECT * FROM user")
      @Results({
              @Result(id = true,column = "id",property = "id"),
              @Result(column = "username",property = "username"),
              @Result(column = "password",property = "password"),
              @Result(
                      property = "orderList",
                  	//注意该方法的返回值类型为List<Order>,与javaType一致
                      many = @Many(select = "pers.zero.mapper.OrderMapper.findByUid"),
                      column = "id",
                      javaType = List.class
              )
      })
      public List<User> findUserAndOrderAll();
      
      /*
      相当于:
      	SELECT * FROM user
      	SELECT * FROM orders WHERE uid=xxx
      	拼接
      */
      
    • 多对多查询

      public interface RoleMapper {
          @Select("SELECT * FROM user_role ur,role r WHERE ur.roleid=r.id AND ur.userid=#{uid}")
          public List<Role> findByUid(int uid);
      }
      public interface UserMapper {
          @Select("SELECT * FROM user")
          @Results({
                  @Result(column = "id",property = "id"),
                  @Result(column = "username",property = "username"),
                  @Result(column = "password",property = "password"),
                  @Result(
                          property = "roleList",
                          many = @Many(select = "pers.zero.mapper.RoleMapper.findByUid"),
                          column = "id",
                          javaType = List.class
                  )
          })
          public List<User> findUserAndRolesAll();
      }
      
      /*
      相当于:
          SELECT * FROM user;
          SELECT * FROM user_role ur,role r WHERE ur.roleid=r.id AND ur.userid=xxx;
          拼接
      */
      
  3. 总结:基于注解的多表查询的关键在于如何让类的复杂类属性获得值

    • 从面向对象的角度看,多表查询的实质是一个实体类中含有一个类型为另一个实体类(或其集合)的成员,注意,第一个实体类对应的表中并不会含有这个成员的所有信息,我们要从多张表中获得数据填充第一个实体类的所有成员。

    • 开发步骤:

      • 我们可以先确定要在查询哪个实体类的同时查询出另一个实体类(如查询用户的同时查询出其对应角色)。将第一个记作类A,第二个记作类B,在类A中实现一个类B类型(或其集合)的属性。即

        class A{	//一对一
        	private B xxx;
        }
        
        class A{	//一对多/多对多,以下以此为例
            private List<B> xxx;
        } 
        
      • 在A对应的mapper接口中创建相应方法,返回值的类型是A或其集合,关键看要查几个A

      • 先使用@Select查询A对应的表a

        @Select("SELECT * FROM a")
        public List<A> findUserAndRolesAll();
        
      • 将表a的相关数据填入表A对应属性

      • 找出表a中与表b耦合的那个字段(一般会被其他表作为外键约束),记作key

      • 在B对应的mapper中实现一个方法:功能是根据key找到表b对应的数据、返回值类型是类A中的类B属性(或其集合)、参数是key

快速配置

  1. 映射文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace=>
        
    
    </mapper>
    
  2. 核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <properties resource="jdbc.properties"/>
        
        <!-- 自定义别名 -->
        <typeAliases>
            <package name=/>	<!-- 扫包,将该包下的类的别名设置为类名或其小写 -->
            <typeAlias type= alias=/>
        </typeAliases>
    	
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
                </dataSource>
            </environment>
        </environments>
        
        <!-- 加载映射文件 -->
        <mappers>
            <mapper resource=/>
            <package name=/> <!-- 扫包,加载资源文件对应包下所有映射文件/java包下含注解Mapper -->
        </mappers>
    </configuration>
    

参考:2020年IDEA版黑马Java就业班-进阶篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值