zoukankan      html  css  js  c++  java
  • 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

    
    
  • 相关阅读:
    微服务框架核心问题
    django restframework 全局异常处理,编写自定义custom_exception_handler
    django 异常处理中间件
    drf_yasg2 定制swagger
    redis-django
    django 权限
    django restframework 多对多的批量修改,基于逻辑删
    python3 动态绑定
    Django restframework 逻辑删除
    django restframework 多对多模型
  • 原文地址:https://www.cnblogs.com/liconglong/p/14027557.html
Copyright © 2011-2022 走看看