Java 基础

MyBatis 基础

2022-03-19  本文已影响0人  yjtuuige

环境说明:

需要掌握:

一、Mybatis 简介

1.1 什么是 MyBatis

1.2 持久化

为什么需要持久化服务?

1.3 持久层

什么是持久层?

1.4 为什么需要 Mybatis

MyBatis 的优点

二、使用 Mybatis

2.1 搭建环境

CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`(
    `id` INT NOT NULL,
    `name` VARCHAR(30) DEFAULT NULL,
    `pwd` VARCHAR(30) DEFAULT NULL,
    PRIMARY KEY (`id`)
)ENGINE INNODB DEFAULT CHARSET=utf8mb4;

INSERT INTO `user`(`id`,`name`,`pwd`) VALUES 
(1,'张三','123456'),
(2,'李四','abc'),
(3,'王五','111');

2.2 创建模块(子项目)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/数据库名?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                <property name="username" value="用户名"/>
                <property name="password" value="密码"/>
            </dataSource>
        </environment>
    </environments>
    <!--注册 Mapper.xml-->
    <mappers>
        <mapper resource="com/study/mybatis/dao/userMapper.xml"/>
    </mappers>
</configuration>

编写代码

// SqlSessionFactory --> SqlSession
public class MybatisUtils {
    // 提升作用域
    private static SqlSessionFactory sqlSessionFactory;

    // 静态代码块:类初始化时,一起加载
    // 1.获取SqlSessionFactory对象(加载资源文件)
    static {
        try {
            // 获取资源配置文件
            String resource = "mybatis-config.xml";
            // 配置文件转换为输入流
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 创建SqlSessionFactory实例    build加载流
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 2.通过sqlSessionFactory,获得sqlSession实例(创建执行sql的对象)
    // sqlSession完全包含,面向数据库执行sql命令所需的所有方法
    // 获取SqlSession连接
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}
public class User {
    // id
    private int id;
    // 姓名
    private String name;
    // 密码
    private String pwd;
    // 构造方法:有参、无参
    // set/get
    // toString()
}
public interface UserMapper {
    public List<User> getUserList();
}
<?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">
<!--namespace 对应接口类,需要完整包名+类名-->
<mapper namespace="com.study.mybatis.dao.UserMapper">
    <!--id 对应接口类的方法名,resultType 返回类型(集体为泛型),需要完整包名+类名-->
    <select id="getUserList" resultType="com.study.mybatis.pojo.User">
        select * from `user`
    </select>
</mapper>
public class UserTest {
    @Test
    public void selectUser() {
        // 1.获取SqlSession对象
        SqlSession session = MybatisUtils.getSession();

        // 2.执行sql:需要获取dao(UserMapper)
        // 方法一:不推荐
        // List<User> users = session.selectList("com.study.mybatis.dao.UserMapper.getUserList");
        // 方法二:
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.getUserList();
        for (User user : users) {
            System.out.println(user);
        }
        // 3.关闭SqlSession
        session.close();
    }
}

2.3 可能出现的问题:

  1. 未在 Mybatis 核心配置文件中注册 Mapper.xml
<!--注册 Mapper.xml-->
<mappers>
    <mapper resource="com/study/mybatis/dao/userMapper.xml"/>
</mappers>
  1. Maven 静态资源过滤问题:
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

三、CRUD 操作

3.1 namespace

3.2 select 查询

实例:根据 id 查询用户

// 根据id查询用户
public User getUserById(int id);
<!--根据id查询用户-->
<select id="getUserById" resultType="com.study.mybatis.pojo.User">
    select * from `user` where id=#{id}
</select>
// 根据id查询用户
@Test
public void getUserById() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.getUserById(2);
    System.out.println(user);
    session.close();
}

3.3 insert 插入

在数据库中,增加一个用户

// 增加用户
public int addUser(User user);
<!--增加用户-->
<insert id="addUser" parameterType="com.study.mybatis.pojo.User">
    insert into `user` (id, name, pwd) values (#{id}, #{name}, #{pwd})
</insert>
// 增加用户
@Test
public void addUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    int i = mapper.addUser(new User(4, "测试", "111"));
    if (i > 0) {
        System.out.println("添加成功!");
    }
    // 提交事务:增、删、改操作,都需要提交事务
    session.commit();
    session.close();
}

3.4 update 修改

修改用户的信息

// 根据id修改用户信息
public int updateUser(User user);
<!--修改用户信息-->
<update id="updateUser" parameterType="com.study.mybatis.pojo.User">
    update `user` set `name`=#{name},`pwd`= #{pwd} where `id`= #{id}
</update>
// 修改用户信息
    @Test
    public void updateUser() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int i = mapper.updateUser(new User(4, "新用户", "123456"));
        if (i > 0) {
            System.out.println("修改成功");
        }
        // 提交事务:增、删、改操作,都需要提交事务
        session.commit();
        session.close();
    }

    // 修改用户信息2
    @Test
    public void updateUser2() {
        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.getUserById(4);
        user.setPwd("abfdas");
        int i = mapper.updateUser(user);
        if (i > 0) {
            System.out.println("修改成功");
        }
        // 提交事务:增、删、改操作,都需要提交事务
        session.commit();
        session.close();
    }

3.5 delete 删除

根据 id 删除一个用户

// 删除用户
public int deleteUser(int id);
<!--删除用户-->
<delete id="deleteUser" parameterType="int">
    delete from `user` where `id`=#{id}
</delete>
// 删除用户
@Test
public void deleteUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    int i = mapper.deleteUser(4);
    if (i > 0) {
        System.out.println("删除成功!");
    }
    // 提交事务:增、删、改操作,都需要提交事务
    session.commit();
    session.close();
}

可能出现的问题:

3.6 使用万能的 Map

根据密码和名字查询用户

// 根据密码和名字查询用户
public User selectUserByNP(Map<String,Object> map);
}
<!--根据密码和名字查询用户-->
<select id="selectUserByNP" parameterType="map" resultType="com.study.mybatis.pojo.User">
    select * from `user` where name = #{username} and pwd =#{password}
</select>
 // 根据密码和名字查询用户
@Test
public void selectUser1() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    Map<String, Object> map = new HashMap<>();
    map.put("username", "张三");
    map.put("password", "123456");
    User user = mapper.selectUserByNP(map);
    if (user != null) {
        System.out.println(user);
    }
    session.close();
}

3.7 模糊查询

  1. 在 Java 代码中,添加 sql 通配符 %
<!--模糊查询 java-->
<select id="getUserLike" resultType="com.study.mybatis.pojo.User">
    select * from `user` where name like #{value}
</select>
// 模糊查询
@Test
public void selectUser2() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    String wildCardName = "%李%";
    List<User> user = mapper.getUserLike(wildCardName);
    System.out.println(user);
    session.close();
}
  1. 在 sql 语句中,拼接通配符 %,会引起 sql 注入:
<!--模糊查询 sql-->
<select id="getUserLike" resultType="com.study.mybatis.pojo.User">
    select * from `user` where name like "%"#{value}"%"
</select>
String wildCardName = "李";
List<User> user = mapper.getUserLike(wildCardName);

小结:

四、配置解析

4.1 核心配置文件

<!-- 注意元素节点的顺序!顺序不对会报错 -->
configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
        environment(环境变量)
            transactionManager(事务管理器)
            dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)

4.2 环境配置(environments)

<environments default="development">
    <!--具体的一套环境-->
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

4.3 属性(properties)

优化配置文件

# mysql8 驱动
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名?useUnicode=true;characterEncoding=utf8;useSSL=true
username=用户名
password=密码
<!--configuration核心配置文件-->
<configuration>
    <!--导入properties文件-->
    <properties resource="db.properties"/>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--Mapper.xml-->
    <mappers>
        <mapper resource="com/study/mybatis/dao/userMapper.xml"/>
    </mappers>
</configuration>

4.4 类型别名(typeAliases)

设置别名的两种方式:在核心配置文件中设置

  1. 为每个类单独指定类名:
    • 实体类较少
    • 别名可自定义;
<!--实体类设置别名-->
<typeAliases>
    <typeAlias type="com.study.mybatis.pojo.User" alias="User"/>
</typeAliases>
  1. 扫描包:
    • 实体类较多
    • 首字母小写的类名,作为别名(User --> user);
    • 若有注解,则别名为其注解值;
<!--指定包名-->
<typeAliases>
    <package name="com.study.mybatis.pojo"/>
</typeAliases>
@Alias("user")
public class User {
    ...
}

4.5 设置(settings)

<settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useColumnLabel" value="true"/>
    <setting name="useGeneratedKeys" value="false"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
    <setting name="defaultExecutorType" value="SIMPLE"/>
    <setting name="defaultStatementTimeout" value="25"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <setting name="mapUnderscoreToCamelCase" value="false"/>
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

4.6 其它设置(了解)

4.7 映射器(mappers)

实现方式:

<mappers>
    <mapper resource="com/study/mybatis/dao/userMapper.xml"/>
</mappers>
<mappers>
    <mapper class="com.study.mybatis.dao.UserMapper"/>
</mappers>
<mappers>
    <package name="com.study.mybatis.dao"/>
</mappers>

注意:

4.8 生命周期和作用域

五、ResultMap 结果集映射

5.1 查询为 null

public class User {
    private int id;
    private String name;
    // 密码,和数据库不一致
    private String password;
    // 构造函数
    // set/get
    // toString()
}
// 根据id查询用户
public User getUserById(int id);
<!--根据id查询用户-->
<select id="getUserById" resultType="User">
    select * from `user` where id = #{id}
</select>
// 根据id查询用户
@Test
public void getUserById() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.getUserById(2);
    System.out.println(user);
    session.close();
}

分析:

5.2 解决方案

<select id="getUserById" resultType="User">
    select `id`,`name`,`pwd` as password from `user` where id = #{id}
</select>
<!--结果集映射 id对应resultMap type对应实体类-->
<resultMap id="userMap" type="User">
    <!-- id为主键 -->
    <id column="id" property="id"/>
    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" resultMap="userMap">
    select * from `user` where id = #{id}
</select>

5.3 ResultMap

  1. 自动映射:
<!--简单映射-->
<select id="getUserById" resultType="User">
    select * from `user` where id = #{id}
</select>
  1. 手动映射
<select id="getUserById" resultMap="userMap">
    select `id`,`name`,`pwd` from `user` where id = #{id}
</select>
<resultMap id="userMap" type="User">
    <!-- id为主键 -->
    <!--<id column="id" property="id"/>-->
    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
    <!--<result column="name" property="name"/>-->
    <!--可以只映射数据库与类属性不对应的-->
    <result column="pwd" property="password"/>
</resultMap>

六、日志

6.1 日志工厂

标准日志实现

<!--标准日志-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

6.2 Log4j

简介:

使用步骤:

# 将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

# 控制台输出的相关设置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 文件输出的相关设置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/mybatis-04.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd HH:mm:ss}][%c]%m%n

# 日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
<!--Log4j日志-->
<settings>
    <setting name="logImpl" value="Log4j"/>
</settings>
public class UserTest {
    // 注意导包:org.apache.log4j.Logger
    // 日志对象:参数为当前类的class
    static Logger logger = Logger.getLogger(UserTest.class);

    @Test
    public void getUserById() { 
        // 日志级别
        logger.info("info级别日志:进入getUserById方法");
        logger.debug("debug级别日志:进入getUserById方法");
        logger.error("error级别日志: 进入getUserById方法");

        SqlSession session = MybatisUtils.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        System.out.println(user);
        session.close();
    }
}

七、分页的实现

7.1 使用 limit 分页(掌握)

<!--stratIndex:起始记录 pageSize:页面大小(几条记录)-->
SELECT * FROM table LIMIT stratIndex,pageSize
<!--检索记录行 6-15-->
SELECT * FROM table LIMIT 5,10
<!--只给定一个参数,表示返回最大的记录行数目,等价于 LIMIT 0,n -->
SELECT * FROM table LIMIT 5
<!--先排序,再分页-->
SELECT * FROM table LIMIT 5,10 ORDER BY id

步骤:

// 用户列表:分页
public List<User> getUserByLimit(Map<String,Integer> map);
<!--用户列表:分页-->
<select id="getUserByLimit" parameterType="map" resultMap="userMap">
    select * from `user` limit #{startIndex},#{pageSize}
</select>
@Test
public void testLimit() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 当前页
    int currentPage = 1;
    // 页面大小(每页显示记录数)
    int pageSize = 2;
    Map<String, Integer> map = new HashMap<>();
    map.put("startIndex", (currentPage - 1) * pageSize);
    map.put("pageSize", pageSize);
    List<User> userList = mapper.getUserByLimit(map);
    for (User user : userList) {
        System.out.println(user);
    }
    session.close();
}

7.2 RowBounds 分页(了解)

// 分页:RowBounds
public List<User> getUserByRowBounds();
<!--用户列表:分页 RowBounds-->
<select id="getUserByRowBounds" resultMap="userMap">
    select * from `user`
</select>
@Test
public void testRowBounds() {
    SqlSession session = MybatisUtils.getSession();
    int currentPage = 2;
    int pageSize = 2;
    RowBounds rowBounds = new RowBounds((currentPage - 1) * pageSize, pageSize);
    // 通过session.selectList方法传递rowBounds(已经不推荐使用)
    List<User> userList = session.selectList("com.study.mybatis.dao.UserMapper.getUserByRowBounds", null, rowBounds);
    for (User user : userList) {
        System.out.println(user);
    }
    session.close();
}

7.4 分页插件 PageHelper(了解)

八、使用注解开发

8.1 面向接口编程

关于接口的理解:

三个面向区别

8.2 利用注解开发

注解开发实例:只适用于简单的 SQL 语句;

public interface UserMapper {
    @Select("select * from `user`")
    List<User> selectUser();
}
<!--使用class绑定接口-->
<mappers>
    <mapper class="com.study.mybatis.dao.UserMapper"/>
</mappers>
@Test
public void testGetAllUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    List<User> usersList = mapper.selectUser();
    for (User user : usersList) {
        System.out.println(user);
    }
    session.close();
}

8.3 注解 CRUD 操作

// true 设置自动提交
public static SqlSession getSession() {
    return sqlSessionFactory.openSession(true);
}
public interface UserMapper {
    @Select("select * from `user`")
    List<User> selectUser();

    @Insert("insert into `user` (id,name,pwd) values (#{id},#{name},#{password})")
    int addUser(User user);

    // 方法存在多个参数,参数前必须加上@Param()注解
    @Select("select * from `user` where `id`=#{id}")
    User getUserById(@Param("id") int id);

    @Update("update `user` set `name`=#{name},`pwd`=#{password} where `id`=#{id}")
    int updateUser(User user);

    @Delete("delete from `user` where `id`=#{uid}")
    int deleteUser(@Param("uid") int id);
}

8.4 关于 @Param

使用原则

8.5 #{} 与 ${} 的区别

九、Lombok(了解)

使用步骤:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
    <scope>provided</scope>
</dependency>
// get、set、toString、equals、hashCode
@Data
// 有参构造
@AllArgsConstructor
// 无参构造
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

十、多对一的处理

10.1 数据库设计

CREATE TABLE `teacher` (
    `id` INT NOT NULL,
    `name` VARCHAR(30) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

INSERT INTO teacher(`id`, `name`) VALUES (1, '李老师');

CREATE TABLE `student` (
    `id` INT NOT NULL,
    `name` VARCHAR(30) DEFAULT NULL,
    `tid` INT DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `fktid` (`tid`),
    CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

10.2 搭建测试环境

// Lombok方式或手动创建get、set、toString、有参、无参构造
@Data
public class Student {
    private int id;
    private String name;
    // 学生需要关联一个老师(多对一方式)
    private Teacher teacher;
}
// Lombok方式或手动创建get、set、toString、有参、无参构造
@Data
public class Teacher {
    private int id;
    private String name;
}
<!--使用class绑定接口-->
<mappers>
    <mapper class="com.study.mybatis.dao.TeacherMapper"/>
    <mapper class="com.study.mybatis.dao.StudentMapper"/>
</mappers>

10.3 按查询嵌套处理

// 获取所有学生及对应老师的信息
List<Student> getStudentList();
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mybatis.dao.StudentMapper">
    <!--
        思路:
            1.查询所有的学生信息
            2.根据查询出来的学生的tid,寻找对应的老师!子查询
    -->
    <select id="getStudentList" resultMap="studentTeacher">
        select id, name, tid from student
    </select>

    <resultMap id="studentTeacher" type="Student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!--
            复杂的属性,需要单独处理
            对象:association
            集合:collection
            select:对应子查询
         -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <!--
        当传递过来的参数,只有一个属性的时候,可以写任何值
        association中column多参数配置:
        column="{key=value,key=value}"
        键值对,key是传给下个sql的取值名称,value是sql查询的字段名
    -->
    <select id="getTeacher" resultType="Teacher">
        select * from teacher where id = #{id}
    </select>
</mapper>
@Test
public void TestStudentList() {
    SqlSession session = MybatisUtils.getSession();
    StudentMapper mapper = session.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.getStudentList();
    for (Student student : studentList) {
        System.out.println(student);
    }
    session.close();
}

10.4 按结果嵌套处理

List<Student> getStudentList2();
<!--
    按查询结果嵌套处理
    思路:直接查询出结果,进行结果集的映射
-->
<select id="getStudentList2" resultMap="studentTeacher2">
    select s.id sid,s.name sname,t.name tname from student s,teacher t
    where s.tid=t.id
</select>
<resultMap id="studentTeacher2" type="Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <!--关联对象property 关联对象在Student实体类中的属性-->
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>
@Test
public void TestStudentList2() {
    SqlSession session = MybatisUtils.getSession();
    StudentMapper mapper = session.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.getStudentList2();
    for (Student student : studentList) {
        System.out.println(
                "学生:" + student.getName()
                        + "\t老师:" + student.getTeacher().getName()
        );
    }
    session.close();
}

小结:

十一、一对多的处理

11.1 实体类编写

// Lombok方式或手动创建get、set、toString、有参、无参构造
@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
// Lombok方式或手动创建get、set、toString、有参、无参构造
@Data
public class Teacher {
    private int id;
    private String name;
    // 一个老师对应多个学生(集合)
    private List<Student> students;
}

11.2 按结果嵌套处理

public interface TeacherMapper {
    // 获取指定老师,及老师下的所有学生
    // xml的取值#{tid}与@Param("tid")对应
    public Teacher getTeacher(@Param("tid") int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.mybatis.dao.TeacherMapper">
    <!--
        思路:
            1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
            2. 对查询出来的操作做结果集映射
            集合:使用collection
    -->
    <select id="getTeacher" resultMap="teacherStudent">
        select s.id sid, s.name sname, t.name tname, t.id tid
        from student s,
             teacher t
        where s.tid = t.id
          and t.id = #{tid};
    </select>

    <resultMap id="teacherStudent" type="Teacher">
        <id property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--ofType:实例类中,集合的泛型类型-->
        <collection property="students" ofType="Student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
</mapper>
<!--使用class绑定接口-->
<mappers>
    <mapper class="com.study.mybatis.dao.TeacherMapper"/>
    <mapper class="com.study.mybatis.dao.StudentMapper"/>
</mappers>
@Test
public void testGetTeacher() {
    SqlSession session = MybatisUtils.getSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher.getName());
    System.out.println(teacher.getStudents());
    session.close();
}

11.3 按查询嵌套处理

public Teacher getTeacher2(@Param("tid") int id);
<!--按查询嵌套处理-->
<select id="getTeacher2" resultMap="teacherStudent2">
    select * from teacher where id = #{tid}
</select>
<resultMap id="teacherStudent2" type="Teacher">
    <!--只映射不同的内容,id,name自动映射-->
    <!--column="id":teacher的id,传递给子查询-->
    <collection property="students" column="id" javaType="ArrayList" ofType="Student"
                select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{tid}
</select>
@Test
public void testGetTeacher() {
    SqlSession session = MybatisUtils.getSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher2(1);
    System.out.println(teacher.getName());
    System.out.println(teacher.getStudents());
    session.close();
}

小结:

十二、动态 SQL

12.1 搭建环境

CREATE TABLE `blog` (
    `id` VARCHAR(50) NOT NULL COMMENT '博客id',
    `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
    `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
    `create_time` DATETIME NOT NULL COMMENT '创建时间',
    `views` INT NOT NULL COMMENT '浏览量'
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4
public class IDUtil {
    public static String getId() {
        // 去掉UUID的"-",将值返回
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}
// Lombok方式或手动创建get、set、toString、有参、无参构造
@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    // 数据表字段create_time,类属性名createTime,
    // 需在核心配置文件,设置下划线驼峰自动转换
    private Date createTime;
    private int views;
}
public interface BolgMapper {
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
    
</mapper>
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    <!--下划线驼峰自动转换-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

<typeAliases>
    <typeAlias type="com.study.mybatis.pojo.Blog" alias="Blog"/>
</typeAliases>

<!--使用class绑定接口-->
<mappers>
    <mapper class="com.study.mybatis.mapper.BolgMapper"/>
</mappers>

插入初始数据:

int addBlog(Blog blog);
<insert id="addBlog" parameterType="Blog">
    insert into blog (id, title, author, create_time, views)
    values (#{id}, #{title}, #{author}, #{createTime}, #{views});
</insert>
public class TestBlog {
    @Test
    public void addInitBlog() {
        SqlSession session = MybatisUtils.getSession();
        BlogMapper mapper = session.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtil.getId());
        blog.setTitle("Mybatis如此简单");
        blog.setAuthor("admin");
        blog.setCreateTime(new Date());
        blog.setViews(9999);

        mapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("Java如此简单");
        mapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("Spring如此简单");
        mapper.addBlog(blog);

        blog.setId(IDUtil.getId());
        blog.setTitle("微服务如此简单");
        mapper.addBlog(blog);

        session.close();
    }
}

12.2 if 语句

// if 语句 根据作者和标题,查询
List<Blog> queryBlogIF(Map map);
<!--
    if 语句:根据作者和标题,查询博客
    select * from blog where author = #{author} and title = #{title}
-->
<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog where 1=1
    <if test="author!=null">
        and author=#{author}
    </if>
    <if test="title!=null">
        and title=#{title}
    </if>
</select>
@Test
public void testIf() {
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("author", "admin");
    map.put("title", "Mybatis如此简单");
    List<Blog> blogs = mapper.queryBlogIF(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    session.close();
}

问题:

12.3 choose 语句(when、otherwise)

List<Blog> queryBlogChoose(Map map);
<select id="queryBlogChoose" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <choose>
            <when test="author!=null">
                author=#{author}
            </when>
            <when test="title!=null">
                and title=#{title}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>
@Test
public void testChoose() {
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("author", "admin");
    map.put("title", "Mybatis如此简单");
    map.put("views", "9999");
    List<Blog> blogs = mapper.queryBlogChoose(map);
    for (Blog blog : blogs) {
        System.out.println(blog);
    }
    session.close();
}

12.4 trim(where、set)

where:

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <if test="author!=null">
            author=#{author}
        </if>
        <if test="title!=null">
            and title=#{title}
        </if>
    </where>
</select>

Set:

int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="author!=null">
            author=#{author},
        </if>
        <if test="title!=null">
            title=#{title}
        </if>
    </set>
    where id=#{id}
</update>
@Test
public void testSet() {
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("author", "test");
    map.put("title", "动态SQL");
    map.put("id", "dcbb78122be34654bb8750447ede2257");
    mapper.updateBlog(map);
    session.close();
}

12.5 SQL 片段

<sql id="if-title-author">
    <if test="author!=null">
        author=#{author}
    </if>
    <if test="title!=null">
        and title=#{title}
    </if>
</sql>
<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <!--如果refid指定的不在本文件中,需要在前面加上 namespace-->
        <include refid="if-title-author"></include>
        <!--还可以引用其他的sql片段-->
    </where>
</select>

12.6 Foreach 语句

List<Blog> queryBlogForeach(Map map);
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
    select * from blog
    <where>
        <!--
            collection:指定输入对象中的集合属性
            item:每次遍历生成的对象
            open:开始遍历时的拼接字符串
            close:结束时拼接的字符串
            separator:遍历对象之间需要拼接的字符串
            index:索引(一般不用)
            select * from blog where 1=1 and (id=1 or id=2 or id=3)
        -->
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
@Test
public void testForeach() {
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    List<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids", ids);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    System.out.println(blogs);
    session.close();
}

小结:

十三、缓存(了解)

13.1 简介

13.2 Mybatis 缓存

MyBatis 系统中,默认定义了两级缓存:

缓存清除策略:

13.3 一级缓存

测试:

// 根据id查询用户
User getUserById(@Param("id") int id);
<select id="getUserById" resultType="User">
    select * from user where id = #{id}
</select>
@Test
public void getUser() {
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 第一次查询
    User user1 = mapper.getUserById(1);
    System.out.println(user1);
    // 第二次查询
    User user2 = mapper.getUserById(1);
    System.out.println(user2);
    // 判断两个对象是否相同
    System.out.println(user1 == user2);
    session.close();
}

一级缓存失效的四种情况:

  1. 查询不同的内容;

  2. 增、删、改操作,有可能会改变原来的数据,所以必定会刷新数据;

  3. 查询不同的 Mapper.xml

  4. 手动清理缓存;

    // 手动清理缓存
    session.clearCache();
    

小结:

13.4 二级缓存

工作机制:

使用步骤:

<settings>
    <!--显示的开启全局缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
<!--在当前Mapper.xml中使用二级缓存 60秒刷新-->
<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"
/>
@Test
public void getUser() {
    SqlSession session = MybatisUtils.getSession();
    // 创建另一个session
    SqlSession session2 = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    UserMapper mapper2 = session2.getMapper(UserMapper.class);
    // 第一次查询
    User user1 = mapper.getUserById(1);
    System.out.println(user1);
    session.close();
   
    // 第二次查询
    User user2 = mapper2.getUserById(1);
    System.out.println(user2);
    // 判断两个对象是否相同
    System.out.println(user1 == user2);
    session.close();
}
Cause: java.io.NotSerializableException: 
public class User implements Serializable {
}

小结:

13.5 缓存原理

13.6 自定义缓存 EhCache(了解)

使用步骤:

<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.2</version>
</dependency>
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
        diskStore:为缓存路径;
        ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
        参数解释如下:
            user.home – 用户主目录
            user.dir – 用户当前工作目录
            java.io.tmpdir – 默认临时文件路径
    -->
    <diskStore path="./tmpdir/Tmp_EhCache"/>
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>
    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>    
</ehcache>
上一篇 下一篇

猜你喜欢

热点阅读