mybatis

1 简介

1.1什么是Mybatis

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

  • 支持定制SQL、存储过程以及高级映射

  • Mybatis避免了几乎所有的JDBC代码和手动设置参树,以及获取结果集

  • Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录

  • MyBatis本是apache的一个开源项目iBatis

  • 2010年这个项目由apache software foundation迁移到了谷歌代码,并且改名为MyBatis

  • 2013年11月迁移到Github

    如何获得Mybatis?

  • `Maven仓库:下载地址

org.mybatismybatis3.5.2
  • Github:Mybatis地址
  • 中文文档地址

1.2 持久化

数据持久化

  • 持久化就是将程序的数据保存在硬盘中

  • 内存特性:断电会失去信息

  • 数据库(jdbc),io文件持久化

  • 生活中的持久化:冷藏,写…

为什么需要持久化?

  • 我们需要将一些数据保存下来,方便以后使用
  • 内存价格过高(制造成本高)

1.3 持久层

复习学习过的层:Dao,Service,Controller

持久层的工作

  • 完成持久化工作的代码块
  • 层之间的接线非常明显

1.4 为什么需要MyBatis?

  • 帮助程序员将数据存入到数据库中
  • 传统的JDBC代码太复杂了
  • 简化、框架、自动化
  • 不用Mybatis也能实现,Mybatis可以使程序员在不知道底层原理的情况下,完成网站后台搭建
  • 优点:
    • ​ 简单易学
    • ​ 灵活
    • ​ 解除sql与程序代码的耦合
    • ​ 提供映射标签,支持对象与数据库的orm字段关系映射
    • ​ 提供对象关系映射标签,支持对象关系组建维护
    • ​ 提供xml标签,支持编写动态sql。

2 第一个MyBatis程序

思路:搭建环境–>导入Mybatis–>编写代码–>测试

2.1 搭建环境

搭建mysql

新建项目

1.新建一个maven项目

2.删除src目录

3.导入maven依赖

  ```xml
  <dependencies>
          <!--mysql驱动-->
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>8.0.30</version>
          </dependency>
          <!--mybatis-->
          <dependency>
              <groupId>org.mybatis</groupId>
              <artifactId>mybatis</artifactId>
              <version>3.5.6</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
          </dependency>
  
      </dependencies>
       <!-- 在build中配置resources 来防止我们资源导出失败问题--><build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>

2.2 创建一个模块

  • 编写mybatis核心配置文件

    <?xml version="1.0" encoding="utf-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "https://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/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="0414"/>
                </dataSource>
            </environment>
        </environments>
    <!--    每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
        <mappers>
            <mapper resource="com/lei/dao/UserMapper.xml"/>
        </mappers>
    </configuration>
    
    
  • 编写mybatis工具类

    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            try {
                String resource = "mybatis-config.xml";
                InputStream inputStream = Resources.getResourceAsStream(resource);
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
        //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
        // SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    
    
    }
    
    

2.3 编写代码

  • 实体类

    public class User {
        private int id;
        private String name;
        private String pwd;
    
        public User() {
        }
    
        public User(int id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int 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;
        }
    }
    
    
  • dao接口

    public interface UserDao {
        List<User> getUserList();
    }
    
  • 接口实现类由原来的userDaoImp转变为配置一个Mapper配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace=绑定一个对应的mapper接口-->
    <mapper namespace="com.lei.dao.UserDao">
    <!--    select查询语句-->
        <select id="getUserList" resultType="com.lei.pojo.User">
            select * from mybatis.user
        </select>
    </mapper>
    

2.4 测试

@Test
public void test(){
    //第一步 获得 sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //方式1执行sql
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    List<User> userList = mapper.getUserList();
    for (User user:userList){
        System.out.println(user);
    }
    sqlSession.close();
}

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2xn3A4p-1667903639478)(C:\Users\lll\AppData\Roaming\Typora\typora-user-images\image-20221023233628594.png)]

3 CRUD

1.namespace

namespace中的包1名要和Dao/mapper接口的包名一致

2.select

选择,查询语句

  • id:就是对应namespace中的方法名
  • reuItType:Sql语句执行的返回值
  • parameterTypeparameterType 参数类型

1.编写接口

//查询全部用户
List<User> getUserList();

//根据id查询
User getUserById(int id);

2.编写对应的mapper中的sql语句

<!--    select查询语句-->
    <select id="getUserList" resultType="com.lei.pojo.User" >
        select * from mybatis.user
    </select>

<select id="getUserById"  parameterType="int" resultType="com.lei.pojo.User ">
       SELECT *FROM mybatis.user WHERE id=#{id}
   </select>
       
       

3测试

public void test(){
    //第一步 获得 sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //方式1执行sql
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    for (User user:userList){
        System.out.println(user.toString());
    }
    sqlSession.close();
}
@Test
public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User userById = mapper.getUserById(1);
    System.out.println(userById);

    sqlSession.close();
}

3.Insert

<insert id="addUser" parameterType="com.lei.pojo.User" >
    insert into mybatis.user (id, name, pwd) VALUES (#{id},#{name},#{pwd})
</insert>

4.update

<update id="updateUser" parameterType="com.lei.pojo.User">
    update mybatis.user set name = #{name},pwd=#{pwd} where id = #{id};
</update>

5.delete

<delete id="deleteUser" parameterType="int">
    delete  from mybatis.user WHERE id=#{id};
</delete>
public void getUserLike(){
    //第一步 获得 sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //方式1执行sql
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    java.util.List<com.lei.pojo.User> userList = mapper.getUserLike("%李%");
    for (User user:userList){
        System.out.println(user);
    }
    sqlSession.close();
}
@Test
public void test(){
    //第一步 获得 sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //方式1执行sql
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> userList = mapper.getUserList();
    for (User user:userList){
        System.out.println(user.toString());
    }
    sqlSession.close();
}
@Test
public void getUserById(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User userById = mapper.getUserById(1);
    System.out.println(userById);

    sqlSession.close();
}



//增删改需要提交事务
@Test
public void addUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    int hh = mapper.addUser(new User(4, "ccc", "12345"));
    if (hh>0){
        System.out.println("插入成功");
    }
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}
public void addUser2(){
    org.apache.ibatis.session.SqlSession sqlSession = com.lei.utils.MybatisUtils.getSqlSession();
    com.lei.dao.UserMapper mapper = sqlSession.getMapper(com.lei.dao.UserMapper.class);

    java.util.HashMap<String, Object> map = new java.util.HashMap<String, Object>();
    map.put("lid",5);
    map.put("lname","大小");
    map.put("lid","125");


    mapper.addUser2(map);

}

@Test
public void upadteUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.updateUser(new User(4,"哈哈","123123"));
    sqlSession.commit();
    sqlSession.close();
}
@Test
public void deleteUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
   mapper.deleteUser(4);
    sqlSession.commit();
    sqlSession.close();
}

注意:

  • 增删改需要提交事务 sqlSession.commit();

6分析错误

  • 标签不要匹配错误
  • resource 绑定 mapper 需要使用路径
  • 程序配置文件必须符合规范

7万能的mapper方法

我们的实体类,或数据库中的表,字段或参数过多,我们应该考虑map

//万能的map
User addUser2(java.util.Map<String , Object> map) ;
<!--    对象之中的,可以直接取出来 传递map的key-->
    <insert id="addUser2" parameterType="map">
        insert into mybatis.user(id, name, pwd) values (#{lid},#{lname},#{lpwd})
    </insert>
public void addUser2(){
    org.apache.ibatis.session.SqlSession sqlSession = com.lei.utils.MybatisUtils.getSqlSession();
    com.lei.dao.UserMapper mapper = sqlSession.getMapper(com.lei.dao.UserMapper.class);

    java.util.HashMap<String, Object> map = new java.util.HashMap<String, Object>();
    map.put("lid",5);
    map.put("lname","大小");
    map.put("lid","125");

8模糊查询

1.java代码执行的时候,传递通配符% %

List<com.lei.pojo.User> userList = mapper.getUserLike("%李%");

接口

List<User> getUserLike(String value);


sql语句

<select id="getUserLike" resultType="com.lei.pojo.User">
    select * from mybatis.user u where name like #{value}
</select>

测试

public void getUserLike(){
    //第一步 获得 sqlSession对象
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //方式1执行sql
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    java.util.List<com.lei.pojo.User> userList = mapper.getUserLike("%李%");
    for (User user:userList){
        System.out.println(user);
    }
    sqlSession.close();
}

结果

查询结果
User{id=3, name='李四', pwd='1234'}
User{id=4, name='李五', pwd='147'}

4.配置解析

1.核心配置文件

  • mybatis-config.xml
  • mybatis 的配置文件影响mybatis 行为的设置和属性信息
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)

2.环境配置(environments)

MyBatis 可以配置成适应多种环境但每个 SqlSessionFactory 实例只能选择一种环境。

学会使用配置多套运行环境

mybatis默认的事务管理器就是JDBC 连接池:POOLED

3.属性(properties)

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

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

编写一个配置文件

dp.properties

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

在核心配置文件引入

<!--    引入配置文件-->
      <properties resource="dp.properties">
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </properties>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件

4.类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。

  • 存在意义在于降低冗余的全限定类名书写

    <!--    起别名-->
        <typeAliases>
            <typeAlias type="com.lei.pojo.User" alias="User"/>
        </typeAliases>
    

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

扫描实体类的包,它的默认别名就为这个类的类名,首字母小写

<typeAliases>
    <package name="com.lei.pojo.User"/>
</typeAliases>

在实体类比较少的时候,使用第一种

在实体类比较多的时候,使用第二种

5.设置(settings)

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

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

6.映射器(mappers)

<!--    每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="com/lei/dao/UserMapper.xml"/>
    </mappers>

5 解决属性名和字段名不一致

5.1 新建项目

  • 复制原文件到对应目录下,修改属性(ctrl+r)pwd->password
    img

  • 使用测试类测试
    img

  • 用户映射器.xml

      <mapper namespace="com.yu.dao.UserMapper">   
          <select id="getUserById" parameterType="int" resultType="user">                       from mybatis.user where id=#{id}      
              <!--  这句代码的本质:select id,name,pwd from ...类型处理器找不到对应字段的属性,无法赋值 -->     
          </select>  
    </mapper>
    

5.2 解决方法

  • 起别名

      select id,name,pwd as password from mybatis.user where id=#{id}
    
  • 使用resultMap
    结果Map:结果集映射

      <resultMap id="UserMap" type="user"> 
          <result column="id" property="id"/>    
          <result column="name" property="name"/> 
          <result column="pwd" property="password"/> 
    </resultMap> 
    <select id="getUserById" parameterType="int" resultMap="UserMap">  
        select * from mybatis.user where id=#{id} 
    </select>
    

    ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

    ResultMap 的优秀之处——你完全可以不用显式地配置它们。虽然上面的例子不用显式配置 ResultMap。但为了讲解,我们来看看如果在刚刚的示例中,显式使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。

6 日志

6.1日志工程

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

曾经:sout,debug

现在:日志工厂

logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J(3.5.9 起废弃) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
  • SLF4J
  • LOG4J 掌握 (3.5.9 起废弃
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 掌握
  • NO_LOGGING

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

STDOUT_LOGGING 标准日志输出

<!--    日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
Opening JDBC Connection
Created connection 1403704789.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@53aad5d5]
==>  Preparing: select * from mybatis.user u where name like ?
==> Parameters: %%(String)
<==    Columns: id, name, pwd
<==        Row: 3, 李四, 1234
<==        Row: 4, 李五, 147
<==      Total: 2
User{id=3, name='李四', password='null'}
User{id=4, name='李五', password='null'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@53aad5d5]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@53aad5d5]
Returned connection 1403704789 to pool.

7 分页

使用理由 :减少数据的处理量

使用Limit 分页

select *from user u limit 0,2;

使用Mybatis实现分页 ,核心sql

  • mybatis的sql语句如果有多个参数,需要用map封装

  • 接口 用户映射器.java

      List<User> selectLimit(Map<String,Integer> map);
    
  • 映射器.xml

      <select id="selectLimit" parameterType="map" resultMap="UserMap">  
          select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
    
  • 测试

      public class UserDaoTest {   
          @Test    
          public void limitTest(){      
              SqlSession sqlSession = MybatisUtils.getSqlSession();      
              UserMapper mapper = sqlSession.getMapper(UserMapper.class);                         Map<String, Integer> map = new HashMap<String, Integer>();                         map.put("startIndex",0);     
              map.put("pageSize",2);     
              List<User> list=mapper.selectLimit(map); 
              for (User u:list) {  
                  System.out.println(u);     
              }          sqlSession.close();   
          }  
      }
    

8 使用注解开发

注解的本质是使用反射,底层是代理模式(见设计模式)

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

8.1 注解查找

  • 接口

      @Select("select * from mybatis.user") 
       List<User> selectAll();
    
  • 注册绑定

      <mappers>     
          <mapper class="com.yu.dao.UserMapper"/>  
    </mappers>
    
  • 测试

      @Test  public void selectAll(){   
          SqlSession sqlSession = MybatisUtils.getSqlSession();      //底层主要应用反射      UserMapper mapper = sqlSession.getMapper(UserMapper.class);  
          List<User> list=mapper.selectAll();   
          for (User user : list) {     
              System.out.println(user);   
          }      sqlSession.close(); 
      }
    

8.2 注解CRUD

  • 设置自动提交

      public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); }
    
  • 映射

      //多个参数情况下,有两种解决方式,一个map封装,另一种是注解
      Param  @Select("select * from mybatis.user where id=#{id}") 
          User selectUserById(@Param("id") int id); 
    @Select("select * from mybatis.user") 
    List<User> selectAll(); 
    @Insert("insert into mybatis.user() values(#{id},#{name},#{password}) ") 
    boolean insertUser(User u); 
    @Update("update user set name=#{name},pwd=#{password} where id = #{id}") 
    boolean updateUser(User u); 
    @Delete("delete from mybatis.user where id=#{id}") 
    boolean deleteUser(@Param("id") int id);
    
  • 测试

      @Test  public void selectAll(){    
          SqlSession sqlSession = MybatisUtils.getSqlSession();      //底层主要应用反射      UserMapper mapper = sqlSession.getMapper(UserMapper.class);//     
          List<User> list=mapper.selectAll();//    
          for (User user : list) {//     
              System.out.println(user);//        }/**    
              User u=mapper.selectUserById(1);    
              System.out.println(u);*///        
                  boolean isInserted=mapper.insertUser(new User(4,"图拉真","dgsdgs"));//  
                  if (mapper.updateUser(new User(6,"寒江雪",null)))  
                      if (mapper.deleteUser(6))  
                          for (User user : mapper.selectAll()) { 
                              System.out.println(user);      
                          }    
              sqlSession.close(); 
          }
    

8.3 @Param注解

  • 基本类型的参数和String类型,需要加上这个注解
  • 引用类型不需要加
  • 如果只有一个基本类型的参数,可以省略
  • 我们在sql中引用的就是@Param(“xxx”)中设定的属性名

9 Lombok

使用步骤:

1.在idea中安装Lombok

2.在项目中导入lombok的包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope>
</dependency>

3.在实体类写

@Data:无参构造器,get,set,tostring,hashcode,equals
@AllArgsConstructor 有参构造器
@NoArgsConstructor   无参构造器
    

10 多对一处理

  • 多个学生对应一个老师
  • 学生关联老师,多对一
  • 老师管理集合,一对多

​ 多对一 关联

​ 一对多 集合

搭建sql语句


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

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

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT 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.1 测试环境搭建

  1. 新建教师,Student实体类

    public class Student {
        private int id;
        private String name;
    
        public Student() {
    
        }
    
        //学生需要关联一个老师
        private Teacher teacher;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public com.lei.pojo.Teacher getTeacher() {
            return teacher;
        }
    
        public void setTeacher(com.lei.pojo.Teacher teacher) {
            this.teacher = teacher;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", teacher=" + teacher +
                    '}';
        }
    }
    
    public class Teacher {
        private int id;
        private String name;
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  2. 新建Mapper接口

public interface StudentMapper {
    //查询所以的学生信息,以及对应的老师的信息
    public List<Student> getStudent();
    public List<Student> getStudent2();

}
public interface TeacherMapper {
    @Select("select *from teacher where id =#{tid}")
    Teacher getTeacher(@Param("tid") int id);
}
  1. 在resources新建com->lei->dao文件夹
  2. 新建Mapper.xml文件
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration 核心配置文件-->
<mapper namespace="com.lei.dao.StudentMapper">

<!--    按照结果嵌套处理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname from mybatis.student s,mybatis.teacher t where s.tid=t.id;
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>


<!--    思路
            1.查询所以的学生信息
            2.根据查询处理的学生的tid,寻找对应的老师
            3.
            -->
 <select id="getStudent" resultMap="StudentTeacher" >
     select * from mybatis.student s,mybatis.teacher t;
 </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--        复杂的属性需要单独处理  对象:association 集合:collection-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher t where id = #{id};
    </select>
</mapper>
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration 核心配置文件-->
<mapper namespace="com.lei.dao.TeacherMapper">


</mapper>
  1. 在mybatis-config.xml中注册绑定Mapper.xml
  2. 在TeacherMapper接口中创建selectAll()方法
  3. 在TeacherMapper.xml中写对应的查询
  4. 新建测试类,在测试类中测试使用
import com.lei.dao.StudentMapper;
import com.lei.dao.TeacherMapper;
import com.lei.pojo.Student;
import com.lei.pojo.Teacher;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

import static com.lei.utlis.MybatisUtils.*;

/**
 * @author lei
 * @verson:1.8
 */
public class MyTest {
    @org.junit.Test
    public void text(){
        SqlSession sqlSession = getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
        sqlSession.close();
    }
    @org.junit.Test
    public void text1(){
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> student = mapper.getStudent();
        for (Student student1:student) {
            System.out.println(student1);
        }

        sqlSession.close();
    }

    @org.junit.Test
    public void text2(){
        SqlSession sqlSession = getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> student = mapper.getStudent2();
        for (Student student1:student){
            System.out.println(student1);
        }

        sqlSession.close();
    }
}

10.2 按照查询嵌套处理

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration 核心配置文件-->
<mapper namespace="com.lei.dao.StudentMapper">

<!--    思路
            1.查询所以的学生信息
            2.根据查询处理的学生的tid,寻找对应的老师
            3.
            -->
 <select id="getStudent" resultMap="StudentMapper" >
     select * from mybatis.student s,mybatis.teacher t;
 </select>
    <resultMap id="StudentMapper" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--        复杂的属性需要单独处理  对象:association 集合:collection-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from mybatis.teacher t where id = #{id};
    </select>
</mapper>

10.3 按照结果嵌套处理

<!--    按照结果嵌套处理-->
    <select id="getStudent2" resultMap="StudentTeacher2">
        select s.id sid,s.name sname,t.name tname from mybatis.student s,mybatis.teacher t where s.tid=t.id;
    </select>
    <resultMap id="StudentTeacher2" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
        </association>
    </resultMap>

11 一对多处理

实体类

public class Teacher {
    private int id;
    private String name;

    private List<Student> students;

    public Teacher() {
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", students=" + students +
                '}';
    }
}
public class Student {
    private int id;
    private String name;
    private int tid;
    public Student() {

    }
    }

按照结果嵌套处理

<!--按结果查询-->
    <select id="getTeacher" resultMap="sTeacher">
        select s.id sid,s.name sname,t.name tname ,t.id  tid
        from mybatis.student s,mybatis.teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>
<resultMap id="sTeacher" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--        复杂的属性需要单独处理  对象:association 集合:collection
            javaType=“”指定属性的类型
            集合中的泛型信息,我们需要使用 ofType获取
        -->
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>

    </collection>
</resultMap>

小结

1.关联 association 多对一

2.集合 collection 一对多

3.javaType & ofType

1.javaType 用来指定实体类中属性的类型

2.ofType 用来指定映射到List或集合中的pojo类型,泛型中的约束类型

注意点:

  • 保证sql语句的可读性
  • 注意一对多和多对一属性和字段的问题
  • 面试要点
    • Mysql 引擎
    • InnoDB底层原理
    • 索引
    • 索引优化

12 动态sql

什么是动态SQL?

动态SQL是指根据不同的条件生成不同的SQL语句

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

- if
- choose(when,otherwise)
- trim(where,set)
- foreach

搭建环境

  id varchar(50) not null comment '博客id',
  title varchar(100) not null comment '博客标题',
  author varchar(30) not null comment '博客作者',
  ctreate_time datetime not null comment '创建时间',
  views int not null comment '浏览量'
  )engine=InnoDB default charset=utf8;

1.if

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

2.choose(when,otherwise)选择

otherwise 否则

<select id="queryBlogChoose" resultType="Blog" parameterType="map">
    select * from mybatis.blog b
    <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>

3.trim(where,set)

  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
  • 用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,
<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title!=null">
            title=#{title},
        </if>
        <if test="author!=null">
            title=#{author}
        </if>
    </set>
    where id=#{id}

</update>
  • 所谓动态sql,本质还是SQL语句,只是我们可以在SQL层面,执行逻辑代码

4.SQL片段

  • 有的时候,我们可以将一些功能的部分抽取出来(类似函数的封装),方便复用

1.使用sql抽出公共部分

<sql id="if-title-author">
    <if test="title!=null">
        title=#{title}
    </if>
    <if test="author!=null">
        and author=#{author}
    </if> 
</sql>

2.在需要的地方使用include标签引入

  •   <select id="queryBlogIf" parameterType="map" resultType="Blog">
            select *
            from mybatis.blog b
            <where>
            <include refid="if-title-author"></include>
           </where>
        </select>
    
    • 注意事项:
      • 最好基于单表定义SQL片段
      • 不要存在where标签

5.foreach

  • 动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

      <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT *    FROM POST P    WHERE ID in  
          <foreach item="item" index="index" collection="list"   
                   open="(" separator="," close=")">    
              #{item}   
          </foreach> 
    </select>
    
      -- 查询id=1,2,3的用户
      select * from  user where 1=1 and 
      <foreach item="id" collection="ids"
          open="(" separator="or" close=")">
          #{id}
      </foreach>
      (id=1 or id=2 or id=3)
    

    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from mybatis.blog b
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id=#{id}
            </foreach>
        </where>
    </select>
    

    测试

@org.junit.Test
public void queryBlogForeach() {
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap blogHashMap = new HashMap<>();
    java.util.ArrayList<Integer> ids = new java.util.ArrayList<>();
    ids.add(1);
    ids.add(2);
    blogHashMap.put("ids",ids);
    List<Blog> blogs = mapper.queryBlogForeach(blogHashMap);
    for (Blog blog:blogs){
        System.out.println(blog);
    }
}

测试结果

PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 926434463.
==>  Preparing: select * from mybatis.blog b WHERE ( id=? or id=? or id=? )
==> Parameters: 1(Integer), 2(Integer), 3(Integer)
<==    Columns: id, title, author, ctreate_time, views
<==        Row: 1, MYBATIS如此的难, 1, 2022-10-29 19:06:40, 9999
<==        Row: 2, Spring如此的难, 1, 2022-10-29 19:06:40, 9999
<==      Total: 2
Blog{id=1, title='MYBATIS如此的难', author='1', createTime=null, Views=9999}
Blog{id=2, title='Spring如此的难', author='1', createTime=null, Views=9999}

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式去排列组合

13 缓存

13.1 简介

  • 为什么要使用缓存

    每次查询都要连接数据库,比较耗资源,我们把查询到的数据暂存到内存里面,下次查询的时候,从内存读取, 这个地方就叫缓存。

  • 什么样的数据适用于缓存?

    经常查询且不经常改变的数据

  • Mybatis系统默认定义了两级缓存

    • 默认情况下,只有一级缓存开启(SqlSession缓存,也称为本地缓存)
    • 二级缓存需要手动配置,它是基于命名空间级别的缓存
    • Mybatis定义了缓存接口Cache,可以通过实现Cache接口来自定义二级缓存

13.2一级缓存

  • 测试步骤
    1. 开启日志
    2. 测试在一个Session中查询两次的计量局
    3. 查看日志输出

img

缓存失效的情况

  1. 查询不同的Mapper.xml
  2. 增删改
  3. 查询不同的东西
  4. 手动清理缓存(sqlSession.clearCache()

13.4 二级缓存

  • MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。

  • 默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

      <cache/>
    
  • 基本上就是这样。这个简单语句的效果如下:

      映射语句文件中的所有 select 语句的结果将会被缓存。  映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。  缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。  缓存不会定时进行刷新(也就是说,没有刷新间隔)。  缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。  缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。  提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
    
  • 这些属性可以通过 cache 元素的属性来修改。比如:

      <cache    eviction="FIFO"    flushInterval="60000"    size="512"    readOnly="true"/>
    
  • 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

  • 使用二级缓存

    1. 开启全局缓存

      <!-- 虽然默认开启,但是写上可以让看代码的人明白 --><setting name="cacheEnabled" value="true"/>
      
    2. 在要使用二级缓存的Mapper.xml中,写标签

      <cacheeviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>
      
    3. 测试使用

      @Testpublic void getUserById2(){ 
          SqlSession sqlSession = MybatisUtils.getSqlSession(); 
          SqlSession sqlSession2 = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class); 
          UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class); 
          User u=mapper.getUserById(1);
          System.out.println(u); 
          sqlSession.close();
          System.out.println("============");
          User user = mapper2.getUserById(1); 
          System.out.println(user==u);
          sqlSession2.close();}
      

img

  • 问题
    • 我们需要实体类序列化,否则会抛出异常
  • 小结
    • 二级缓存在同一个Mapper下有效
    • 所有的数据都会先放在一级缓存中
    • 当会话提交或者关闭,数据会被转存到二级缓存中

13.5 缓存原理

  • 图片演示
    img

13.6 自定义缓存 ehcache

  • 简介

    EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。

  • 使用

    1. 导包

      <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --><dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.2</version></dependency>
      
    2. 写入配置文件(resources->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"><!-- 磁盘缓存位置 --><diskStore path="java.io.tmpdir/ehcache"/><!-- 默认缓存 --><defaultCache       maxEntriesLocalHeap="10000"       eternal="false"       timeToIdleSeconds="120"       timeToLiveSeconds="120"       maxEntriesLocalDisk="10000000"       diskExpiryThreadIntervalSeconds="120"       memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/></defaultCache><!-- helloworld缓存 --><cache name="HelloWorldCache"      maxElementsInMemory="1000"      eternal="false"      timeToIdleSeconds="5"      timeToLiveSeconds="5"      overflowToDisk="false"      memoryStoreEvictionPolicy="LRU"/></ehcache>
      
    3. 在Mapper中指定

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      
    4. 测试使用(用之前的代码即可)

  • 自定义缓存

    只要实现了org.apache.ibatis.cache.Cache接口,就能定义自己的缓存,但是实现比较复杂,只需要会使用就行,ehcache是继承了AbstractEhcacheCache,该类已经实现了Cache接口。

      public class MyCache implements Cache {   
          @Override   
          public String getId() 
          {          return null;      }   
          @Override     
          public void putObject(Object key, Object value) {  
          }     
          @Override     
          public Object getObject(Object key) {    
              return null;    
          }      
          @Override 
          public Object removeObject(Object key) {    
              return null;   
          }      
          @Override  
         
          public void clear() {   
          }   
          @Override 
          public int getSize() {     
              return 0;  
          }  
      }
    
  • 实际开发中使用的缓存

    • 在实际开发中,我们更多的使用Redis来做缓存
      tp://ehcache.org/ehcache.xsd">
      
      
    1. 在Mapper中指定

      <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
      
    2. 测试使用(用之前的代码即可)

  • 自定义缓存

    只要实现了org.apache.ibatis.cache.Cache接口,就能定义自己的缓存,但是实现比较复杂,只需要会使用就行,ehcache是继承了AbstractEhcacheCache,该类已经实现了Cache接口。

      public class MyCache implements Cache {   
          @Override   
          public String getId() 
          {          return null;      }   
          @Override     
          public void putObject(Object key, Object value) {  
          }     
          @Override     
          public Object getObject(Object key) {    
              return null;    
          }      
          @Override 
          public Object removeObject(Object key) {    
              return null;   
          }      
          @Override  
         
          public void clear() {   
          }   
          @Override 
          public int getSize() {     
              return 0;  
          }  
      }
    
  • 实际开发中使用的缓存

    • 在实际开发中,我们更多的使用Redis来做缓存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值