Mybatis入门笔记

第十二章 什么是Mybatis

mybatis 是一个数据库持久层框架(主要是把内存中的数据写入到数据库中(文件)),通过mybatis能实现对数据库的访问(执行sql语句),mybatis建立在jdbc的基础之上,jdbc对代码耦合性太高。便有了mybatis的存在,mybatis把SQL语句写在xml文件中,达到SQL语句与业务代码的解耦。

mybatis与HIbernate的区别

MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO 和 SQL 的映射关系,操作的数据库,执行操作的语言是SQL语言。
Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可,操作的是POJO对象,执行操作的是HQL语言(Hibernate Query Language)

12.1 快速入门

1,创建一个普通的maven项目。在pom.xml中添加依赖
pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Mybatis3</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
<!--        mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>

<!--     数据库驱动   -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>

<!--        单元测试5-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>    

数据库文件


DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(20) NOT NULL,
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `pwd` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '李四', '12213');
INSERT INTO `user` VALUES (2, '张三', 'abcdef');
INSERT INTO `user` VALUES (3, 'test', '12213');
INSERT INTO `user` VALUES (6, 'llowwww', '123');
INSERT INTO `user` VALUES (7, '王先生', '123');
INSERT INTO `user` VALUES (8, '张先生', '123');

SET FOREIGN_KEY_CHECKS = 1;

User类

public class User{

    // id
    private  Long id;

    //用户名
    private  String name;

    // 密码
    private String  pwd;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }

}

UserMapper

public interface UserMapper {


    /**
     * 查询所有数据
     * @return
     */
    List<User>  selectList();

}

MybatisUtils 工具类

public class MybatisUtils {

    public static SqlSession getSqlSession(){
        // 读取配置文件
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 创造SqlSessionFactory
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        // 返回session对象
        return build.openSession();

    }
}

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
       <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

UserMapper.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.example.mapper.UserMapper">


    <select id="selectList" resultType="com.example.pojo.User">
        select * from user
    </select>

</mapper>

MyBatisTest


public class MyBatisTest {
    @Test
    public void selectList(){
        // 获取sqlSession对象
        SqlSession mapper = MybatisUtils.getSqlSession();
        // 获取UserMapper 对象
        UserMapper userMapper = mapper.getMapper(UserMapper.class);
        // 调用方法
        List<User> users = userMapper.selectList();

        for (User user: users) {
            System.out.println(user);
        }
        // 关闭session
        mapper.close();

    }
}

到此全部全部结束,文件结构如下

在这里插入图片描述
运行测试方法, 结果如下

在这里插入图片描述

12.2 核心对象

MyBatis 有三个基本要素:
核心接口和类
MyBatis核心配置文件(mybatis-config.xml)
SQL映射文件(XxxMapper.xml)

流程

SqlSessionFactoryBuilder 对象 调用buider方法创建SqlSessionFactory对象,参数是mybatis-config.xml 文件, SQLSessionFactory对象调用openSession方法,获得SqlSession对象,SqlSession对象执行Mapper映射文件。

在这里插入图片描述

12.3 配置文件(mybatis-config.xml)

mybatis-config.xml 文件中的元素节点是有一定顺序的,节点位置必须按以上位置排序,否则会编译错误, 且不能同时配置两个相同的标签 ,数据源配置除外。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 配置 -->
    <properties /><!-- 属性 -->
    <settings /><!-- 设置 -->
    <typeAliases /><!-- 类型命名 -->
    <typeHandlers /><!-- 类型处理器 -->
    <objectFactory /><!-- 对象工厂 -->
    <plugins /><!-- 插件 -->
    <environments><!-- 配置环境 -->
        <environment><!-- 环境变量 -->
            <transactionManager /><!-- 事务管理器 -->
            <dataSource /><!-- 数据源 -->
        </environment>
    </environments>
    <databaseIdProvider /><!-- 数据库厂商标识 -->
    <mappers /><!-- 映射器 -->
</configuration>

12.3.1 properties标签

1导入外部文件属性,如数据库的连接信息

datasource.driver= com.mysql.jdbc.Driver
datasource.url= jdbc:mysql://localhost:3306/localhost?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8
datasource.username= root
datasource.password= root
<?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="dataSource.properties"/>-->


<!--    第二种配置 子属性-->
    <properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
<!--        给一个类设置别名,不设置值。默认是类名小写-->
        <typeAlias alias="student" type="com.example.pojo.User"/>
<!--        以包下的类设置别名 别名类名首字母小写-->
        <package name="com.example.pojo"/>
    </typeAliases>

<!--    typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换-->
    <typeHandlers>

    </typeHandlers>

<!--    数据库环境 default 属性指定一个默认数据源-->
    <environments default="development">
<!--        数据源配置-->
        <environment id="development">
<!--            配置事务 类型
                             JDBC 和 MANAGED
                如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。
                如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期
-->
            <transactionManager type="JDBC"/>
<!--            配置数据库的连接属性
1)UNPOOLED
 没有数据库连接池,效率低下。需要每次打开和关闭每个数据库操作的连接。
2)POOLED
MyBatis 将维护一个数据库连接池。
3)JNDI
MyBatis 将从 JNDI 数据源中获取连接。
-->
            <dataSource type="POOLED">

<!--                <property name="driver" value="${datasource.driver}"/>-->
<!--                <property name="url" value="${datasource.url}"/>-->
<!--                <property name="username" value="${datasource.username}"/>-->
<!--                <property name="password" value="${datasource.password}"/>-->

                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
<!--    指定Mapper文件所在的位置-->
    <mappers>
        
<!--        类路径资源引入-->
       <mapper resource="mapper/UserMapper.xml"/>
<!--         Mapper文件必须与接口的类名一致,且在同一个包下-->
        <mapper class="com.example.mapper.UserMapper"/>
<!--     使用完全限定的资源定位符   -->
        <mapper url="E:\testProject\Mybatis3\src\main\resources\mapper\UserMapper.xml"/>

        <!--         Mapper文件必须与接口的类名一致,且在同一个包下-->
        <package name="com.example.mapper"/>
    </mappers>
</configuration>

12.3.2 settings标签

settings 标签用于配置 MyBatis 的运行时行为,红色为常用配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.3.3 配置SQL语句打印

 <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
        </dependency>

log4j2.xml


<?xml version="1.0" encoding="UTF-8"?>
<!--
    status="warn" 日志框架本身的输出日志级别
    monitorInterval="5" 自动加载配置文件的间隔时间,不低于 5-->
<Configuration status="debug" monitorInterval="5">
    <!--日志处理-->
    <Appenders>
        <!--控制台输出 appender-->
        <Console name="Console" target="SYSTEM_ERR">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] [%-5level] %c{36}:%L --- %m%n" />
        </Console>
    </Appenders>
    <!--logger 定义-->
    <Loggers>
        <Root level="debug">
            <!--指定日志使用的处理器-->
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>

mybatis-config.xml

        <setting name="logImpl" value="log4j2"/>

12.4 映射器(Mapper)

负责执行Sql语句的称为映射器,映射器由 Java 接口和 XML 文件(或注解)组成,

实现方式

  • 通过 XML 文件方式实现,比如我们在 mybatis-config.xml 文件中描述的 XML 文件,用来生成 mapper。
  • 通过注解的方式实现,使用 Configuration 对象注册 Mapper 接口。

12.4.1 配置映射器

XML实现映射器

public interface UserMapper {
    /**
     * 查询所有数据
     * @return
     */
    List<User>  selectList();

}

UserMapper.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.example.mapper.UserMapper">


    <select id="selectList" resultType="com.example.pojo.User">
        select * from user
    </select>

</mapper>

注册到mybatis配置文件中

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

注解方式

public interface AnnotationUserMapper {
  
    @Select("select * from user")
    List<User>  selectAll();

}
  @Test
    public void selectAll(){
        try {
            BufferedInputStream inputStream = new BufferedInputStream(
                    new FileInputStream("E:\\testProject\\Mybatis3\\src\\main\\resources\\mybatis-config.xml"));
            SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);
            Configuration configuration = build.getConfiguration();
            // 添加注解的Mapper映射器到配置中
            configuration.addMapper(AnnotationUserMapper.class);
            // 打开session会话
            SqlSession session = build.openSession();
            UserMapper mapper = session.getMapper(AnnotationUserMapper.class);
            List<User> users = mapper.selectAll();
            for (User user: users
                 ) {
                System.out.println(user);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        
    }

12.4.2 映射器主要元素

在这里插入图片描述
不同的 mapper 文件中子元素的 id 可以相同,MyBatis 通过 namescape 和子元素的 id 联合区分。接口中的方法与映射文件中的 SQL 语句 id 应一 一对应。

12.4.3 执行SQL

MyBatis 有两种执行 SQL 语句的方式,如下:
通过 SqlSession 发送 SQL
通过 SqlSession 获取 Mapper 接口,通过 Mapper 接口发送 SQL

SqlSession 发送 SQL

检查 UserMapper是否配置到mybatis-config.xml

 @Test
    public void BySqlSession(){
        // 获取sqlSession对象
        SqlSession mapper = MybatisUtils.getSqlSession();
        // 使用 SQLSession对象 内置的查询方法
        List<User> users = mapper.selectList("com.example.mapper.UserMapper.selectList");
        for (User user: users) {
            System.out.println(user);
        }
        // 关闭session
        mapper.close();

    }

总结
SqlSession 对象内置了很多的SQL语句方法,方法的第一个参数代表接口类的路径加方法名,其他参数代表查询条件。

Mapper接口发送 SQL

  // 获取sqlSession对象
        SqlSession mapper = MybatisUtils.getSqlSession();
        // 获取UserMapper 对象
        UserMapper userMapper = mapper.getMapper(UserMapper.class);
        // 调用方法
        List<User> users = userMapper.selectList();

总结

通过SqlSession 获取自己定义的对象,使用的是自己定义的方法。

区别

上面分别讲解了 MyBatis 两种发送 SQL 的方式,一种用 SqlSession 直接发送,另外一种通过 SqlSession 获取 Mapper 接口再发送。笔者建议采用 Mapper 接口发送 SQL 的方式,理由如下:

  • 使用 Mapper 接口编程可以消除 SqlSession 带来的功能性代码,提高可读性,而 SqlSession 发送 SQL,需要一个 SQL id 去匹配 SQL,比较晦涩难懂。

  • 使用 Mapper 接口,类似 websiteMapper.getWebsite(1) 则是完全面向对象的语言,更能体现业务的逻辑。
    使用 websiteMapper.getWebsite(1) 方式,IDE 会提示错误和校验,而使用 sqlSession.selectOne(“getWebsite”,1L) 语法,只有在运行中才能知道是否会产生错误。

12.5 SQL

12.5.1 select 标签

执行 SQL 语句时可以定义参数,参数可以是一个简单的参数类型,例如 int、float、String;也可以是一个复杂的参数类型,例如 JavaBean、Map 等。执行 SQL 后,会将结果集自动映射到 JavaBean 中。

MySQL 数据库和 JavaBean 采用同一套命名规则,即 Java 命名驼峰规则来达到结果自动映射,(数据库表字段名和属性名不一致时需要手动映射)。
参数的传递使用#{参数名},相当于告诉 MyBatis 生成 PreparedStatement 参数。

​select标签常用属性
在这里插入图片描述

参数传递

  1. 使用Map传递参数
  2. 使用注解传递参数
  3. 使用JavaBean传递参数

使用Map传递参数

 /**
     * 使用map 传递参数
     * @param map
     * @return
     */
    List<User> selectByMap(Map<String,String> map);


    /**
     * 使用Param注解, 如果参数名称与字段名称一样可省略
     * @param name
     * @param pwd
     * @return
     */
    List<User> selectByParam(@Param("name") String name,@Param("pwd") String pwd);


    /**
     * 使用 Javabean 传递参数
     * @param user
     * @return
     */
    List<User> selectByJavabean(User user);

UserMapper.xml


<!--    通过Map传递参数-->
    <select id="selectByMap" resultType="user">
        select  * from user where name = #{name}
        and  pwd =  #{pwd}
    </select>

    <!--    通过@Param传递参数-->
    <select id="selectByParam" resultType="user">
        select  * from user where name =#{name}
        and  pwd = #{pwd}
    </select>

    <!--    通过Javabean传递参数-->
    <select id="selectByJavabean" resultType="user">
        select  * from user where name = #{name}
        and  pwd = #{pwd}
    </select>

测试

 @Test
    public void param(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //通过map方法
        Map<String,String> map = new HashMap<String, String>();
        map.put("name", "王先生");
        map.put("pwd","123");
        List<User> users = mapper.selectByMap(map);

        for (User user: users) {
            System.out.println(user);
        }

        System.out.println("---------------");
        //通过注解
        List<User> users1 = mapper.selectByParam("王先生", "123");
        for (User user: users1) {
            System.out.println(user);
        }

        System.out.println("--------------");
        //通过Javabean对象
        User javaBean = new User();
        javaBean.setName("王先生");
        javaBean.setPwd("123");
        List<User> users2 = mapper.selectByJavabean(javaBean);
        for (User user: users2) {
            System.out.println(user);
        }
        // 关闭session
        sqlSession.close();
    }

运行结果

在这里插入图片描述
总结

  • 使用 Map 传递参数会导致业务可读性的丧失,继而导致后续扩展和维护的困难,所以在实际应用中我们应该果断废弃该方式。
  • 使用 @Param 注解传递参数会受到参数个数的影响。当 n≤5 时,它是最佳的传参方式,因为它更加直观;当 n>5 时,多个参数将给调用带来困难。
  • 当参数个数大于 5 个时,建议使用 JavaBean 方式。

12.5.2 insert、update、delete标签

MyBatis insert 标签用来定义插入语句,执行插入操作。当 MyBatis 执行完一条插入语句后,就会返回其影响数据库的行数。

insert 标签常用属性

在这里插入图片描述

参数传递

单个对象,直接传递参数
使用 Map 传递参数
使用注解传递参数
使用 JavaBean 传递参数

Collection或数组:
如果是Collection(List、Set)类型或者是数组,也会特殊处理,也是把传入的Collection(List、Set)或者数组封装在map中。
key:对于单一参数Collection使用(collection、list)、数组使用(array)来获取,例如:Collection使用#{collection[0]},…#{collection[N]},List集合也可以使用#{list[0]},…#{list[N]},数组可以使用#{array[0]},…,#{array[N]}这种形式来获取键所对应的值。
value:集合或数组索引所对应的值。

主键(自动递增)回填

MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。

一些特殊情况下,我们可能需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加 keyProperty 和 useGeneratedKeys 属性,来实现该功能。

示例

  /**
     * 增加一个用户
     * @param user
     * @return
     */
     int addUser(User user);

使用useGeneratedKeys生成主键,前提确保主键是自增。

 <!--成功后将主键值返回填给id(po的属性)-->
    <insert id="addUser"  useGeneratedKeys="true" keyProperty="id">
        insert to  user(name,pwd) values (#{name},#{pwd})
    </insert>

测试

   @Test
    public void addUser(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setName("王二明");
        user.setPwd("1234");
        int i = userMapper.addUser(user);
        System.out.println("添加成功了"+i+"记录 id:"+ user.getId());

        // 提交事务
        sqlSession.commit();
        // 关闭会话
        sqlSession.close();
    }

测试结果
在这里插入图片描述

自定义主键

在这里插入图片描述

<!--成功后将主键值返回填给id(po的属性)-->
    <insert id="addUser">

        <selectKey keyProperty="id" resultType="Long" order="BEFORE">
            select if(max(id) is null,1,max(id)+1) as newId from user
        </selectKey>
        insert into user(name,pwd) values(#{name},#{pwd})
    </insert>

update、delete 语句


    /**
     * 增加一个用户
     * @param user
     * @return
     */
    int deleteUser(User user);
    /**
     * 增加一个用户
     * @param user
     * @return
     */
    int updateUser(User user);


<!--    更新-->
    <update id="updateUser" >
        update user set name =#{name}, pwd =#{pwd} where  id=#{id}
    </update>

<!--    删除-->
    <delete id="deleteUser">
        delete from  user  where  id= #{id}
    </delete>

测试代码


    @Test
     public void updateAndDelete(){
         SqlSession sqlSession = MybatisUtils.getSqlSession();
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
         User user = new User();
         user.setId(10L);
         user.setName("王先生");
         user.setPwd("321");

         int i = mapper.updateUser(user);
         System.out.println("修改成功了"+i+"记录");
         int i1 = mapper.deleteUser(user);
         System.out.println("修改成功了"+i1+"记录");

         sqlSession.commit();
         sqlSession.close();

测试结果

在这里插入图片描述

12.5.3 级联封装 (resultMap)

resultMap 是 MyBatis 中最复杂的元素,主要用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象。

现有的 MyBatis 版本只支持 resultMap 查询,不支持更新或者保存,更不必说级联的更新、删除和修改。

resultMap元素的构成

<resultMap id="" type="">
    <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
        <idArg/><!-- ID参数,结果为ID -->
        <arg/><!-- 注入到构造方法的一个普通结果 --> 
    </constructor>
    <id/><!-- 用于表示哪个列是主键 -->
    <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
    <association property=""/><!-- 用于一对一关联 -->
    <collection property=""/><!-- 用于一对多、多对多关联 -->
    <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
        <case value=""/><!-- 基于某些值的结果映射 -->
    </discriminator>
</resultMap>

< resultMap> 元素的 type 属性表示需要的 POJO,id 属性是 resultMap 的唯一标识。

  • 子元素 < constructor> 用于配置构造方法。当一个 POJO 没有无参数构造方法时使用。
  • 子元素 < id> 用于表示哪个列是主键。允许多个主键,多个主键称为联合主键。
  • 子元素 < result> 用于表示 POJO 和 SQL 列名的映射关系。

Map存储结果集

    List<Map<String,User>> selectListByMap();
<select id="selectListByMap" resultType="map">
    select * from user
</select>

    @Test
    public void selectListByMap(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<Map<String, User>> users = mapper.selectListByMap();
        for (Map<String,User> maps: users) {
            Set<Map.Entry<String, User>> entries = maps.entrySet();
            for (Map.Entry<String, User> key: entries) {
                String key1 = key.getKey();
                Object value =  key.getValue();
                System.out.print(key1+":"+value+"");
            }
            System.out.println();
        }
                sqlSession.close();
    }

运行结果

在这里插入图片描述

POJO存储结果集

    List<User>  selectList();
    <select id="selectList" resultType="user">
        select * from user
    </select>

<!--    如果pojo对象的属性与数据库的字段名称不一样,则使用resultMap 进行映射-->
    <resultMap id="selectList" type="user">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="pwd"/>

    </resultMap>
    <select id="selectList" resultMap="selectList">
        select * from user
    </select>

resultMap 元素的属性 id 代表这个 resultMap 的标识,type 标识需要映射的 POJO。我们可以使用 MyBatis 定义好的类的别名或自定义类的全限定名。

这里使用 property 元素指定 user的属性名称 name,column 表示数据库中 user表的 SQL 列名 name,将 POJO 和 SQL 的查询结果一 一对应。

测试代码


    @Test
    public void selectList(){
        // 获取sqlSession对象
        SqlSession mapper = MybatisUtils.getSqlSession();
        // 获取UserMapper 对象
        UserMapper userMapper = mapper.getMapper(UserMapper.class);
        // 调用方法
        List<User> users = userMapper.selectList();

        for (User user: users) {
            System.out.println(user);
        }
        // 关闭session
        mapper.close();

    }

MyBatis 的每一个查询映射的返回类型都是 resultMap,只是当我们提供的返回类型是 resultType 时,MyBatis 会自动把对应的值赋给 resultType 所指定对象的属性,而当我们提供的返回类型是 resultMap 时,MyBatis 会将数据库中的列数据复制到对象的相应属性上,可用于复制查询。

需要注意的是,resultMap 和 resultType 不能同时使用。

联表查询

连表关系:

  • 一对一关系 只能使用 association
  • 一对多关系 使用collection
  • 多对多关系 使用 collection

association与collection 查询方式

  • 分步查询 相当于SQL中的子查询
  • 单步查询 相当于SQL中的联表查询

数据库准备



SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for stu_tea
-- ----------------------------
DROP TABLE IF EXISTS `stu_tea`;
CREATE TABLE `stu_tea`  (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `stu_id` bigint(20) NOT NULL COMMENT '学生ID',
  `tea_id` bigint(20) NULL DEFAULT NULL COMMENT '教室ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of stu_tea
-- ----------------------------
INSERT INTO `stu_tea` VALUES (1, 1, 1);
INSERT INTO `stu_tea` VALUES (2, 1, 2);
INSERT INTO `stu_tea` VALUES (3, 1, 3);
INSERT INTO `stu_tea` VALUES (4, 1, 2);
INSERT INTO `stu_tea` VALUES (5, 2, 2);
INSERT INTO `stu_tea` VALUES (6, 3, 2);
INSERT INTO `stu_tea` VALUES (7, 3, 3);
INSERT INTO `stu_tea` VALUES (8, 4, 4);

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(10) NOT NULL,
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `tid` int(10) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `fktid`(`tid`) USING BTREE,
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '小明', 1);
INSERT INTO `student` VALUES (2, '小红', 2);
INSERT INTO `student` VALUES (3, '小张', 2);
INSERT INTO `student` VALUES (4, '小李', 3);
INSERT INTO `student` VALUES (5, '小王', 1);
INSERT INTO `student` VALUES (6, '小赵', 1);
INSERT INTO `student` VALUES (7, '小梁', 2);

-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher`  (
  `id` int(10) NOT NULL,
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, '秦老师');
INSERT INTO `teacher` VALUES (2, '王老师');
INSERT INTO `teacher` VALUES (3, '张老师');
INSERT INTO `teacher` VALUES (4, '梁老师');

SET FOREIGN_KEY_CHECKS = 1;

12.5.3.1 一对一关联查询 (association)

查询需求: 查询出一个学生与一个老师的信息

public class Student {


    private Integer id;

    private String  stuName;

    private Integer teaId;

    private Teacher  teacher;
    public Integer getId() {
        return id;
    }

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

    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public Integer getTeaId() {
        return teaId;
    }

    public void setTeaId(Integer teaId) {
        this.teaId = teaId;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", stuName='" + stuName + '\'' +
                ", teaId=" + teaId +
                ", teacher=" + teacher +
                '}';
    }
}

package com.example.pojo;

 class Teacher {

    private Integer teaId;

    private String  teaName;

    public Integer getTeaId() {
        return teaId;
    }

    public void setTeaId(Integer teaId) {
        this.teaId = teaId;
    }

    public String getTeaName() {
        return teaName;
    }

    public void setTeaName(String teaName) {
        this.teaName = teaName;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "teaId=" + teaId +
                ", teaName='" + teaName + '\'' +
                '}';
    }
}

public interface StudentMapper {


    /**
     * 查询一个学生与一个老师的信息
     * @param id
     * @return
     */
    Student  getStuAndTea(Integer id);
}

使用原生< resultMap> 查询一对一

StudentMapper.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.example.mapper.StudentMapper">


    <resultMap id="getStuAndTea" type="student">
        <id column="id" property="id"/>
        <result column="name" property="stuName"/>
        <result column="tid" property="teaId"/>
        <result column="tea_id" property="teacher.teaId"/>
        <result column="tea_name" property="teacher.teaName"/>
    </resultMap>

    <select id="getStuAndTea" resultMap="getStuAndTea">
        select s.* ,t.id  tea_id ,t.`name` tea_name	from student s ,teacher t
         where t.id = s.tid  and s.id = #{id}
 </select>
</mapper>

测试代码


@Test
    public void resultTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student stuAndTea = mapper.getStuAndTea(1);
        System.out.println(stuAndTea);
        sqlSession.close();
    }

运行结果

在这里插入图片描述

association 单步查询

property:指定映射到实体类的对象属性。
column:指定表中对应的字段(即查询返回的列名)。
javaType:指定映射到实体对象属性的类型。
ofType:: 指定映射到集合中的属性中的泛型。
select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

public interface StudentMapper {
    Student  getStuAndTeaByAssociation(Integer id);
    
    }
<resultMap id="byAssociation" type="student">
        <id column="id" property="id"/>
        <result column="name" property="stuName"/>
        <result column="tid" property="teaId"/>
        <association property="teacher">
            <result column="tea_id" property="teaId"/>
            <result column="tea_name" property="teaName"/>
        </association>


    </resultMap>
    <select id="getStuAndTeaByAssociation" resultMap="byAssociation">
        select s.* ,t.id  tea_id ,t.`name` tea_name	from student s ,teacher t where t.id = s.tid  and s.id = #{id}

    </select>

测试方法

  @Test
    public void resultByAssociationTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student stuAndTea = mapper.getStuAndTeaByAssociation(1);
        System.out.println(stuAndTea);
        sqlSession.close();
    }

在这里插入图片描述

association 分步查询

public interface TeacherMapper {


    /**
     * 查询教师
     * @param id
     * @return
     */
    public Teacher getTeacher(Integer id);
}

TeacherMapper.xml

<select id="getTeacher" resultType="teacher">
        select t.id tea_id, t.name tea_name  from teacher t where t.id = #{id}
    </select>

StudentMapper

 /**
     * 查询一个学生与一个老师的信息
     * @param id
     * @return
     */
    Student  getStuAndTeaByAssociationFenBu(Integer id);

StudentMapper.xml

  <resultMap id="byAssociation1" type="student">
    <id column="id" property="id"/>
    <result column="name" property="stuName"/>
    <result column="tid" property="teaId"/>
    <association property="teacher" column="tid"
                 javaType="com.example.pojo.Teacher"
                 select="com.example.mapper.TeacherMapper.getTeacher">
    </association>
    </resultMap>

    <select id="getStuAndTeaByAssociationFenBu" resultMap="byAssociation1">
        select * from student s where s.id = #{id}

    </select>

在这里插入图片描述

12.5.3.2 一对多关联查询(collection)

查询需求:查询一个老师有多少个学生

collection 单步查询

修改Teacher.java 加入如下属性

    private List<Student> listStu;

TeacherMapper.java

    public Teacher getAllStudentByTeaId(Integer id);

 <resultMap id="allStudentByCollection" type="teacher">
        <id column="tea_id" property="teaId"/>
        <result column="tea_name" property="teaName"/>
        <collection property="listStu"  ofType="com.example.pojo.Student">
            <id column="id" property="id"/>
            <result column="name" property="stuName"/>
            <result column="tid" property="teaId"/>
            <association property="teacher" column="tea_id"
                         select="com.example.mapper.TeacherMapper.getTeacher"
                         javaType="com.example.pojo.Teacher"/>
        </collection>
    </resultMap>


<select id="getAllStudentByTeaId" resultMap="allStudentByCollection">
    select  t.id  tea_id ,t.`name` tea_name ,s.*
    from student s ,teacher t where t.id = s.tid  and t.id = #{id}
</select>

测试方法


    @Test
    public void collectionTest(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher allStudentByTeaId = mapper.getAllStudentByTeaId(2);
        System.out.println(allStudentByTeaId);
        sqlSession.close();
    }

运行结果
在这里插入图片描述

collection 分步

StudentMapper.java

   Student getStudentById(Integer id);

TeacherMapper.java

     Teacher getAllStudentByTeaIdFenBU(Integer id);

StudentMapper.xml

<select id="getStudentById" resultType="student">
        select  t.id,t.name stu_name, t.tid tea_id from student t  where t.tid=#{id}
    </select>

TeacherMapper.xml

 <resultMap id="allStudentByCollectionFenBu" type="teacher">
    <id column="id" property="teaId"/>
    <result column="name" property="teaName"/>
    <collection property="listStu" column="stu_id" ofType="com.example.pojo.Student"
      select="com.example.mapper.StudentMapper.getStudentById"/>
</resultMap>

    <select id="getAllStudentByTeaIdFenBU" resultMap="allStudentByCollectionFenBu">
        select t.*, st.stu_id  from teacher t, stu_tea st  where st.tea_id = t.id and t.id = #{id}
    </select>

测试方法

 @Test
    public void collectionTestFenBu(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher allStudentByTeaId = mapper.getAllStudentByTeaIdFenBU(1);
        System.out.println(allStudentByTeaId);
        sqlSession.close();
    }

在这里插入图片描述

12.5.3.3 多对多关联查询 (collection)

实际应用中,由于多对多的关系比较复杂,会增加理解和关联的复杂度,所以应用较少。MyBatis 没有实现多对多级联,推荐通过两个一对多级联替换多对多级联,以降低关系的复杂度,简化程序。

可以使用一个中间表(学生与老师表)将多对多级联转换成两个一对多的关系。

多对多不能指定条件查询,如果指定,就变成了一对多。

collection 单步查询

Student.java 完整属性。


	private Integer id;

    private String  stuName;

    private Integer teaId;

    private List<Teacher>  teacher;

Teacher完整属性。


    private Integer teaId;

    private String  teaName;

    private List<Student> listStu;

TeacherMapper.java

     List<Teacher> SelectByManyToMany();

TeacherMapper.xml

 <resultMap id="ManyToMany" type="teacher">
        <id column="tea_id" property="teaId"/>
        <result column="tea_name" property="teaName"/>

        <collection property="listStu" ofType="com.example.pojo.Student">
            <id column="id" property="id"/>
            <result column="name" property="stuName"/>
            <result column="tid" property="teaId"/>
        </collection>
    </resultMap>



    <select id="SelectByManyToMany" resultMap="ManyToMany">
        SELECT s.*,t.id tea_id ,t.`name` tea_name
        FROM student s ,teacher t , stu_tea  st  WHERE  s.id =st.stu_id AND  st.tea_id = t.id
    </select>

测试方法

 @Test
    public void ManyToMany(){

        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        List<Teacher> allStudentByTeaId = mapper.SelectByManyToMany();
        System.out.println(allStudentByTeaId);
        sqlSession.close();
    }
12.5.3.4 鉴别器(discriminator)

鉴别器:如果结果为真,就执行对应的结果映射。相当于Switch语句,

必要的属性
对于< discriminator>来说,它的两个必要的属性就是javaType和column,它们相当于指定了switch(x)中x的类型,以及它在结果集的哪列。

对于< case>来说,它也有两个必要的属性,value,相当于switch-case中的case :。第二个属性是resultMap或者resultType


     <resultMap id="getStudentByDiscriminator" type="student">
        <id column="id" property="id"/>
        <result column="name" property="stuName"/>
        <result column="tid" property="teaId"/>
        <discriminator javaType="int" column="tid">
            <case value="1" resultMap="getStudentByDiscriminator"/>
            <case value="2" resultType="student"/>
        </discriminator>
    </resultMap>

    <select id="getStudent" resultMap="getStudentByDiscriminator">
        select * from student s where s.id = #{id}

    </select>

当discriminator 中的column的值等于 case 的 值时,就使用 resultMap值的值进行映射。

需求信息

当学生的tid值等于2时 查询老师的所有信息

 <resultMap id="getStudentByDiscriminator" type="student">
        <id column="id" property="id"/>
        <result column="name" property="stuName"/>
<!--        <result column="tid" property="teaId"/>-->
        <discriminator javaType="int" column="tid">
            <case value="1" resultMap="getStudentByDiscriminator"/>
            <case value="2" resultType="student">
            <association property="teacher" column="tid" select="com.example.mapper.TeacherMapper.getTeacher"/>
            </case>
        </discriminator>
    </resultMap>

    <select id="getStudent" resultMap="getStudentByDiscriminator">
        select * from student s where s.id = #{id}

    </select>

测试代码


    @Test
    public void testStudent(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        // 等于1 不查询老师的信息,等于2 查询出老师的信息
        Student student = mapper.getStudent(2);
        System.out.println(student);
        sqlSession.close();
    }

测试结果, 测试id等于1时
在这里插入图片描述
测试结果, 测试id等于2时
在这里插入图片描述

12.5.3.5 sql片段

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。比如:


<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns"><property name="alias" value="t1"/></include>,
    <include refid="userColumns"><property name="alias" value="t2"/></include>
  from some_table t1 cross join some_table t2
</select>

总结

整个SQL的语句中,都可以使用sql片段,达到可以在多长引用的目的。

12.6 动态SQL

解决需要手动拼接 SQL 语句容易出现问题的麻烦。如去除空格,逗号等。
在这里插入图片描述

12.6.1 if标签

表示 如果传进来的属性值不为空,值执行对应的条件。


   
    <select id="selectIf" resultType="student">
        select * from student where
        <if test="id!=null" >
          id = #{id}
        </if>
        <if test="teaId!=null" >
            and tid = #{teaId}
        </if>
        <if test="stuName!=null" >
           and name like #{stuName}
        </if>
    </select>

但是,以上有一个缺陷,如果第一个条件为空,那么where 与and语句就是一个错误的语句。解决办法是在where后面加一个 必成立的条件。


    <select id="selectIf" resultType="student">
        select * from student where 1=1
        <if test="id!=null" >
          id = #{id}
        </if>
        <if test="teaId!=null" >
            and tid = #{teaId}
        </if>
        <if test="stuName!=null" >
           and name like #{stuName}
        </if>
    </select>

上面的问题解决了,但是,发现不怎么友好,最好的解决办法是加一个where标签



    <select id="selectIf" resultType="student">
        select s.id, s.name stu_name,s.tid tea_id from student s
       <where>
        <if test="id!=null" >
         and id = #{id}
        </if>
        <if test="teaId!=null" >
            and tid = #{teaId}
        </if>
        <if test="stuName!=null" >
           and name like #{stuName}
        </if>

       </where>
    </select>

update 更新



    <update id="update" >
        update student
        <set>
            <if test="id!=null" >
                id = #{id}
            </if>
            <if test="teaId!=null" >
                tid = #{teaId}
            </if>
            <if test="stuName!=null" >
                name = #{stuName}
            </if>
        </set>
        
     where id = #{id}
    </update>

12.6.2 trim 标签

支持在条件前或后增加拼接字符串,如,在查询 条件前增加where字符串,第一个条件会有where,如果到了最后一个where,则去除后面的逗号。

<!-- 
		trim:
			prefix:给拼串后的整个字符串加一个前缀
			suffixOverrides:去掉整个字符串后面多余的字符,支持或(|)
			
			prefixOverrides:去掉整个字符串前面多余的字符,支持或(|)
			suffix:给拼串后的整个字符串加一个后缀
	 -->
    <select id="selectIf1" resultType="student">
        select s.id, s.name stu_name,s.tid tea_id from student s
        <trim prefix="where" suffixOverrides="and|RO">
            <if test="id!=null" >
                 id = #{id} and
            </if>
            <if test="teaId!=null" >
                 tid = #{teaId}  and
            </if>
            <if test="stuName!=null" >
             name like #{stuName}  and
            </if>

        </trim>
    </select>

更新中的Set方法如下

 <update id="update" >
        update student
        <trim prefix="set" suffixOverrides=",">
            <if test="id!=null" >
                id = #{id} ,
            </if>
            <if test="teaId!=null" >
                tid = #{teaId},
            </if>
            <if test="stuName!=null" >
                name = #{stuName},
            </if>
        </trim>

     where id = #{id}
    </update>

12.6.3 choose (when, otherwise)

跟Switch语句一样,满足什么条件就去查询什么条件,有默认值,跟if不同的是,if没有默认值。


 <select id="selectIf2" resultType="student">
        select s.id, s.name stu_name,s.tid tea_id from student s
        <trim prefix="where" suffixOverrides="and|RO">
        <choose>
            <when test="id!=null" >
                s.id = #{id}
            </when>
            <when test="teaId!=null" >
                s.tid = #{teaId}
            </when>
            <when test="stuName!=null" >
                s.name like #{stuName}
            </when>

            <otherwise>
                s.name = #{name}
            </otherwise>
        </choose>
        </trim>
    </select>

12.6.4 foreach标签

代表迭代器,如,SQL中的子查询 in 语句。

foreach 标签主要有以下属性,说明如下。
item:表示集合中每一个元素进行迭代时的别名。
index:指定一个名字,表示在迭代过程中每次迭代到的位置。
open:表示该语句以什么开始(既然是 in 条件语句,所以必然以(开始)。
separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以,作为分隔符)。
close:表示该语句以什么结束(既然是 in 条件语句,所以必然以)开始)。

使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array。
如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。

StudentMapper.java

    List<Student> forEach(List<Integer> list);


    <select id="forEach" resultType="student">
        select  s.id, s.name stu_name,s.tid tea_id from student s
           <where> s.tid in
            <foreach collection="list" index="index" item="tid" open="(" separator="," close=")">
                #{tid}
            </foreach>
           </where>
    </select>

测试

 @Test
    public void forEach(){
        StudentMapper studentMapper = MybatisUtils.getSqlSession().getMapper(StudentMapper.class);
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        List<Student> list1 = studentMapper.forEach(list);
        for (Student s:list1) {
            System.out.println(s);
        }
    }

运行结果

在这里插入图片描述

12.6.5 两个内置参数

mybatis默认还有两个内置参数:

  • _parameter:代表整个参数
    单个参数:_parameter就是这个参数
    多个参数:参数会被封装为一个map,_parameter就是代表这个map
    _databaseId:如果配置了databaseIdProvider标签,那么databaseId就是代表当前数据库的别名,比如:oracle

 <select id="forEach1" resultType="student">
        <bind name="stuName" value="'%'+stuName+'%'"/>

        select  s.id, s.name stu_name,s.tid tea_id from student s
        <where>
        <if test="_parameter!=null">
            s.name like #{stuName}
        </if>

        </where>
    </select>

12.6.6 分页

MyBatis 的分页功能是基于内存的分页,即先查询出所有记录,再按起始位置和页面容量取出结果。

SQL 语句分页

    List<Student> page(@Param("indexPage") Integer pageNum, @Param("pageSize")Integer pageSize);

<select id="page" resultType="student" >
        select  s.id, s.name stu_name,s.tid tea_id from student s
          
        ORDER BY s.id limit #{indexPage},#{pageSize}
    </select>

RowBounds

    List<Student> rowBounds(RowBounds rowBounds);

    <select id="rowBounds" resultType="student">
        select  s.id, s.name stu_name,s.tid tea_id from student s
    </select>
@Test
    public void rowBounds(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        RowBounds rowBounds = new RowBounds(3,3);
        List<Student> page = mapper.rowBounds(rowBounds);
        for (Student s:page) {
            System.out.println(s);
        }
    }

12.7缓存

MyBatis系统中默认定义了两级缓存,它们分别是:一级缓存和二级缓存。

12.7.1 一级缓存

级缓存(local cache),即本地缓存,作用域默认为 sqlSession。当 Session flush 或 close 后,该 Session 中的所有 Cache 将被清空。

本地缓存不能被关闭,但可以调用 clearCache() 来清空本地缓存或者改变缓存的作用域。

12.7.2 二级缓存

二级缓存(second level cache),它是基于namespace级别的全局作用域缓存,二级缓存默认不开启,需要手动配置,要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<!-- 
	cache:
		eviction:缓存的回收策略。
			• LRU – 最近最少使用:移除最长时间不被使用的对象。(默认值)
			• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
			• SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
			• WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
		flushInterval:刷新间隔,该属性可以被设置为任意的正整数,单位为毫秒, 默认情况是不设置。
		size:引用数目,该属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源,默认值是 1024。
		readOnly:只读,该属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 
		type:指定自定义缓存的全类名,实现Cache接口即可。
 -->
<cache eviction="LRU"
		flushInterval="60000"
		size="1024"
		readOnly="true"
		type="" />
		
<!--    引用另一个命名空间的缓存-->
<cache-ref namespace="com.someone.application.data.SomeMapper"/>

12.7.3 和缓存有关的设置

  • mybatis-config.xml的settings标签中的配置:

< setting name=“localCacheScope” value=“SESSION”/>,本地缓存(一级缓存)作用域,当前会话的所有数据保存在会话缓存中,如果值为STATEMENT可以禁用一级缓存
< setting name=“cacheEnabled” value=“true”/>,二级缓存开启和关闭的开关,如果为true则开启二级缓存

  • xxxxxMapper.xml的每个select标签中的配置:

useCache=“true”:默认值为true,如果值为false代表select标签不使用缓存(一级缓存可使用,二级缓存不使用)
flushCache=“false”:默认值为false,如果值为false代表select标签不清除缓存

  • xxxxxMapper.xml的每个insert、update、delete标签中的配置:

flushCache=“true”:默认值为true,如果值为true代表清除缓存(一级二级都会清除)

  • sqlSession的调用代码中:

sqlSession.clearCache():只是清除当前session的一级缓存,而二级缓存不会清除

12.8 代码生成

代码生成器依赖

<dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.4.0</version>
        </dependency>

生成器配置文件


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- j驱动包绝对路径 -->
    <classPathEntry
            location="E:\Program Files (x86)\maven\apache-maven-3.8.1\repository\org\mybatis\generator\mybatis-generator-core\1.4.0\mybatis-generator-core-1.4.0.jar"/>

    <context id="default" targetRuntime="MyBatis3">

        <property name="javaFileEncoding" value="UTF-8"/>

        <!-- 不输出注释 -->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
            <property name="addRemarkComments" value="true"/>
        </commentGenerator>

        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/localhost"
                        userId="root"
                        password="root">
        </jdbcConnection>


        <!-- 不强制把所有的数字类型转化为BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <javaModelGenerator targetPackage="com.example.entity" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <javaClientGenerator type="XMLMAPPER" targetPackage="com.example.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <table tableName="student"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false"
               domainObjectName="Student"
               mapperName="StudentMapper">
            <generatedKey column="id" sqlStatement="MySql"/>
        </table>
    </context>
</generatorConfiguration>

驱动包路径查看f方式

在这里插入图片描述

运行代码生成器

package com.example.utils;

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: lyf
 * @CreateTime: 2022-12-08
 * @description:
 */
public class MybatisGeneratorUtils {

    public static void main(String[] args) throws Exception {

        List<String> warnings = new ArrayList<>();
        // 如果已经存在生成过的文件是否进行覆盖
        boolean overwrite = true;
        File configFile = new File("E:\\testProject\\Mybatis3\\src\\main\\resources\\mybatis-generator-config.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
        generator.generate(null);
    }

}

12.9 注解开发

配置文件

<?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="dataSource.properties"/>-->


<!--    第二种配置 子属性-->
    <properties>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/localhost?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </properties>

    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
<!--        <setting name="logImpl" value="log4j2"/>-->
    </settings>

    <typeAliases>
<!--        给一个类设置别名,不设置值。默认是类名小写-->
        <typeAlias alias="user" type="com.example.pojo.User"/>
<!--        以包下的类设置别名 别名类名首字母小写-->
        <package name="com.example.pojo"/>
    </typeAliases>

<!--    typeHandler 的作用就是承担 jdbcType 和 javaType 之间的相互转换-->
    <typeHandlers>

    </typeHandlers>

<!--    数据库环境 default 属性指定一个默认数据源-->
    <environments default="development">
<!--        数据源配置-->
        <environment id="development">
<!--            配置事务 类型
                             JDBC 和 MANAGED
                如果使用 JDBC 类型的事务管理器,则应用程序服务器负责事务管理操作,例如提交、回滚等。
                如果使用 MANAGED 类型的事务管理器,则应用程序服务器负责管理连接生命周期
-->
            <transactionManager type="JDBC"/>
<!--            配置数据库的连接属性
1)UNPOOLED
 没有数据库连接池,效率低下。需要每次打开和关闭每个数据库操作的连接。
2)POOLED
MyBatis 将维护一个数据库连接池。
3)JNDI
MyBatis 将从 JNDI 数据源中获取连接。
-->
            <dataSource type="POOLED">

<!--                <property name="driver" value="${datasource.driver}"/>-->
<!--                <property name="url" value="${datasource.url}"/>-->
<!--                <property name="username" value="${datasource.username}"/>-->
<!--                <property name="password" value="${datasource.password}"/>-->

                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
<!--    指定Mapper文件所在的位置-->
    <mappers>

        <package name="com.example.mapper"/>
    </mappers>
</configuration>

数据准备

#清空表
TRUNCATE TABLE employee;
TRUNCATE TABLE department;

#添加数据向employee表
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"张三","123@qq.com","男",1);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"李四","123@qq.com","男",2);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"王五","123@qq.com","女",3);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"小六","123@qq.com","男",1);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"小七","123@qq.com","男",2);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"老八","123@qq.com","女",3);
INSERT INTO employee(id,last_name,email,gender,dep_id) 
VALUES(NULL,"老九","123@qq.com","男",1);

#添加数据向department表
INSERT INTO department(dep_id,dep_name) VALUES(1,"运营部");
INSERT INTO department(dep_id,dep_name) VALUES(2,"开发部");
INSERT INTO department(dep_id,dep_name) VALUES(3,"产品部");


实体类对象按数据表创建或者用代码生成即可。

12.9.1 增删改查注解


public interface EmployeeMapper {

	@Select(value = "select * from employee where id=#{id}")
	List<Employee> getListEmp(Integer id);

	/**
	 * 插入员工
	 *
	 * @param employee
	 */
	@Insert(value = "insert into employee(id,last_name,email,gender,dep_id) values(null,#{lastName},#{email},#{gender},#{dep.id})",databaseId="mysql")
 	void insertOne(Employee employee);

	@Select(value = "select * from employee where id =#{id}",databaseId = "mysql")
	Employee selectOne(Integer id);

	@Select(value = "select * from employee")
	List<Employee> selectList();
	/**
	 * 更新员工信息
	 * @param emp
	 * @Update:更新员工信息
	 * 	value:更新语句,如果只有一个数据源,可以直接@Update("update ...")
	 * 	databaseId:数据库别名,如果只有一个数据源,可以省略,@since 3.5.5
	 */
	@Update(value = "update employee set last_name=#{lastName},email=#{email},gender=#{gender},dep_id=#{dep.id} where id = #{id}", databaseId = "mysql")
	public void updateEmp(Employee emp);


		/**
 * 删除员工信息
 * @param id
 * @Delete:删除员工信息
 * 	value:
 */
@Delete(value = "delete from employee where id=#{id}", databaseId = "mysql")
public void deleteEmpById(Integer id);


}

12.9.2 一对一注解

需求信息: 查询员工所在的部门

EmployeeMapper

/**
 * 根据员工的部门id查询出部门信息
 * @param depId
 * @return
 * @Results:代替的是<resultMap>标签,注解中可以使用单个@Result注解,也可以使用@Result集合
 * 	@Result:代替的是<id>标签和<result>标签
 * 		id:是否为主键,是为true,否为false
 * 		column:数据库的列名
 * 		property:实体类的属性名
 * 		@Many:一对多配置,代替的是<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
 * 			select:指定用来多表查询的sqlmapper,它是对应接口方法的全限定方法名
 * 			fetchType:懒加载配置,会覆盖全局的配置参数 lazyLoadingEnabled,一对多一般是延迟加载
 * 				FetchType.LAZY:延迟加载
 * 				FetchType.EAGER:立即加载
 */
	@Select(value = "select * from employee where id =#{id}")
	@Results({
			@Result(id = true,column = "id",property = "id"),
			@Result(column = "last_name",property = "lastName"),
			@Result(column = "email",property = "email"),
			@Result(column = "gender",property = "gender"),
			@Result(column = "dep_id",property = "dep",one = @One(select = "com.lyf.mybatis.mapper.DepartmentMapper.getById",fetchType = FetchType.EAGER))
	})
	Employee getEmpById(Integer id);

DepartmentMapper

 @Select(value = "select * from department where dep_id =#{id}" ,databaseId = "mysql")
    Department getById(Integer id);

12.9.3 一对多注解

需求信息: 根据部门id查询出所有的员工信息

EmployeeMapper

@Select(value = "select * from employee where id =#{id}",databaseId = "mysql")
	Employee selectOne(Integer id);

DepartmentMapper


/**
 * 根据部门编号查询部门级联查询该部门下所有员工
 * @param depId
 * @return
 * @Results:代替的是<resultMap>标签,注解中可以使用单个@Result注解,也可以使用@Result集合
 * 	@Result:代替的是<id>标签和<result>标签
 * 		id:是否为主键,是为true,否为false
 * 		column:数据库的列名
 * 		property:实体类的属性名
 * 		@Many:一对多配置,代替的是<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合
 * 			select:指定用来多表查询的sqlmapper,它是对应接口方法的全限定方法名
 * 			fetchType:懒加载配置,会覆盖全局的配置参数 lazyLoadingEnabled,一对多一般是延迟加载
 * 				FetchType.LAZY:延迟加载
 * 				FetchType.EAGER:立即加载
 */

    @Select(value = "select * from department where dep_id = #{id}")
    @Results({
            @Result(id = true,column = "dep_id",property = "id"),
            @Result(column = "dep_name",property = "depName"),
            @Result(column = "dep_id",property = "employeeList",many = @Many(select = "com.lyf.mybatis.mapper.EmployeeMapper.getListEmp",fetchType = FetchType.EAGER))
    })
    Department getDep(Integer id);

总结

@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@ResultMap:实现引用@Results定义的封装
@One:实现一对一结果集封装
@Many:实现一对多结果集封装

在注解中使用动态SQL看起来比较乱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值