mybatis--使用

一、入门程序

直接上代码

sql:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` varchar(30) DEFAULT NULL COMMENT '性别',
  `address` varchar(10) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3687 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;


INSERT INTO `user`(`id`, `username`, `birthday`, `sex`, `address`) VALUES (1, 'lcl', '2020-11-23', '', '北京');
INSERT INTO `user`(`id`, `username`, `birthday`, `sex`, `address`) VALUES (2, 'lcl', '2020-11-23', '', '北京');
INSERT INTO `user`(`id`, `username`, `birthday`, `sex`, `address`) VALUES (3, 'qmm', '2020-11-23', '', '北京');

pom文件

        <!-- mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!-- mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.35</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

db.propertoes

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://******
jdbc.username=*******
jdbc.password=*******

UserMapper

<?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="test">
    <!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="com.lcl.galaxy.mybatis.common.domain.UserDo">
        select * from user where id = #{id}
    </select>
</mapper>

SqlMapperConfig

<?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="db.properties"></properties>
    <!--配置环境-->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务 -->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"></property>
                <property name="url" value="${jdbc.url}"></property>
                <property name="username" value="${jdbc.username}"></property>
                <property name="password" value="${jdbc.password}"></property>
            </dataSource>
        </environment>
    </environments>
    <!-- 配置映射文件的位置 -->
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>
</configuration>

UserDo

package com.lcl.galaxy.mybatis.common.domain;

import lombok.Data;

import java.util.Date;

@Data
public class UserDo {
    private int id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
}

UserDao

package com.lcl.galaxy.mybatis.demo.dao;

import com.lcl.galaxy.mybatis.common.domain.UserDo;

import java.util.List;

public interface UserDao {

        public UserDo findUserById(int id) throws Exception;
}

UserDaoImpl

package com.lcl.galaxy.mybatis.demo.dao.impl;

import com.lcl.galaxy.mybatis.common.domain.UserDo;
import com.lcl.galaxy.mybatis.demo.dao.UserDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

import java.util.List;

@Slf4j
public class UserDaoImpl implements UserDao {

    private SqlSessionFactory sqlSessionFactory;

    public UserDaoImpl(SqlSessionFactory sqlSessionFactory){
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public UserDo findUserById(int id) throws Exception {
        SqlSessionFactory sqlSessionFactory = this.sqlSessionFactory;
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserDo userDo = null;
        try {
            userDo = sqlSession.selectOne("test.findUserById",id);
        }finally {
            sqlSession.close();
        }
        return userDo;
    }
}

测试类

package com.lcl.galaxy.mybatis;

import com.lcl.galaxy.mybatis.common.domain.UserDo;
import com.lcl.galaxy.mybatis.demo.dao.UserDao;
import com.lcl.galaxy.mybatis.demo.dao.impl.UserDaoImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

@Slf4j
public class demoTest {

    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws Exception {
        SqlSessionFactoryBuilder sessionFactoryBuilder = new
                SqlSessionFactoryBuilder();
        InputStream inputStream =
                Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory = sessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception {
        UserDao userDao = new UserDaoImpl(sqlSessionFactory);
        UserDo user = userDao.findUserById(2);
        log.info("userDo====[{}]",user);
    }
}

  这里简单说一下Mapper.xml文件,每一个文件都有一个nameSpace,每一个sql语句都有一个id,因此在查询时使用nameSpace+id就定位到了唯一一条sql;

sql语句可以使用select、insert、update、delete标签来作为sql类型标记,parameterType是参数类型,resultType是出参类型。对于参数传递,可以使用#{}来进行传递,如果入参是简单类型(8中基本类型+String类型),#{}内可以随便写内容,否则要与入参一直;如果入参是对象,则参数名称要与对象的属性名称保持一致;如果返回值为对象,则查询字段要和对象属性保持一致。

二、基础应用

(一)Mapper代理开发模式

  在入门程序中,每一个实现类(例如UserDaoImpl)都要自己获取SqlSessionFactory以及SqlSession,且自己需要写impl实现类,对于mybatis,其提供了基于动态代理的Mapper开发模式。

  针对Mapper开发对象可以使用Xml模式和注解模式

     1、Xml方式

  针对Xml开发模式,只需要写Mapper接口和Mapper映射对象,不需要编写实现类,其中Mapper接口就是之前的Dao对象。

  对于开发时,需要遵循如下接口:

    1、Mapper映射文件的nameSpace要与Mapper接口的类路径一致

    2、Mapper映射文件的Statement的id要与Mapper接口中的方法名保持一致

    3、Mapper映射文件的ParameterType要与Mapper接口中方法的入参保持一致

    4、Mapper映射文件的ResultType要与Mapper接口中的返回值保持一致

  接下来,就直接上代码:

  新增UserMapper接口,内容同UserDao一致

package com.lcl.galaxy.mybatis.anno.mapper;

import com.lcl.galaxy.mybatis.common.domain.UserDo;

import java.util.List;

public interface UserMapper {
    public UserDo findUserById(int id) throws Exception;
}

新增UserMapper.xml文件,其中nameSpace为上述UserMapper接口的类路径

<?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.lcl.galaxy.mybatis.anno.mapper.UserMapper">
    <!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="com.lcl.galaxy.mybatis.common.domain.UserDo">
        select * from user where id = #{id}
    </select>
</mapper>

在SqlMapperConfig中新增上述xml配置

<mapper resource="mapper/anno/UserMapper.xml"></mapper>

新增测试类

由于UserMapper接口没有实现类,如何才能对其初始化调用其方法?其实,Mybatis在SqlSession接口中提供了一个getMapper方法,入参是Mapper接口类,返回值就是Mapper接口对象,那么就可以如此获得UserMapper对象,然后直接调用其提供的方法。

package com.lcl.galaxy.mybatis;

import com.lcl.galaxy.mybatis.anno.mapper.UserMapper;
import com.lcl.galaxy.mybatis.common.domain.UserDo;
import lombok.extern.slf4j.Slf4j;
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.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

@Slf4j
public class AnnoTest {

    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws Exception {
        SqlSessionFactoryBuilder sessionFactoryBuilder = new
                SqlSessionFactoryBuilder();
        InputStream inputStream =
                Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory = sessionFactoryBuilder.build(inputStream);
    }
    @Test
    public void testFindUserById() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        UserDo user = userMapper.findUserById(2);
        log.info("userDo====[{}]",user);
    }
}

(二)使用注解方式

  使用注解模式,只需要编写Mapper接口就OK了,但是在接口的每一个方法上都需要使用mybatis提供的注解,例如@Select、@Insert、@Update、@Delete

  新增Mapper接口

package com.lcl.galaxy.mybatis.anno.mapper;

import com.lcl.galaxy.mybatis.common.domain.UserDo;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserAnnoMapper {

    @Select("select * from user where id = #{id}")
    public UserDo findUserById(int id) throws Exception;

}

  新增测试类

package com.lcl.galaxy.mybatis;

import com.lcl.galaxy.mybatis.anno.mapper.UserAnnoMapper;
import com.lcl.galaxy.mybatis.common.domain.UserDo;
import lombok.extern.slf4j.Slf4j;
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.Before;
import org.junit.Test;

import java.io.InputStream;
import java.util.List;

@Slf4j
public class AnnoTest {

    private SqlSessionFactory sqlSessionFactory;
    @Before
    public void init() throws Exception {
        SqlSessionFactoryBuilder sessionFactoryBuilder = new
                SqlSessionFactoryBuilder();
        InputStream inputStream =
                Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory = sessionFactoryBuilder.build(inputStream);
        sqlSessionFactory.getConfiguration().addMapper(UserAnnoMapper.class);
    }
    @Test
    public void testFindUserById() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserAnnoMapper userMapper = sqlSession.getMapper(UserAnnoMapper.class);
            UserDo user = userMapper.findUserById(2);
            log.info("userDo====[{}]",user);
        }finally {
            sqlSession.close();
        }
    }
}

  这里需要注意一点,该测试类与xml基本上一致,只新增了一行代码:sqlSessionFactory.getConfiguration().addMapper(UserAnnoMapper.class);因为这一次并没有新增xml文件,因此加载的SqlMapperConfig中也没有加载到xml,因此需要使用addMapper方法加载,否则会出现加载不到xml错误:sqlSessionFactory.getConfiguration().addMapper(UserAnnoMapper.class);

(二)全局配置文件

   对于SqlMapperConfig.xml中配置的内容和顺序如下(顺序必须按照如下进行):

  properties(属性配置)、settings(全局配置参数)、typeAliases(类型别名)、typeHandlers(类型处理器)、objectFactory(对象工厂)、plugins(插件)、environments(环境属性对象集合)、environment(环境对象属性)、TransactionManager(事务管理)、dataSource(数据源)、mappers(映射器),对于以上对象,下面将一一描述。

  1、properties标签

   properties标签主要是用来加载配置文件的,可以有两种写法,第一种是直接使用外部配置文件,第二种是直接在该标签中使用property标签来设置属性

     propertites标签使用方式
     方式一:使用resource加载外部配置文件
     <properties resource="db.properties"></properties>
     方式二:使用property子标签配置
    <properties>
        <property name="jdbc.username" value="*******"/>
    </properties>

  同时这里需要说明一下,如果同时使用了这两种配置方式配置了一个参数,则外部的配置参数会覆盖property子标签的值,因为mybatis加载SqlMapperConfig时会先加载property子标签的内容,加载完毕后再加载外部配置文件的参数,因此外部文件的配置会将property子标签的值给覆盖掉。

 2、typeAliases标签

  typeAliase标签用来简化映射文件中ParamaterType和ResultType中对象的编写。

    <!--typeAliase标签使用方式-->
    <typeAliases>
        <typeAlias type="com.lcl.galaxy.mybatis.common.domain.UserDo" alias="user"/>
    </typeAliases>

  那么UserMapper.xml文件中对于UserDao的路径引用就可以直接替换成user

<?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.lcl.galaxy.mybatis.xml.mapper.UserMapper">
    <!-- 根据id获取用户信息 -->
    <select id="findUserById" parameterType="int" resultType="user">
        select * from user where id = #{id}
    </select>
</mapper>

  对于mybatis,有一些默认的标签内容,例如int、double等。

  3、mapper标签

  mapper标签可以有四种写法,分别使用resource、url、class和package四种

  (1)resource写法:这种写法就是前面demo和xml写法里面用到的

        <mapper resource="mapper/demo/UserMapper.xml"></mapper>

  (2)url写法:url内是xml文件的绝对路径,同时,这里不会区分nameSpace,只要Statement有重复,就会报错

        <mapper url="file:///d://workSpace/selfWorkSpace/lcl-galaxy/lcl-galaxy-mybatis/src/main/resources/mapper/xml/UserMapper.xml"/>

  (3)class写法:这种是对于注解Mapper写法的补充,在前面说注解写法时,必须新增了一行代码:sqlSessionFactory.getConfiguration().addMapper(UserAnnoMapper.class);因为没有地方可以加载Mapper文件;那么这种class方式,就解决了在SqlMapperConfig文件中加载注解Mapper的情况,因此,就可以在初始化SqlSessionFactory时不用addMapper

<mapper class="com.lcl.galaxy.mybatis.anno.mapper.UserAnnoMapper"/>

  初始化时就不需要addMapper

    @Before
    public void init() throws Exception {
        SqlSessionFactoryBuilder sessionFactoryBuilder = new
                SqlSessionFactoryBuilder();
        InputStream inputStream =
                Resources.getResourceAsStream("SqlMapConfig.xml");
        sqlSessionFactory = sessionFactoryBuilder.build(inputStream);
        //sqlSessionFactory.getConfiguration().addMapper(UserAnnoMapper.class);
    }

  (4)package写法:该写法就是对class写法的补充,不需要详细到具体的Mapper类,直接加载package下的所有Mapper类

<package name="com.lcl.galaxy.mybatis.anno.mapper"/>

(三)输入映射和输出映射

  1、输入类型:paramterType

  输入类型paramterType可以分为简单类型、对象类型、Map类型和List集合类型

  (1)传递简单类型:之前的代码示例就是传递简单类型,这里需要特殊说明的就是模糊查询

    <!-- 根据名称模糊查询用户列表 -->
    <select id="findUserByUsername" parameterType="java.lang.String" resultType="user">
        <!-- select * from user where username like '%${value}%  -->
        select * from user where username like CONCAT('%',#{name},'%')
    </select>

   这里不能直接写 select * from user where username like '%#{value}%,这是因为#{}会将String类型默认加上单引号,如果传参为lcl,那么实际的sql输出为 select * from user where username like '%'lcl'%,这样查询肯定是不行的,这里可以使用${}来查询,因为${}是直接将值替换的;但是有一个问题就是${}可能存在SQL注入的问题,因此我们不建议使用${},那么怎么处理呢,这里就可以使用sql中提供的CONCAT函数将多个字符串拼接起来就OK了。

  说到这里,我们不妨说一下${}和#{}的去别点

  #{} ${}
区别一 相当于JDBC中的占位符?(PreparedStatement) 相当于JDBC中的连接符+(Statement)
区别二

进行输入映射的时候,会对输入内容进行解析(如果输入为String类型,则会自动加上单引号)

进行输入映射的时候,不会进行内容解析,会将内容原样输出
区别三 如果进行简单类型的输入映射时,#{}里面名称可以任意 进行简单类型的输入映射时,${}中参数必须是value

  (2)传递对象类型

   mapper中新增insert语句,这里要说一下,这里可以查询新增后的id,keyProperty指更新后的id赋给对象的哪个属性,order为执行顺序,order="AFTER"表示只在insert语句执行后获取相关信息,LAST_INSERT_ID()函数表示最新插入数据的id。

    <insert id="insertUser" parameterType="user">
        <selectKey keyProperty="id" order="AFTER" resultType="long">
            select LAST_INSERT_ID()
        </selectKey>
        INSERT INTO user(username, birthday, sex, address) VALUES (#{username}, #{birthday}, #{sex}, #{address});
    </insert>

    Mapper类

    public int insertUser(UserDo userDo);

    测试类:这里说一句,insert语句返回的是insert条数,想要获取插入的id,则直接取在xml文件中keyProperty设置的属性,即userDo.getId()

    @Test
    public void testInsertUser() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            UserDo userDo = UserDo.builder().username("qmm2").birthday(new Date()).sex("").address("北京").build();
            int row = userMapper.insertUser(userDo);
            sqlSession.commit();
            log.info("row====[{}]==========insertId====[{}]", row, userDo.getId());
        }finally {
            sqlSession.close();
        }
    }

  2、输出类型:paramterType

    输出类型也分为简单类型、对象类型和Map类型,其中Map类型与对象类型基本一致。使用resultType时,要保证查询列与接收对象的属性保持一致。

    (1)简单类型:使用简单类型接收时,必须只有一个返回值,以查询总条数为例:

  xml文件

    <select id="getCount" parameterType="String" resultType="int">
          select count(1) from user where username = #{value };
    </select>

  Mapper对象

    public int getCount(String username);

  测试类

    @Test
    public void testGetCount() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            int count = userMapper.getCount("qmm2");
            log.info("count====[{}]", count);
        }finally {
            sqlSession.close();
        }
    }

  (2)对象类型,这个在前面的入门程序里面已经描述过了

  3、映射配置:resultMap

    如果查询列与接收对象的属性不一致,可以使用resultMap做一个映射,从而将查询结果映射到对象的属性上;其实ResultType的底层也是使用的ResultMap做的映射。

    接下来可以使用别名查询来演示一下resultMap的使用

  xml文件:使用resultMap做映射,其中id是后续其他查询要使用该映射map的标识,type为需要映射的对象;result子标签中property为对象属性,column为查询sql的查询列;在查询语句中,resultMap要指定使用resultMap的id。

    <resultMap id="userAliaseMap" type="user">
        <result property="username" column="uname"/>
        <result property="birthday" column="bir"/>
        <result property="sex" column="aaasex"/>
        <result property="address" column="addr"/>
    </resultMap>

    <select id="selectAsAliase" resultMap="userAliaseMap" parameterType="int">
        select id, username uname, birthday bir, sex  aaasex, address addr from user where id = #{id }
    </select>

  Mapper对象

    UserDo selectAsAliase(int id) throws Exception;

  测试类

    @Test
    public void testSelectAsAliase() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            UserDo user = userMapper.selectAsAliase(2);
            log.info("userDo====[{}]",user);
        }finally {
            sqlSession.close();
        }
    }

 三、高级应用

(一)关联查询

1、一对一查询

新增订单表及sql

CREATE TABLE `order_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(20) NOT NULL COMMENT '用户名',
  `orderId` bigint(20) DEFAULT NULL COMMENT '订单号',
  `payMoney` decimal(11,2) DEFAULT NULL COMMENT '订单实付金额',
  `orderCreateTime` datetime DEFAULT NULL COMMENT '订单下单时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

INSERT INTO `order_info`(`id`, `name`, `orderId`, `payMoney`, `orderCreateTime`) VALUES (2, 'lcl', 10001, 10.00, '2020-11-17 21:29:35');
INSERT INTO `order_info`(`id`, `name`, `orderId`, `payMoney`, `orderCreateTime`) VALUES (3, 'lcl', 10002, 12.00, '2020-11-18 21:29:53');
INSERT INTO `order_info`(`id`, `name`, `orderId`, `payMoney`, `orderCreateTime`) VALUES (4, 'lcl', 10003, 15.00, '2020-11-12 21:30:08');
INSERT INTO `order_info`(`id`, `name`, `orderId`, `payMoney`, `orderCreateTime`) VALUES (5, 'qmm', 10004, 11.00, '2020-11-24 21:30:23');

Do对象

package com.lcl.galaxy.mybatis.common.domain;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.Date;

@Data
@NoArgsConstructor
public class OrderInfoDo {
    private long id;
    private String username;
    private String orderId;
    private BigDecimal payMoney;
    private Date orderCreateTime;
}
(1)resultType类型实现

 resultType类型实现就比较简单,新增一个包含所有返回值的对象,然后使用关联查询查出所有需要的列就OK。

 需要返回的Dto对象

package com.lcl.galaxy.mybatis.common.dto;

import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;

@Data
public class UserOrderDto2{
    private long id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;


    private String name;
    private String orderId;
    private BigDecimal payMoney;
    private Date orderCreateTime;

}

Mapper类

    List<UserOrderDto> selectUserOrderDtoByUserName(String name) throws Exception;

Xml文件

    <select id="selectUserOrderDtoByUserName" resultType="com.lcl.galaxy.mybatis.common.dto.UserOrderDto2" parameterType="String">
        SELECT order_info.*,
          user.username, user.address,user.birthday,user.sex FROM user
          LEFT JOIN order_info ON order_info.name = user.username where order_info.name = #{username}
    </select>

测试类

    @Test
    public void testSelectUserOrderDtoByUserId() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<UserOrderDto> userOrderDto = userMapper.selectUserOrderDtoByUserName("qmm");
            log.info("userOrderDto====[{}]",userOrderDto);
        }finally {
            sqlSession.close();
        }
    }
(2)resultMap类型实现

 resultType类型实现就比较简单,新增一个包含所有返回值的对象,然后使用关联查询查出所有需要的列就OK。

 Dto

package com.lcl.galaxy.mybatis.common.dto;

import com.lcl.galaxy.mybatis.common.domain.OrderInfoDo;
import lombok.Data;
import java.util.Date;

@Data
public class UserOrderDto extends OrderInfoDo{
    private long id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private OrderInfoDo orderInfoDo;

}

Mapper类

    List<UserOrderDto> selectUserOrderDtoByUserName2(String name) throws Exception;

Xml文件

使用ResultMap方式时,需要用到association标签,该标签表示一对一关联映射,property属性表示关联查询的结果存于哪个对象中,javaType表示查询结果的映射类型

    <resultMap id="userOrderDtoMap" type="com.lcl.galaxy.mybatis.common.dto.UserOrderDto">
        <result column="us" property="username"/>
        <result column="address" property="address"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <association property="orderInfoDo" javaType="com.lcl.galaxy.mybatis.common.domain.OrderInfoDo">
            <result column="name" property="name"/>
            <result column="orderId" property="orderId"/>
            <result column="payMoney" property="payMoney"/>
            <result column="orderCreateTime" property="orderCreateTime"/>
        </association>
    </resultMap>

    <select id="selectUserOrderDtoByUserName2" resultMap="userOrderDtoMap" parameterType="String">
        SELECT order_info.*, user.username as us, user.address as address,user.birthday as birthday,user.sex as sex FROM user
          LEFT JOIN order_info ON order_info.name = user.username where order_info.name = #{username}
    </select>

测试类

    @Test
    public void testSelectUserOrderDtoByUserId2() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<UserOrderDto> userOrderDto = userMapper.selectUserOrderDtoByUserName2("qmm");
            log.info("userOrderDto====[{}]",userOrderDto);
        }finally {
            sqlSession.close();
        }
    }

2、一对多查询

  一对多查询只能用resultMap

 Dto

package com.lcl.galaxy.mybatis.common.dto;

import com.lcl.galaxy.mybatis.common.domain.OrderInfoDo;
import lombok.Data;

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

@Data
public class UserOrdersDto {
    private long id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
    private List<OrderInfoDo> orderInfoDoList;

}

Mapper类

    List<UserOrdersDto> selectUserOrdersDtoByUserName(String name) throws Exception;

Xml文件

与一对一唯一的差别就是将映射标签association改成collection,即一对一改为集合;在collection标签中,property属性是对象中的属性值,ofType为集合属性中对象的值

    <resultMap id="userOrdersDtoMap" type="com.lcl.galaxy.mybatis.common.dto.UserOrdersDto">
        <result column="us" property="username"/>
        <result column="address" property="address"/>
        <result column="birthday" property="birthday"/>
        <result column="sex" property="sex"/>
        <collection property="orderInfoDoList"   ofType="com.lcl.galaxy.mybatis.common.domain.OrderInfoDo">
            <result column="name" property="name"/>
            <result column="orderId" property="orderId"/>
            <result column="payMoney" property="payMoney"/>
            <result column="orderCreateTime" property="orderCreateTime"/>
        </collection>
    </resultMap>

    <select id="selectUserOrdersDtoByUserName" resultMap="userOrdersDtoMap" parameterType="String">
        SELECT order_info.*, user.username as us, user.address as address,user.birthday as birthday,user.sex as sex FROM user
          LEFT JOIN order_info ON order_info.name = user.username where order_info.name = #{username}
    </select>

测试类

    @Test
    public void testSelectUserOrderDtosByUserId() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<UserOrdersDto> userOrdersDtoList = userMapper.selectUserOrdersDtoByUserName("lcl");
            log.info("userOrdersDtoList====[{}]",userOrdersDtoList);
        }finally {
            sqlSession.close();
        }
    }

(二)延迟加载

   延迟加载即懒加载,是指在关联查询时,延迟关联对象的查询;(1)Mybatis的延迟加载只对关联对象有效,对于主加载对象都是直接查询;(2)Mybatis的延迟加载需要通过ResultMap的collection和association标签才可以验证(3)延迟加载可以减少数据库的压力;但是也会有N+1的问题。(4)延迟加载可以分为直接加载、侵入式延迟和深度延迟。

  直接加载:执行完主查询select后马上执行关联对象的select查询

  侵入式延迟:查询主对象时,不会马上查询关联对象,但是如果使用主对象的某一个属性的时候,就会查询关联对象

  深度延迟:查询主对象时,不会马上查询关联对象,使用主对象的某一个属性的时候,也不会查询关联对象,但是当使用关联对象的属性时,才会查询关联对象

  设置延迟加载使用setting标签,然后lazyLoadingEnabled属性表示是否开启懒加载,true:开启,false:不开启,默认false;aggressiveLazyLoading属性表示懒加载模式,ture为侵入式开关 false为深度延迟加载,默认false

  (1)直接加载配置:

<settings>
        <setting name="lazyLoadingEnabled" value="false"/>
</settings>

  (2)侵入式懒加载配置

<settings>
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
</settings>

  (3)深度懒加载

<settings>
       <setting name="lazyLoadingEnabled" value="true"/>
       <setting name="aggressiveLazyLoading" value="false"/>
</settings>

  之前说到,懒加载可能会引发N+1问题,即会查询N+1次,1次是指的查询主信息,N指的是关联数据有多少条就会查询多少次,因此如果关联数据多的情况,使用懒加载反而会增加数据库压力

(三)缓存

  Mybatis缓存中如果存在数据,则不再查询数据库,用于减小数据库压力。Mybatis缓存分为一级缓存和二级缓存。

  1、一级缓存

    一级缓存是Session级别的缓存,在操作数据库时创建了一个SqlSession对象,在该对象中有一个HashMap对象用于存储缓存,因此不同的Sqlsession之间的缓存数据区域是互不影响的。

    例如使用id查询用户信息时,会先从HashMap查看是否存在缓存,如果存在,则直接返回,不存在则查询数据库。如果出现增删改操作,则会清除缓存。下面就使用一个例子说明,第一次查询会发送sql,第二次使用相同的id查询则不会发送sql,但是如果使用一次插入操作后,下一次查询则会重新发送sql。

    @Test
    public void testMybatisCache() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            UserDo user = userMapper.findUserById(2);
            log.info("user====[{}]",user);
            UserDo user2 = userMapper.findUserById(2);
            log.info("user2====[{}]",user2);
            UserDo user3 = new UserDo();
            user3.setUsername("test");
            userMapper.insertUser(user2);
            UserDo user4 = userMapper.findUserById(2);
            log.info("user4====[{}]",user4);
        }finally {
            sqlSession.close();
        }
    }

  2、二级缓存

    二级缓存可以跨Session。

    例如使用不同的SqlSession查询相同的id用户,如果开启了二级缓存,则会使用二级缓存;同样的,如果在同一个NameSpace中操作了增删改操作,则会清除缓存

  二级缓存开启总开关

   <settings>
        <!-- 开启二级缓存总开关 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

  然后还有更细粒度的控制,如果哪个NameSpace需要使用二级缓存,需要在其Xml文件中开启(在Xml中加入cache标签)

    <!-- 开启二级缓存 -->
    <cache/>

  然后就是验证

    @Test
    public void testMybatisCache2() throws Exception {
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
        SqlSession sqlSession3 = sqlSessionFactory.openSession();
        UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
        SqlSession sqlSession4 = sqlSessionFactory.openSession();
        UserMapper userMapper4 = sqlSession4.getMapper(UserMapper.class);
        UserDo user1 = userMapper1.findUserById(2);
        log.info("==========================================================");
        log.info("user1====[{}]",user1);
        sqlSession1.close();


        UserDo user2 = userMapper2.findUserById(2);
        log.info("==========================================================");
        log.info("user2====[{}]",user2);
        sqlSession2.close();

        UserDo user3 = new UserDo();
        user3.setUsername("test2");
        userMapper3.insertUser(user3);
        sqlSession3.commit();
        sqlSession3.close();
        log.info("==========================================================");
        log.info("user3====[{}]",user3);

        UserDo user4 = userMapper4.findUserById(2);
        log.info("==========================================================");
        log.info("user4====[{}]",user4);
        sqlSession4.close();
    }

  同样的,还可以有更细粒度的控制:如果在XML中配置了cache标签,那么如果在该NameSpace中有方法不想使用二级缓存,那么可以在select标签中使用useCache属性来设置

    <!-- 根据id获取用户信息 -->
    <select id="findUserByIdNoCache" parameterType="int" resultType="user" useCache="false">
        select * from user where id = #{id}
    </select>

  除了上述的useCache属性外,Mybatis还提供了flushCache属性,用来控制该操作是否会刷新二级缓存,select标签默认为false,增删改标签默认为true

    <!-- 根据id获取用户信息 -->
    <select id="findUserByIdNoCache" parameterType="int" resultType="user" useCache="false" flushCache="false">
        select * from user where id = #{id}
    </select>

  二级缓存的使用场景:查询效率要求比较高,但是数据实时性不是很高的情况,可以使用二级缓存

  二级缓存使用注意事项:在使用二级缓存的时候,要设置一下刷新缓存的间隔,(cache标签中的flushInterval属性)

  二级缓存的使用局限:缓存的清理是整个NameSpace级别的,因此不能控制到一条数据的维度,因此还需要借助其他的缓存来处理,例如Redis。其实这里也可使将查询单独写一个Xml文件,这样就可以避免增删改操作清除缓存。

(四)动态SQL

  动态Sql简单的说就是用来做sql拼接的,常见的标签有sql、where、if、foreach等

  if标签是用来判断,foreach标签是用来循环

package com.lcl.galaxy.mybatis.common.vo;

import lombok.*;

import java.util.List;

@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class QueryVo {
    private String sex;
    private List<String> addressList;
}
    <select id="findUserList" parameterType="com.lcl.galaxy.mybatis.common.vo.QueryVo" resultType="user">
        select * from user where 1=1
        <if test="sex != null and sex != ''">
            and sex = #{sex}
        </if>
        <foreach collection="addressList" item="addr" open=" and address in (" separator="," close=")" index="">
            #{addr}
        </foreach>
    </select>

  where标签主要是可以省略1=1的条件,而sql标签则是一个标签块,可以直接印用

   <sql id="base_column_list">
        id,username,birthday,sex,address
    </sql>

    <select id="findUserList2" parameterType="com.lcl.galaxy.mybatis.common.vo.QueryVo" resultType="user">
        select
        <include refid="base_column_list"/>
        from user
        <where>
            <if test="sex != null and sex != ''">
                and sex = #{sex}
            </if>
            <foreach collection="addressList" item="addr" open=" and address in (" separator="," close=")" index="">
                #{addr}
            </foreach>
        </where>
    </select>

  如果要引用其他NameSpace中的sql块,则需要加上NameSpace

<include refid="namespace.base_column_list"/>

(五)Mybatis逆向工程

  https://www.cnblogs.com/liconglong/p/11692412.html

(六)PageHelper分页插件

  https://www.cnblogs.com/liconglong/p/11693782.html


原文地址:https://www.cnblogs.com/liconglong/p/14027557.html