Mybatis-学习笔记

Mybatis

一 、简介

  1. 什么是Mybatis

    1. MyBatis 是一款 优秀的持久层框架

    2. 它支持自定义 SQL、存储过程以及高级映射。

    3. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。

    4. MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口、

      和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

  2. 导入Mybatis

二、Mybatis 使用

思路:搭建Maven环境 --> 导入 Mybatis 依赖 --> 编写 Mybatis.xml 文件配置 --> 编写代码 --> 测试

1、搭建Maven及其导入依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    <scope>test</scope>
</dependency>
<!--Mybatis 依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<!--mysql 驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>

2、编写 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",默认使用的环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!--连接数据库的 4 个必填属性-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="970699"/>
            </dataSource>
        </environment>

        <!--第二套环境-->
        <!--
        <environment id="test">
            <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都需要在Mybatis核心配置文件中
    这里需要使用Maven解决资源过滤问题,因为UserMapper.xml不会自动打包,解决办法在pom.xml导入资源导出问题!!
    -->
    <mappers>
        <!--这里必须使用/表示目录,不能用.-->
        <mapper resource="com/wyx/dao/UserMapper.xml"/>
    </mappers>
</configuration>

<!--使用时出现编码,报错,请将IDEA的文件编码改成 UTF-8 -->

3、编写工具类,获取 SqlSession

SqlSession:可以直接执行 SQL 语句。

package com.wyx.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;

import java.io.IOException;
import java.io.InputStream;

//获取 SqlSessionFactory --> sqlSession
public class Mybyatis {
    private static SqlSessionFactory sqlSessionFactory;

    //类一加载就需要获得
    static {
        try {
            //使用Mybatis第一步,获取SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //返回sqlSession对象,,,openSession(true) 这里填写参数后,就会自动提交事务,但是在开发中一般不设置为true
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

4、编写代码

  • 实体类

    package com.wyx.pojo;
    
    public class User {
        private Integer id;
        private String userName;
        private String passWord;
    
        public User() {
        }
    
        public User(Integer id, String userName, String passWord) {
            this.id = id;
            this.userName = userName;
            this.passWord = passWord;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPassWord() {
            return passWord;
        }
    
        public void setPassWord(String passWord) {
            this.passWord = passWord;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", userName='" + userName + '\'' +
                    ", passWord='" + passWord + '\'' +
                    '}';
        }
    }
    
  • Dao接口

    package com.wyx.dao;
    
    import com.wyx.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类,由原来的 UserDaoImpl 转换为 Mapper.xml 配置文件(文件名尽量和接口名相同,但是一般以Mapper结尾)

    <?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.wyx.dao.UserMapper">
    <!--id 对应方法名字,resultType 对应返回值类型-->
        <select id="getUserList" resultType="com.wyx.pojo.User">
    <!--sql 语句,记得表的时候加上数据库名-->
            select * from mybatis.user
        </select>
    </mapper>
    
  
在**mybatis-config.xml** 添加类的映射(**本程序上面已经添加,这里不用复制上去**)
  
  ```xml
  <mappers>
      <mapper resource="UserMapper.xml"/>
  </mappers>
  • 编写测试类

    package com.wyx.dao;
    
    import com.wyx.pojo.User;
    import com.wyx.utils.Mybatis;
    
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void test(){
    //        第一步获取 SqlSession 对象
            SqlSession sqlSession = Mybatis.getSqlSession();
    //        执行SQL
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = userMapper.getUserList();
            for (User user:userList) {
                System.out.println(user);
            }
            
    //        第二种方式,不推荐使用
    //        List<User> userList2 = sqlSession.selectList("com.wyx.dao.UserMapper.getUserList()");
    //        for (User user:userList2) {
    //            System.out.println(user);
    //        }
           sqlSession.close();
        }
    }
    
  • ​ 可能遇见的问题

    • 配置文件没有注册
    • 接口绑定错误
    • 方法名不对
    • 返回值类型不对
    • Maven资源导出问题

三、Maybatis 的 CRUD(增删改查)

第一步注册好后,只需要编写,接口中的方法,然后在对应Mapper.xml中进行配置即可。

namespace: 中的包名要和Dao/Mapper 接口的包名一致(全路径

id: 对应接口的方法名;

resultType: sql语句的返回值类型

parameterType: 参数类型

**编写接口方法 --》 编写Mapper.xml的sql语句 --》编写测试类 **

1、基础使用

  1. select

    package com.wyx.dao;
    import com.wyx.pojo.User;
    public  interface UserMapper {
    //    List<User> getUserList();
        User getUserId(int id);
    }
    

    记得以下标签是放在 这里面

    <select id="getUserId" resultType="com.wyx.pojo.User" parameterType="int">
        select * from mybatis.user where id=#{id}
    </select>
    
    @Test
    public void test1(){
        SqlSession sqlSession = Mybatis.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userId = mapper.getUserId(3);
        System.out.println(userId);
        
        sqlSession.close();
    }
    
  2. Insert

    //在接口中
    int addUser(User user);
    

    记得以下标签是放在 这里面

    <insert id="addUser" parameterType="com.wyx.pojo.User">
        insert into mybatis.user (id,userName,passWord) values (#{id},#{userName},#{passWord})
    </insert>
    
        @Test
        public void addUser(){
            SqlSession sqlSession = Mybatis.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.addUser(new User(4,"网红","000123"));
    
            //提交事务,不然数据库提交不成功
            sqlSession.commit();
            sqlSession.close();
        }
    
  3. update

    int updateUser(User user);
    
    <update id="updateUser" parameterType="com.wyx.pojo.User">
        update mybatis.user
        set  userName= #{userName},passWord = #{passWord}
        where id = #{id};
    </update>
    
    @Test
    public void updateUser(){
        SqlSession sqlSession = Mybatis.getSqlSession();
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.updateUser(new User(2,"张超","145623"));
    
         //提交事务,不然数据库提交不成功
        sqlSession.commit();
        sqlSession.close();
    }
    
  4. delete

    int deleteUser(int id);
    
    <delete id="deleteUser" parameterType="int">
        delete
        from mybatis.user
        where id = #{id};
    </delete>
    
    @Test
    public void deleteUser(){
        SqlSession sqlSession = Mybatis.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.deleteUser(3);
    
        //提交事务,不然数据库提交不成功
        sqlSession.commit();
        sqlSession.close();
    }
    

注意:增删改需要提交事务

sqlSession.commit();

2、使用Map

假设我们写的实体类,或者数据库的表,字段或参数较多,我们使用Map能不用写那么多参数,所以会更好用

map:传递参数,直接在sql取key,

对象传递参数:直接在sql中取对象的属性即可!

只有一个参数,可以直接取。不用加参数类型也可以

多个参数使用Map,或者 注解后面学

int addUser2(Map<String,Object> map);
<insert id="addUser2" parameterType="map">
    insert into mybatis.user (id,passWord) values (#{id},#{pwd})
</insert>
@Test
public void addUser2(){
    SqlSession sqlSession = Mybatis.getSqlSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id",5);
    map.put("pwd","214124");
    mapper.addUser2(map);

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

3、模糊查询

模糊查询怎么写?

  1. 在Java代码中,传递通配符%值%

    List<User> userList = mapper.getUserLike("%李%")
    
  2. 在Sql 语句中使用通配符!

    select * from mybatis.user where id="%"#{id}"%"
    

四、配置解析

配置优化

1、核心配置文件

  • mybatis.xml
  • Mybatis 的配置文件包含了 Mybatis 行为的设置和属性信息。

2、环境配置(environments)

MyBatis 可以配置成适应多种环境,

不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

Mybatis 默认的事务管理器就是JDBC,连接池:POOLED(有池连接)

3、属性(properties)

我们可以通过 properties 属性来实现应用配置文件

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】

编写一个配置文件 db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
username=root
password=970699

在核心配置文件中引入以上资源

<!--
	在 configuration 第一个添加这个标签,property,可以配置用户名和密码,但是优先使用 db.properties 中的,
	使用前请删掉注释,不然会报错
-->
<properties resource="db.properties">
    <property name="userName" value="root"/>
    <property name="password" value="970699"/>
</properties>
<!--
 用了以上的配置,那么配置环境时就可以,使用以下配,
 使用前请删掉注释,不然会报错
-->

<environment id="test">
    <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>

4、类型别名(typeAliases)

别名优化

  • 类型别名可为 Java 类型设置一个缩写名字。
  • 存在的意在降低冗余的全限定类名书写。
  • 第一种方式
<!--
注意书写的位置
 取别名,可以防止写全类名,减少使用全限定名,
 使用前请删掉注释,不然会报错
-->

<typeAliases>
  <typeAlias alias="User" type="com.wyx.pojo.User"/>
</typeAliases>
  • 第二种方式:也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
<!--
	扫描实体包,他的默认别名就是这个类的类名,首字母小写
	使用前请删掉注释,不然会报错
-->
<typeAliases>
  <package name="com.wyx.dao"/>
</typeAliases>

实体类少使用第一种,实体类多,使用第二种

第一种可以自定义别名,第二种不行,如果非要改,可以在实体类上使用注解取别名

@Alias("author")
public class Author {
    ...
}


5、设置

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。



更多设置查看官网,一般不使用。

6、其他设置

7、映射器(mappers)

  • 推荐使用第一种注册方式,
  • 第二种和第三种,必须保证接口名和mapper文件同名,并且需要在同一个包下面

8、生命周期和作用域

生命周期类别是至关重要的,因为错误的使用会导致非常严重的 并发问题。

五、解决属性名和字段名不匹配的问题

1、问题

由于在创建实体类时,因为属性名和数据库中的字段名不一样导致,在执行过程中会查出空值

  • 解决方式简单暴力
<select id="getUserId" resultType="com.wyx.pojo.User" parameterType="int">
    select id,pwd as password from mybatis.user where id=#{id}
</select>

2、resultMap

结果集映射,解决以上问题

<!--
	resultMap="UserMap" 必须对应 id="UserMap" id和resultMap的值必须相同,
	column="id"  对应数据库中的字段,property="id" 对应实体类中的属性名
	字段名和是属性名一样的地方可以不用写 <result column="id" property="id"/>
-->

<resultMap id="UserMap" type="User">
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserList" resultMap="UserMap">
    select * from mybatis.user
</select>
  • resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
  • 如果这个世界总是这么简单就好了。

六、日志

1、日志工厂

如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的选择。

日志工厂:

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

Mybatis中具体使用那一个日志实现,在设置中设定!

  • STDOUT_LOGGING 标准日志输出

配置日志:

<!--不能有空格,名字大小写也不能有问题!-->
<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

2、Log4j

什么是Log4j:

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
  1. 先导入Log4j 的包
<!--Log4j 依赖-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 创建log4j.properyies文件

    #将等级为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/wyx.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}][%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.sq1.PreparedStatement=DEBUG
    
  2. 配置Log4j 为日志实现

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    

3、Log4j 简单使用

  1. 在要使用Log4j 的类中导入包 import org.apache.log4j.Logger;

  2. 日志对象参数为当前的 class

    static Logger logger = Logger.getLogger(UserDaoTest.class);
    
  3. **log4j ** 调试常用级别

    logger.info("-info:进入了 testLog4j");
    logger.debug("-debug:进入了 testLog4j");
    logger.error("-error:进入了 testLog4j");
    

七、数据库分页查询

为什么使用分页?

1、简单分页实现

  • 减少数据的处理量

sql 使用 Limit 分页

# 2 代表重第几个开始,3代表,每页查询几个
select * from user limit 2,3
# 只要一个参数时,代表,重第0个开始,每页查询 3(参数) 个任
select * from user limit 3

java 中使用分页查询,使用 Map 集合来实现,实现过程参考下面

List<User> limitGetUser(Map<String,Integer> map);
<select id="limitGetUser" resultType="User" parameterType="map">
    select * from mybatis.user limit #{Indexparams},#{Lastparams}
</select>
@Test
public void limitGetUser(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("Indexparams",2);
    map.put("Lastparams",3);
    List<User> users = mapper.limitGetUser(map);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

2、RowBounds分页(了解)

List<User> limitGetUserRowBounds();
<select id="limitGetUserRowBounds" resultType="User">
    select * from mybatis.user
</select>
@Test
public void limitGetUserRowBounds(){
    RowBounds rowBounds = new RowBounds(2, 3);
    SqlSession sqlSession = Mybatis.getSqlSession();
    List<User> users = sqlSession.selectList("com.wyx.dao.UserMapper.limitGetUserRowBounds", null, rowBounds);
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

3、分页插件

Mybatis pageHelper 插件使用,详细看官网,很简单:https://pagehelper.github.io/

八、使用注解开发

对于像 BlogMapper 这样的映射器类来说,还有另一种方法来完成语句映射。 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。

使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句

  1. 在接口的方法上添加注解(里面填写Sql语句)
@Select("select * from user")
List<User> getUser();
  1. mybatis-config.xml 中配置映射
<mappers>
    <mapper class="com.wyx.dao.UserMapper"/>
</mappers>
  1. 编写测试类
@Test
public void getUser(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> users = mapper.getUser();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

1、Mybatis 执行流程解析

2、注解的增删改查

  1. 在接口的方法上添加注解(里面填写Sql语句)(其他的增、删、改,使用 对应的注解即可,记得提交事务)
@Select("select * from user where id = #{uid}")
User getUserById(@Param("uid") int id);
  1. mybatis-config.xml 中配置映射
<mappers>
    <mapper class="com.wyx.dao.UserMapper"/>
</mappers>
  1. 编写测试类
@Test
public void getUserById(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User userId = mapper.getUserById(3);
    System.out.println(userId);
    sqlSession.close();
}
  1. 关于@Param()注解

    • 基本引用类型的参数或者String类型的参数,需要加上
    • 引用类型不需要加
    • 如果只有一个基本类型可以忽略,建议加上
    • 在SQL中引用的就是我们这里的 @Param(“uid”) 设置的属性名
  2. 同时在 <mappers>中配置映射时,类路径,和资源路径,必须类路径在前面,如下

    <mappers>
        <mapper class="com.wyx.dao.UserMapper"/>
        <mapper resource="com/wyx/dao/UserMapper.xml"/>
    </mappers>
    
  3. (#{}和${}取值问题)

    (#{}是预编译处理,${}是字符串替换。)
    mybatis处理#{}时,将#{}替换成?,调用PreparedStatement的set方法赋值,这种预编译的机制可以很大程度防止SQL注入;处理${}时,直接进行字符串的替换,无法防止SQL注入。
    (#{}解析后会将String类型的数据自动加上引号,${}不会。例如:)

    select * from student where sname = #{sname}
    select * from student where sname = ‘${value}’
    

九、LomBok

介绍:

Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.

翻译如下

Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加生动有趣。 永远不要再写另一个getter或equals方法,带有一个注释的您的类有一个功能全面的生成器,自动化您的日志记录变量等等。

1、使用步骤

  1. 在IDEA中安装LomBok插件

  2. 在项目中导入 LomBok 的包

    <!--lombok 依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.18</version>
    </dependency>
    
@Getter and @Setter【学习】
@FieldNameConstants
@ToString		【学习】
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor 【学习】
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data  【学习,使用最多:无参构造,get,set,toString,hashCode,equals】
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
  1. 在实体类上加注解
package com.wyx.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

// @Data  【学习,使用最多:无参构造,get,set,toString,hashCode,equals】
// @AllArgsConstructor 所有参数的构造器,默认会去掉 @Data 生成的无参构造,需要的话,还需要添加这个注解 @NoArgsConstructor
// @Getter and @Setter【学习】 加在方法上,对一个字段实现get或set 方法
// @ToString	【学习】
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String userName;
    private String passWord;
}

十、多对一查询结果处理(学生 --> 教师)

测试环境搭建,创建学生表和教师表。让学生表中存在教师表中的主键属性,做外键。

实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String teacher_name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
	//一个学生对应一个老师
    private int id;
    private String student_name;
    private Teacher teacher;

}

1、按照查询结果嵌套

association 多对一

List<Student> getStudent();
<!--
    思路:
        1、查询所有的学生信息
        2、根据查出来的学生tid 寻找对应的老师!!
    -->

<!--结果集映射-->
<resultMap id="StudentTeacher" type="Student">
    <result property="id" column="id"/>
    <result property="student_name" column="student_name"/>
    <!--复杂属性 Teacher 我们单独处理
            对象使用:association
            集合使用:collection
			property="teacher":对应实体类的属性名
 			column="tid":对应数据库的字段名
			javaType="Teacher" 对应的Java类型
			select="getTeacher"	根据查询出来的 tid 然后要跳转查询的方法 id 在下面的地方
        -->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<!--查询方法入口:
	结果集映射在上面
-->
<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>

<!--第一次查询后,需要跳转查询老师的表
	id = #{tid} 这里最好对应跳转查询的 column="tid"一样。
 	但是,不一样也可以,因为mybatis做了优化,连表查询基本就是一个字段连接,所以大多数都能查询成功。
	连表查询多个时必须写清楚,不然会报错
-->
<select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{tid}
</select>

@Test
public void test01(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.getStudent();
    for (Student student : studentList) {
        System.out.println(student);
    }
    sqlSession.close();
}

2、按结果嵌套查询

List<Student> getStudent();
<!--按结果嵌套查询-->

<!--使用别名来设计数据表,与正常的sql语句没有区别-->
<select id="getStudent" resultMap="StudentTeacher">
    select s.id,s.student_name,s.tid,t.teacher_name
    from student s, teacher t
    where s.tid=t.id
</select>

<resultMap id="StudentTeacher" type="Student">
    <result property="id" column="id"/>
    <result property="student_name" column="student_name"/>
    <!--对于复杂类,只需要将复杂类型的Java类型写出来,然后在其标签下,继续为他的属性一一添加放回结果集映射-->
    <association property="teacher" javaType="Teacher">
        <result property="id" column="id"/>
        <result property="teacher_name" column="teacher_name"/>
    </association>
</resultMap>

@Test
public void test01(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    List<Student> studentList = mapper.getStudent();
    for (Student student : studentList) {
        System.out.println(student);
    }
    sqlSession.close();
}

十一、一对多查询结果处理(教师 -->学生 )

搭建环境,和刚才一样,改变实体类即可

collection 一对多

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    
    //一个老师可以有多名学生
    private int id;
    private String teacher_name;
    private List<Student> students;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String student_name;
}

1、按结果嵌套查询

//查寻 id 为 n 的老师下面有多少个学生
List<Teacher> getTeacher(@Param("id") int id);
<select id="getTeacher" resultMap="TeacherStudent">
    select t.id,t.teacher_name,s.tid,s.id,s.student_name
    from  student s,teacher t
    where s.tid = #{tid} and s.tid = t.id
</select>

<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="id"/>
    <result property="teacher_name" column="teacher_name"/>
    <collection property="students" ofType="Student">
        <result property="id" column="id"/>
        <result property="student_name" column="student_name"/>
    </collection>
</resultMap>
@Test
public void test(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    List<Teacher> teachers = mapper.getTeacher(1);
    for (Teacher teacher : teachers) {
        System.out.println(teacher);
    }
    sqlSession.close();
}

2、按照查询结果嵌套

//查寻 id 为 n 的老师下面有多少个学生
List<Teacher> getTeacher(@Param("id") int id);
<select id="getTeacher" resultMap="TeacherStudent">
    select *
    from teacher
    where id = #{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="id"/>
    <result property="teacher_name" column="teacher_name"/>
    <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select *
    from student
    where tid = #{id}
</select>

@Test
public void test(){
    SqlSession sqlSession = Mybatis.getSqlSession();
    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
    List<Teacher> teachers = mapper.getTeacher(2);
    for (Teacher teacher : teachers) {
        System.out.println(teacher);
    }
    sqlSession.close();
}

**小结: **

  • 关联 - association 【多对一】
  • 集合 - collection 【一对多】
  • javaType & ofType
    • javaType 用来指定实体类中的属性的类型
    • ofType 用来指定映射的 List 或者pojo 类型,泛型的约束类型!

十二、动态SQL

动态sql:根据不同的条件生成不同的SQL语句

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(30) not null comment '浏览量'
	)ENGINE=InnoDB DEFAULT CHARSET=utf8

实体类

package com.wyx.pojo;

import lombok.Data;

import java.util.Date;

@Data
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

核心配置

<settings>
    <!--开启数据库对应pojo的驼峰命名-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

Mapper.xml

<?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.wyx.mapper.BlogMapper">
    <insert id="addBlog" parameterType="Blog">
        insert into mybatis.blog (id, title, author, create_time, views) values
        (#{id}, #{title}, #{author}, #{create_time}, #{views});
    </insert>
</mapper>

新建随机生成ID包

package com.wyx.utils;

import org.junit.Test;

import java.util.UUID;

@SuppressWarnings("all")
public class IDUtiles {

    public static String getId(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    @Test
    public void  test(){
        System.out.println(getId());
    }

}

测试类:添加数据

import com.wyx.mapper.BlogMapper;
import com.wyx.pojo.Blog;
import com.wyx.utils.IDUtiles;
import com.wyx.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.Date;

public class MyTest {

    @Test
    public void addBlog(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);

        Blog blog = new Blog();
        blog.setId(IDUtiles.getId());
        blog.setAuthor("houdongun");
        blog.setCreateTime(new Date());
        blog.setViews(999);
        blog.setTitle("first");

        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("second");
        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("third");
        blogMapper.addBlog(blog);

        blog.setId(IDUtiles.getId());
        blog.setTitle("forth");
        blogMapper.addBlog(blog);

        sqlSession.close();
    }
}

2. if

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

test

@Test
public void queryBlogIF(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();

    //        map.put("title", "second");
    map.put("author", "houdongun");

    List<Blog> list = blogMapper.queryBlogIF(map);

    for (Blog blog : list) {
        System.out.println(blog);
    }

    sqlSession.close();
}

3. choose、when、otherwise

<select id="queryBlogchoose" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <choose>
            <when test="title != null">
                title = #{title}
            </when>
            <when test="author != null">
                and author = #{author}
            </when>
            <otherwise>
                and views = #{views}
            </otherwise>
        </choose>
    </where>
</select>

4. trim、where、set

<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

trim 可以自定义

SQL片段

有些时候我们有一些公共部分

  1. 使用sql便签抽取公共部分
  2. 在使用的地方使用include标签
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIF" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

注意:

  • 最好基于单表
  • sql里不要存在where标签

5. for-each

<!--ids是传的,#{id}是遍历的-->
<select id="queryBlogForeach" parameterType="map" resultType="Blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and ("
                 close=")" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>

test

@Test
public void queryBlogForeach(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
    Map map = new HashMap();

    ArrayList<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(3);
    map.put("ids",ids);

    List<Blog> list = blogMapper.queryBlogForeach(map);

    for (Blog blog : list) {
        System.out.println(blog);
    }

    sqlSession.close();
}

十三、缓存(了解)

1、一级缓存

  1. 开启日志
  2. 测试一个session中查询两次相同记录。

缓存失效:

  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 查询不同的mapper.xml
  • 手动清除缓存

一级缓存默认开启,只在一次sqlseesion中有效

2. 二级缓存

  1. 开启全局缓存
<setting name="cacheEnabled" value="true"/>
  1. 在当前mapper.xml中使用二级缓存
<cache eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>

test

@Test
public void test(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.queryUserByid(1);
    System.out.println(user);
    sqlSession.close();

    UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = userMapper1.queryUserByid(1);
    System.out.println(user1);
    System.out.println(user==user1);
    sqlSession1.close();
}

只用cache时加序列化

<cache/>

实体类

package com.wyx.pojo;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    private int id;
    private String name;
    private String pwd;

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }
}

小结:

  • 只有开启了二级缓存,在Mapper下有效
  • 所有数据都会先放在一级缓存
  • 只有当回话提交,或者关闭的时候,才会提交到二级缓存

3. 自定义缓存-ehcache

<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.2.0</version>
</dependency>

ehcache.xml

<?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="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <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>



posted @ 2021-03-13 08:49  橘子有点甜  阅读(71)  评论(0编辑  收藏  举报