Mybatis

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持定制化 SQL、存储过程以及高级映射。
  • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
  • **MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 **

如何获取Myabtis

  • Maven仓库

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    
    
  • Github

持久化

数据持久化

  • 持久化就是将程序的数据在持久状态和瞬时状态转化的过程(将不能长久存放的数据持久到数据库中)

持久层

  • 完成持久化工作的代码块(Dao层)

第一个Mybatis程序

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

  1. 搭建环境

    • 搭建数据库

      CREATE DATABASE Mybatis;
      use Mybatis;
      create table user(
      id int not null PRIMARY key,
      `NAME` varchar(30) ,
      pwd varchar(30)
      )ENGINE=innodb default charset=utf8;
      
      insert into user values(1,'张三','123456'),(2,'李四','123456'),(3,'王五','123456');
      
    • 新建项目

      1. 新建一个普通的Maven项目

      2. 删除Src文件

      3. 导入Maven依赖

      4. <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.9</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        
  2. 创建一个模块(普通的Maven项目 之前普通Maven项目的子类)

    • 编写Mybatis工具类

      package com.dyz.utils;
      
      import org.apache.ibatis.io.Resources;
      import org.apache.ibatis.session.SqlSession;
      import org.apache.ibatis.session.SqlSessionFactory;
      import org.apache.ibatis.session.SqlSessionFactoryBuilder;
      
      import java.io.IOException;
      import java.io.InputStream;
      //sqlSessionFactory--->sqlSession
      public class MybatisUtils {
          private static SqlSessionFactory sqlSessionFactory;
          //使用Mybatis第一步,获取SqlSessionFactory对象
          static{
              String resource = "mybatis-config.xml";
              InputStream inputStream = null;
              try {	
                  inputStream = Resources.getResourceAsStream(resource);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
          }
          /*
          既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
          SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession
          实例来直接执行已映射的 SQL 语句。
           */
          public static SqlSession getSqlSession(){
          //    SqlSession sqlSession = sqlSessionFactory.openSession();
          //    return sqlSession;
             return  sqlSessionFactory.openSession();
          }
      }
      
    • 编写Mybatis核心配置文件

      <?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.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="123456"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="com/dyz/dao/UserMapper.xml"/>
          </mappers>
      </configuration>
      
  3. 编写代码

    • 实体类

      package com.dyz.pojo;
      
      public class User {
          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;
          }
      
          public User() {
          }
      
          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;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      ", pwd='" + pwd + '\'' +
                      '}';
          }
      }
      
    • Dao接口

      package com.dyz.dao;
      
      import com.dyz.pojo.User;
      
      import java.util.List;
      
      public interface UserDao {
      List<User> getUserList();
      }
      
    • 接口实现类由原来的UserDaoImpl转变为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接口-->
      <mapper namespace="com.dyz.dao.UserDao">
      <!--id就相当于原来重写接口方法的java类 有了框架以后该类可以省略-->
      <!--resultType是Sql返回的类型,必须精确到该类型的位置-->
          <select id="getUserList" resultType="com.dyz.pojo.User">
              select *from user ;
          </select>
      </mapper>
      
    1. 测试

      问题1:1 字节的 UTF-8 序列的字节 1 无效。

      由于版本问题可将将xml文件中UTF-8改为UTF8;

      问题2:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LrI9b2hb-1659085170241)(D:\桌面\javaPicture\1650087176143.png)]

​ Mapper没有在Mybatis-config.xml文件中注册

如果注册了仍然显示error may exist in…说明Mybatis-config.xml无法导出(缓存target中不存在)需要在pom.xml(父子pox.xml中都要添加并且刷新maven)中添加

<!-- 在bulid中配置resource 来防止资源导出失败的问题  并不是每个人都会遇到 遇到时可以使用-->
<build>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
    </resource>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.properties</include>
        <include>**/*.xml</include>
      </includes>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>
  • junit测试

    package com.dyz.dao;
    
    import com.dyz.pojo.User;
    import com.dyz.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void Test(){
            //获取SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            //getMapper 获取接口的实例对象mapper;
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            /*执行该方法  该方法已经在UserMapper.xml中实现
            而UserMapper.xml也与UserDao接口进行了绑定
            因此直接mapper.方法即可执行
             */
            List<User> userList = mapper.getUserList();
            for(User user:userList){
                System.out.println(user);
            }
            sqlSession.close();
    
        }
    }
    

CRUD

namespace

namespace绑定的包名要和Mapper/Dao接口名一致

  1. id:对应namespace中的方法名
  2. resultType:Sql语句执行的返回值!增删改都没有
  3. parameterType:参数列表的参数类型
  • 编写接口

    package com.dyz.dao;
    
    import com.dyz.pojo.User;
    
    import java.util.List;
    
    public interface UserDao {
        //查询全部用户
        List<User> getUserList();
        //根据id查用户
        User getUserById( int id);
        //增加一个用户
        int addUser(User user);
        //修改用户
        int updateUser(User user);
        //删除用户
        int deleteUser(int id);
    }
    
    
  • 编写对应mapper中的sql语句

    <?xml version="1.0" encoding="UTF8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace绑定一个对应的Mapper接口-->
    <mapper namespace="com.dyz.dao.UserDao">
    <!--id就相当于原来重写接口方法的java类 有了框架以后该类可以省略-->
    <!--resultType是Sql返回的类型,必须精确到该类型的位置-->
        <select id="getUserList" resultType="com.dyz.pojo.User">
            select *from user
        </select>
    <!--    根据id查用户 parameterType是方法的参数列表中参数类类型 如果是User就需要精确到具体位置-->
        <select id="getUserById" parameterType="int" resultType="com.dyz.pojo.User">
            select * from user where id=#{id}
        </select>
    
        <insert id="addUser" parameterType="com.dyz.pojo.User">
            insert into mybatis.user(id, NAME, pwd) VALUES (#{id},#{name},#{pwd})
        </insert>
        <update id="updateUser" parameterType="com.dyz.pojo.User">
            update mybatis.user set NAME=#{name} where id=#{id}
        </update>
        <delete id="deleteUser" parameterType="int" >
            delete from user where id=#{id}
        </delete>
    </mapper>
    
  • 测试

    package com.dyz.dao;
    
    import com.dyz.pojo.User;
    import com.dyz.utils.MybatisUtils;
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class UserDaoTest {
        @Test
        public void Test(){
            //获取SqlSession对象
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            //getMapper 获取接口的实例对象mapper;
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            /*执行该方法  该方法已经在UserMapper.xml中实现
            而UserMapper.xml也与UserDao接口进行了绑定
            因此直接mapper.方法即可执行
             */
            List<User> userList = mapper.getUserList();
            for(User user:userList){
                System.out.println(user);
            }
            sqlSession.close();
        }
        @Test
        public void getUserById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            User user= mapper.getUserById(1);
            System.out.println(user);
            sqlSession.close();
        }
        @Test
        public void addUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            int result = mapper.addUser(new User(4, "章鱼", "123654789"));
            //增删改必须要提交事务!!
            sqlSession.commit();
            sqlSession.close();
        }
        @Test
        public void updateUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            mapper.updateUser(new User(4,"张二","147852"));
            //增删改必须要提交事务
            sqlSession.commit();
            sqlSession.close();
        }
        @Test
        public void deleteUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            mapper.deleteUser(4);
            //增删改必须要提交事务
            sqlSession.commit();
            sqlSession.close();
        }
    }
    
    

增删改需要提交事务

报错读错时从下往上读

万能的Map

当数据库中对象的字段过多时,我们可以考虑去使用Map (字段过多,但我们只需要对少数字段进行修改)

//修改用户(修改部分字段)
int updateUser2(Map<String,Object> map);
<!--    修改用户信息2-->
    <update id="updateUser2" parameterType="Map">
        update mybatis.user set pwd=#{password} where id=#{id}
    </update>
@Test
public void updateUser2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    HashMap<String, Object> map = new HashMap<>();
    map.put("password","360669145");
    map.put("id",1);
    mapper.updateUser2(map);
    sqlSession.commit();
    sqlSession.close();
}

Map传递参数,直接在sql中去取key即可[parameterType=Map]

模糊查询

//模糊查询
List<User> getUser(String name);
<!--    模糊查询1-->
    <select id="getUser" resultType="com.dyz.pojo.User">
        select *from mybatis.user where NAME like #{name}
    </select>
@Test
public void getUser(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDao mapper = sqlSession.getMapper(UserDao.class);
    List<User> user = mapper.getUser("王%");
    for (User user1 : user) {
        System.out.println(user1);
    }
    sqlSession.close();
}

配置解析

核心配置文件 Mybatis-config.xml

<?xml version="1.0" encoding="UTF8" ?>
<!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.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/dyz/dao/UserMapper.xml"/>
    </mappers>
</configuration>

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

环境配置

Mybaits可以配置成适应多种环境

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

Mybatis,默认的事务管理器是JDBC, 连接池 POOLED

属性

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

编写一个配置文件 在Mybatis核心配置文件中导入

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration 核心配置文件-->
<configuration>
<!--    映入外部配置文件 自闭和标签 properties标签需要写在第一个 直接在resource文件夹下,所以不需要完整路径-->
    <!--   也可以在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>
    <mappers>
<!--        不在resource文件夹下,所以需要完整路径-->
        <mapper resource="com/dyz/dao/UserMapper.xml"/>
    </mappers>
</configuration>

类型别名(typeAlias)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写

typeAlias标签只能放在第三个,前面没有的话就默认为第三个

<!--    类型别名 typeAlias-->
    <typeAliases>
        <typeAlias type="com.dyz.pojo.User" alias="User"/>
    </typeAliases>
<select id="getUserList" resultType="User">
    select *from user
</select>

2.指定一个包名,MybatisUI在包下面搜索需要的Java Bean,比如扫描实体类的包,它的默认就为这个类的类名(首字母小写)

<typeAliases>
    <package name="com.dyz.pojo"/>
</typeAliases>
<select id="getUserList" resultType="user">
    select *from user
</select>

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

如果实体类十分多,建议使用第二种

但是第一种可以自定义别命,第二种不行,如果非要改可以在实体类上加注解.

@Alias("hello")
public class User {
    private int id;
    private  String name;
    private String pwd;

别命

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

映射器

MapperRegistry:注册绑定mapper文件

方式一:推荐使用

<mappers>
    <mapper resource="com/dyz/dao/UserMapper.xml"/>
</mappers>

方式二

    <mappers>
<!--        不在resource文件夹下,所以需要完整路径-->
<!--        <mapper resource="com/dyz/dao/UserMapper.xml"/>-->
        <mapper class="com.dyz.dao.UserDao"/>
    </mappers>

接口和mapper文件必须同名

两个文件必须在同一个包下面

方式三

 <mappers>
<!--        不在resource文件夹下,所以需要完整路径-->
<!--        <mapper resource="com/dyz/dao/UserMapper.xml"/>-->
<!--        <mapper class="com.dyz.dao.UserDao"/>-->
        <package name="com.dyz.dao"/>
    </mappers>

接口和mapper文件必须同名

两个文件必须在同一个包下面

作用域和生命周期

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

SqlSessionFactoryBulider

  • 一旦创建了SqlSessionFactory,就不在需要它了
  • 局部变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcvXVuc0-1659085170242)(D:\桌面\javaPicture\1650515448008.png)]

SqlSessionFactory

  • 可以理解为数据库连接池
  • SqlSessionFactory一旦被创建就会在应用运行期间一直存在,没有任何理由丢弃它或者重新创建另一个实例
  • 因此SqlSessionFactory的最佳作用域就是应用作用域.
  • 最简单的就是使用单例模式或者静态单例模式

SqlSession

  • 连接到连接池的一个请求!
  • SqlSesion的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用域是请求或者方法的作用域
  • 用完之后需要赶紧关闭,否则资源被占用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oO9ozHRA-1659085170242)(D:\桌面\javaPicture\1650515596446.png)]

这里的每一个Mapper就代表一个具体的业务

解决属性名和字段名不一致的问题

数据库中的字段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5I1HN4DO-1659085170243)(D:\桌面\javaPicture\1650517663397.png)]

测试实体类字段不一致的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sqjk1z21-1659085170244)(D:\桌面\javaPicture\1650517885165.png)]

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCc3DNPP-1659085170245)(D:\桌面\javaPicture\1650518074556.png)]

//select *from user where id=1
//找不到pwd  因此password为null
//select id,name,pwd from user where id=1

解决办法

  • 起别名

    <select id="getUserById" parameterType="int" resultType="com.dyz.pojo.User">
        select id,name,pwd as password from mybatis.user where id=#{id}
    </select>
    
  • 结果集映射

    id name pwd
    id name password
    
    <!--    id是该标签的唯一属性 与select标签中resultMap相对应
             type是实体类的名称 我们在配置文件中起了别名此处可以直接用别名-->
        <resultMap id="UserMap" type="User">
            <result column="pwd" property="password"/>
        </resultMap>
        <select id="getUserById" resultMap="UserMap">
            select * from mybatis.user where id=#{id}
        </select>
    

日志

日志工厂

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

曾经:sout debug

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8X4kigM-1659085170246)(D:\桌面\javaPicture\1650602228720.png)]

  • SLF4J
  • LOG4J(deprecated since 3.5.9) (掌握)
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING (掌握)
  • NO_LOGGING

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

STDOUT_LOGGING

STDOUT_LOGGING标准日志输出

<!--properties下面-->
<!--    日志-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMW9mIMD-1659085170247)(D:\桌面\javaPicture\1650602863697.png)]

LOG4J

什么是Log4j

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件

  • 我们也可以控制每一条日志的输出格式

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程

  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

  1. 先导入log4j的依赖

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. LOG4J.properties

    #将等级为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/kuang.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.sql.PreparedStatement=DEBUG
    
  3. 配置log4j为日志的实现

<!--    log4j日志实现-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
  1. LOG4J的使用,直接测试运行刚才的查询

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y5wAM2Hf-1659085170248)(D:\桌面\javaPicture\1650604958597.png)]

简单使用

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

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

static Logger logger= Logger.getLogger(UserMapperTest.class);
  1. 日志级别

    log4j的日志信息一共分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,明白这一点很重要,这里Log4j有一个规则:假设设置了级别为P,如果发生了一个级别Q比P高,则可以启动,否则屏蔽掉。

    1. DEBUG: 这个级别最低的东东,一般的来说,在系统实际运行过程中,一般都是不输出的。因此这个级别的信息,可以随意的使用,任何觉得有利于在调试时更详细的了解系统运行状态的东东,比如变量的值等等,都输出来看看也无妨。
    2. INFO:这个应该用来反馈系统的当前状态给最终用户的,所以,在这里输出的信息,应该对最终用户具有实际意义,也就是最终用户要能够看得明白是什么意思才行。从某种角度上说,Info 输出的信息可以看作是软件产品的一部分(就像那些交互界面上的文字一样),所以需要谨慎对待,不可随便。
    3. WARN、ERROR和FATAL:警告、错误、严重错误,这三者应该都在系统运行时检测到了一个不正常的状态,他们之间的区别,要区分还真不是那么简单的事情。我大致是这样区分的:所谓警告,应该是这个时候进行一些修复性的工作,应该还可以把系统恢复到正常状态中来,系统应该可以继续运行下去。
      所谓错误,就是说可以进行一些修复性的工作,但无法确定系统会正常的工作下去,系统在以后的某个阶段,很可能会因为当前的这个问题,导致一个无法修复的错误(例如宕机),但也可能一直工作到停止也不出现严重问题。
      所谓Fatal,那就是相当严重的了,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话,可以肯定必然会越来越乱。这时候采取的最好的措施不是试图将系统状态恢复到正常,而是尽可能地保留系统有效数据并停止运行。
      也就是说,选择 Warn、Error、Fatal 中的具体哪一个,是根据当前的这个问题对以后可能产生的影响而定的,如果对以后基本没什么影响,则警告之,如果肯定是以后要出严重问题的了,则Fatal之,拿不准会怎么样,则 Error 之。

    分页

    使用Limit分页

    #从下标1(第二个数据)开始查两行数据
    select *from mybatis.user limit 1,2
    # 从下标0开始查询两行数据 相当于limit 0,2
    select *from mybatis.user limit 2;
    

    使用MyBatis实现分页

    1. 接口

       //分页
      List<User> getUserByLimit(Map<String,Integer> map);
      
    2. Mapper.xml

      <!--    分页查询-->
          <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
              select *from mybatis.user LIMIT #{startIndex},#{pageSize}
          </select>
      
    3. Test

      @Test
      public void getUserByLimitTest(){
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          HashMap<String, Integer> map = new HashMap<>();
          map.put("startIndex",1);
          map.put("pageSize",3);
          List<User> userByLimit = mapper.getUserByLimit(map);
          for (User user : userByLimit) {
              System.out.println(user);
          }
          sqlSession.close();
      }
      
    4. RowBounds分页查询(扩展了解)

      接口

      //分页2
       List<User> getUserByRowBounds();
      

      mapper

      <!--    分页查询2 熟悉 不重要-->
          <select id="getUserByRowBounds"  resultMap="UserMap">
              select *from mybatis.user
          </select>
      

      Test

      @Test
      public void getUserByLimitTest2(){
          RowBounds rowBounds = new RowBounds(1, 2);
          SqlSession sqlSession = MybatisUtils.getSqlSession();
          List<User> users = sqlSession.selectList("com.dyz.dao.UserMapper.getUserByRowBounds", null, rowBounds);
          for (User user : users) {
              System.out.println(user);
          }
          sqlSession.close();
      
      }
      

分页插件

MyBatis 分页插件 PageHelper

使用注解开发

  1. 接口
//查询全部用户
@Select("select *from user")
List<User> getUserList();
  1. 核心配置文件中绑定接口

        <mappers>
    <!--        不在resource文件夹下,所以需要完整路径-->
    <!--        <mapper resource="com/dyz/dao/UserMapper.xml"/>-->
            <mapper class="com.dyz.dao.UserMapper"/>
    <!--        <package name="com.dyz.dao"/>-->
        </mappers>
    
  2. Test

    @Test
    public void getUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    

    CRUD

    1. 在工具类创建(返回sqlSession)的时候实现自动提交事务

      public static SqlSession getSqlSession(){
      //    SqlSession sqlSession = sqlSessionFactory.openSession();
      //    return sqlSession;
         return  sqlSessionFactory.openSession(true);
      }
      
    2. 编写接口,增加注解

      package com.dyz.dao;
      
      import com.dyz.pojo.User;
      import org.apache.ibatis.annotations.*;
      
      import java.util.List;
      import java.util.Map;
      
      public interface UserMapper {
          //查询全部用户
          @Select("select *from user")
          List<User> getUserList();
          //根据id查询用户
          @Select("select *from user where id=#{id} and name=#{name} ")
          User getUserByID(@Param("id") int id,@Param("name") String name);
          //增加用户
          //#{参数和实体类中相同}
          @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
          int addUser(User user);
          //修改用户
          @Update("update user set name=#{name},pwd=#{password} where id=#{id}")
          int updateUser(@Param("name") String name,@Param("password") String password,@Param("id") int id);
          //删除用户
          @Delete("delete from user where id=#{id}")
          int deleteUser(@Param("id") int id);
      }
      
    package com.dyz.dao;
    
    import com.dyz.pojo.User;
    import org.apache.ibatis.annotations.Param;
    
    public interface UserMapper {
        //查询User
        User getUserById(@Param("id") int id,@Param("name") String name);
    }
    
    <mapper namespace="com.dyz.dao.UserMapper">
        <select id="getUserById"  resultType="user">
            select *from mybatis.user where id=#{id} and name=#{name} #{使用注解括号中的参数}
        </select>
    </mapper>
    
    public class UserMapperTest {
        @Test
        public void getUserByIdTest(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(1, "张三");
            System.out.println(user);
            sqlSession.close();
        }
    }
    

    关于@Param()注解

    • 基本类型的参数或者String类型,需要加上
    • 引用类型不需要加
    • 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
    • 我们在SQL中的引用就是我们这里的@Param()中设定的属性名

    Mybatis执行流程

    1. Resource.getResourceAsStream:获取Mybatis-config.xml配置文件的数据流

    2. build调用XMLConfigBuilder(inputStream, environment, properties);解析配置文件,解析完之后传给configuration

      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
          SqlSessionFactory var5;
          try {
              XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
              var5 = this.build(parser.parse());
          } catch (Exception var14) {
              throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
          } finally {
              ErrorContext.instance().reset();
      
              try {
                  inputStream.close();
              } catch (IOException var13) {
              }
      
          }
      
          return var5;
      }
      
    3. SqlSession中创建transaction事务管理器

    4. SqlSession中创建executor执行器

    5. 创建SqlSession实例

    6. 实现CRUD

    7. 查看是否执行成功

    8. 提交事务

    9. 关闭

Lombok

使用步骤

  1. 在IDEA中安装Lombok插件

  2. 在项目中导入lombok的jar包

  3. 添加注解

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    @val
    @var
    experimental @var
    @UtilityClass
    
@Data

public class User {
    int id;
    String name;
    String password;
}
@Data:无参构造,get set toString hashcode,equals
有参无参
@AllArgsConstructor
@NoArgsConstructor


放在类上所有属性都有 放在单个属性上 单个属性有
@toString
@Getter

多对一处理

例:多个学生有一个老师 对学生而言就是多对一

测试环境搭建

  1. 导入lombok依赖 如果父类文件中有 就不需要

  2. 新建实体类Teacher Student

  3. 建立Mapper接口

  4. 家里Mapper.xml文件

  5. 在核心配置文件中绑定注册我们的Mapper接口或者文件

  6. [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIAwPi4R-1659085170249)(D:\桌面\javaPicture\1650949308237.png)]

    即使Mapper.xml文件与Mapper接口不在同一个大的资源文件下,但是其路径名相同,编译后也会在同一个文件下.

按照查询嵌套处理

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dyz.dao.StudentMapper">
    <select id="getStudentTeacher" resultMap="studentTeacher">
        select *from mybatis.student;
    </select>
<!--思路:先查询出所有学生信息
    根据查询出学生信息中的tid 寻找对应的老师
-->
    <resultMap id="studentTeacher" type="Student">
<!--        javaType 是根据tid查询到的对象类型 select是查询该对象执行的方法-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select *from mybatis.teacher where id=#{tid}  此处#{tid}系统会自动匹配 {}里面可以随意写但不建议
    </select>
</mapper>

按照结果嵌套处理

    <select id="getStudentTeacher2" 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">
<!--        上面查询语句涉及到Teacher表中的name 一次你 此处需要给name添加结果集映射-->
            <result property="name" column="tname"/>
        </association>
    </resultMap>
原因是resultMap属性指向不正确
报错: Result Maps collection does not contain value for com.dyz.dao.StudentMapper.

一对多处理

例:一个老师有多个学生 对老师而言就是一对多

  1. 按照结果嵌套处理

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

    <select id="getTeacherStudent2" resultMap="teacherStudent2">
        select *from mybatis.teacher where id=#{tid}
    </select>
    
    <resultMap id="teacherStudent2" type="Teacher">
        <collection property="students" javaType="ArrayList" ofType="Student"  select="getStudentByTeacherId" column="id"/>
    </resultMap>
    
    <select id="getStudentByTeacherId" resultType="student">
        select *from mybatis.student where tid=#{id}
    </select>
    

小结

  1. 关联-association[多对一]

  2. 集合-collection [一对多]

  3. javaType ofType

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

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

动态sql

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

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

搭建环境

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

创建一个基础工程

  1. 导包
  2. 编写配置文件
  3. 编写实体类对应的Mapper接口和Mapper.XML文件

IF

//根据要求查数据
List<Blog> getBlogByIf(Map map);
    <select id="getBlogByIf" parameterType="map" resultType="Blog">
#    如果不传author 和 title name满足1=1的数据都会被查询出来
        select * from mybatis.blog where 1=1 (正常不会出现1=1 会是其他条件)
        <if test="author!=null">
            and author=#{author}
        </if>
        <if test="title!=null">
            and title=#{title}
        </if>
    </select>
@Test
    public void getBlogByIf(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap map = new HashMap();
        map.put("author","狂神说");   //  //如果什么都不传,会查出所有结果
        List<Blog> blogByIf = mapper.getBlogByIf(map);
        for (Blog blog : blogByIf) {
                System.out.println(blog);
        }
        sqlSession.close();
}

choose、when、otherwise

//根据要求查数据2
List<Blog> getBlogByIf2(Map map);
</select>
<select id="getBlogByIf2" parameterType="map" resultType="Blog">
    select *from mybatis.blog    //choose 相当于 switch  when相当于case 满足条件就跳出 因此查询结果根据第一个满足的条件来决定
    <where>
        <choose>
         <when test="title!=null">
              and title=#{title}
         </when>
           <when test="views!=null">
                views=#{views}
           </when>
       </choose>
    </where>
</select>
@Test
public void getBlogByIf2(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("title","Mybatis");     //如果什么都不传,会查出所有结果
    map.put("views",10000);
    List<Blog> blogByIf = mapper.getBlogByIf(map);
    for (Blog blog : blogByIf) {
        System.out.println(blog);
    }
    sqlSession.close();
}

trim(where set)

where

   <select id="getBlogByIf" parameterType="map" resultType="Blog">
   select * from mybatis.blog where 1=1  //此处如果没有条件且下面第一个author条件不满足那么语句就会执行失败
       				//因为查询sql语句会变成 select * from mybatis.blog where and title=#{title}
    <if test="author!=null">
        and author=#{author}
    </if>
    <if test="title!=null">
        and title=#{title}
    </if>
</select>

修改成如下就会自动把and去掉

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

set

//更新数据
int updateBlog(Map map);
<update id="updateBlog" parameterType="map">
    update mybatis.blog
<set>
    <if test="author!=null">
        author=#{author},
    </if>
    <if test="views!=null">
        views=#{views}
    </if>
    where id=#{id}
</set>
</update>
@Test
public void updateBlog(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    map.put("author","张宇说");  //修改的数据需要和sql中的对应 不能sql有 却没有数据传过去
    map.put("views",998998);
    mapper.updateBlog(map);
    sqlSession.close();
}

所谓动态SQL,本质上还是SQL语句.只是我们可以在SQL层面去执行一个逻辑代码

SQL片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用!

1.使用SQL标签抽取公共的部分

<sql id="select-title-views">
        <choose>
            <when test="title!=null">
                and title=#{title}
            </when>
            <when test="views!=null">
                views=#{views}
            </when>
        </choose>
</sql>
  1. 在需要使用的地方使用include标签引用即可

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

    注意事项

    • 最好基于单表来定义SQL片段
    • 不要存在where标签

foreach

//根据id集合查询博客
List<Blog> getBlogByIdList(Map map);
<!--    select *from Mybatis.blog where id in(1,2,3)-->
    <select id="getBlogByIdList" parameterType="map" resultType="blog">
#         select *from Mybatis.blog where (id=1 or id=2 or id=3)
        select *from mybatis.blog
        <where>
#             collection是存储id的集合名 通过参数map传过来 item是集合中存放的数据名 separator 集合中数据的分隔符
        <foreach collection="idl" item="id" open="(" close=")" separator="or">
            id=#{id}
        </foreach>
        </where>
    </select>
@Test
public void getBlogByIdList(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    ArrayList list = new ArrayList();
    list.add(1);
    list.add(2);
    list.add(3);
    HashMap map = new HashMap();
    map.put("idl",list);
    List<Blog> blog = mapper.getBlogByIdList(map);
    for (Blog blog1 : blog) {
        System.out.println(blog1);
    }
    sqlSession.close();
}

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

建议:

  • 先在Mysql中写出完整的SQL,再对应的去修改成为我们的动态SQL的实现

缓存

简介

查询:需要连接到数据库,耗资源!
一次查询的结果,给他暂存在一个可以直接取到的地方---内存即缓存
我们可以再次查询相同数据的时候,直接走缓存,就不用走数据库了

  1. 什么是缓存(cache)
    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题.
  2. 为什么使用缓存?
    • 减少和数据库交互的次数,减少系统开销,提高系统效率
  3. 什么样的数据能使用缓存?
    • 经常查询并且不经常改变的数据

Mybatis缓存

  • Mybatis包含了一个非常强大的缓存特性,他可以非常方便的定制和配置缓存,缓存可以极大地提高查询到效率
  • Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
  • 默认情况下只有一级缓存开启(SqlSession级别的缓存,也称为本地缓存)
  • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
  • 为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

一级缓存

  • 一级缓存也叫本地缓存:SqlSession
    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试步骤

  1. 开启日志

  2. 测试在一个Session中查询两次相同的记录

  3. 查看日志输出
    在这里插入图片描述
    g-blog.csdnimg.cn/645f374aca724ed287e7ed8fe0a21b7a.png#pic_center)

只查询了一次

缓存失效的情况

  1. 查询不同的东西

2.增删改操作,可能会改变原来的数据,所以必定会刷新缓存(再次执行sql)

在这里插入图片描述

  1. 手动刷新缓存

    public void getUserByIdTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        // sqlSession.clearCache(); 此语句放在执行接口方法上面就不会影响下面的语句执行 
        User user = mapper.getUserById(1);
        System.out.println(user);
        System.out.println("==============");
        sqlSession.clearCache();  //放在此处则会影响 会重新执行sql访问数据库
        User user2 = mapper.getUserById(1);
        System.out.println(user2);
        sqlSession.close();
    }
    

小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段!这个区间段外是会刷新缓存的

一级缓存就是一个Map

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
  • 基于namespace级别的缓存,一个名称空间对应一个二级缓存
  • 工作机制
    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了 一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会被放在自己对应的缓存(map)中

步骤:

  1. 开启全局缓存

    <!--        显示开启二级缓存-->
            <setting name="cacheEnabled" value="true"/>
    
  2. 在要使用二级缓存的mapper中开启

    <!--    在当前mapper.xml文件中使用二级缓存-->
    <cache/>
    

    也可以自定义参数

    <!--    在当前mapper.xml文件中使用二级缓存-->
    <cache  eviction="FIFO"
            flushInterval="60000"
            size="512"
            readOnly="true"/>
    
  3. 测试

    问题:我们需要将实体类序列化(继承Serializable接口)否则会报错

    java.io.NotSerializableException: com.dyz.pojo.User
    
    

小结:

  • 只要开启了二级缓存,在同一个Mapper下就有效

  • 所有的数据都会放在一级缓存中

  • 只有当会话提交或者关闭的时候才会提交到二级缓存中

  • 缓存顺序:

    先看二级缓存中有没有

    再看一级缓存中有没有

    最后去查数据库

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值