Featured image of post MyBatis学习笔记

MyBatis学习笔记

记录学习MyBatis时的一些心得体会

花了四天的时间,终于把MyBatis的视频教程全部看完了,为了以后不要那么快的遗忘掉,还是来写一下笔记比较好。

下面的内容都是基于遇见狂神说MyBatis视频教程而来,在此感谢能提供这么优秀的教程。

# 简介

  • MyBatis的作用用我自己的话说,就是不用自己手动搞JDBC了,省事,行!
  • MyBatis作用于持久层——DAO层 【DAO (Data Access Object) 数据访问对象】,通俗点说就是用来操作数据库的层
  • 选择MyBatis的最大原因还是用的人多,为后面的Spring打基础_(:з)∠)_

# 第一个MyBatis程序

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

# 具体代码

  1. 创建Maven项目

  2. 引入依赖包

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    <!-- 导入依赖 -->
    <dependencies>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
            <scope>runtime</scope>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    
  3. resources目录下创建mybatis-config.xml,作为MyBatis的配置文件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    
    <!--核心配置文件-->
    <configuration>
    
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
    <!--每一个mapper都需要注册-->
        <mappers>
            <mapper resource="UserMapper.xml"/>
        </mappers>
    
    </configuration>
    
  4. 编写MyBatis工具类,在utils的包下面创建MybatisUtils类,填入以下代码

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    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
    
    /**
     * @author luoboQAQ
     */
    public class MybatisUtils {
        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();
            }
        }
    
        //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
        //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
    
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
  5. pojo包下创建实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public class User {
       private int id;  		//id
       private String name;   	//姓名
       private String pwd;   	//密码
    
       //构造,有参,无参
       //set/get
       //toString() 
    }
    
  6. dao包下创建接口类,我们需要的功能就填入到这里面

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    import top.lbqaq.pojo.User;
    
    import java.util.List;
    import java.util.Map;
    
    public interface UserMapper {
        //模糊查询
        List<User> getUserLike(String value);
    
        //查询全部用户
        List<User> getUserList();
    
        //根据ID查询用户
        User getUserById(int id);
    
        //插入用户
        int addUser(User user);
        int addUser2(Map<String,Object> map);
    
        //修改用户
        int updateUser(User user);
    
        //删除用户
        int deleteUser(int id);
    }
    
  7. 编写Mapper.xml配置文件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
    <?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绑定一个对应的Dao/Mapper接口-->
    <mapper namespace="top.lbqaq.dao.UserMapper">
        <!--id就是namespace中对应的方法名,resultType:sql语句的返回值-->
        <select id="getUserList" resultType="top.lbqaq.pojo.User">
            select * from user
        </select>
    
        <select id="getUserLike" resultType="top.lbqaq.pojo.User">
            select * from user where name like #{value}
        </select>
    
        <!--parameterType:传入参数类型-->
        <select id="getUserById" parameterType="int" resultType="top.lbqaq.pojo.User">
            select * from user where id=#{id}
        </select>
    
        <insert id="addUser" parameterType="top.lbqaq.pojo.User">
            insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
        </insert>
    
        <!--传递map的key-->
        <insert id="addUser2" parameterType="map">
            insert into user (id,name,pwd) values (#{userid},#{userName},#{passWord})
        </insert>
    
        <update id="updateUser" parameterType="top.lbqaq.pojo.User">
            update user set name =#{name},pwd=#{pwd} where id=#{id}
        </update>
    
        <delete id="deleteUser" parameterType="int">
            delete from user where id=#{id}
        </delete>
    </mapper>
    
  8. 进行测试

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    
    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    import top.lbqaq.pojo.User;
    import top.lbqaq.utils.MybatisUtils;
    
    import java.util.HashMap;
    import java.util.List;
    
    public class UserMapperTest {
        @Test
        public void test() {
            //获得sqlSession对象
            try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
                //执行SQL
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                List<User> userList = mapper.getUserList();
                for (User user : userList) {
    System.out.println(user);
                }
            }
        }
    
        @Test
        public void getUserLike() {
            //获得sqlSession对象
            try (SqlSession sqlSession = MybatisUtils.getSqlSession()) {
                //执行SQL
                UserMapper mapper = sqlSession.getMapper(UserMapper.class);
                List<User> userList = mapper.getUserLike("%李%");
                for (User user : userList) {
    System.out.println(user);
                }
            }
        }
    
        @Test
        public void getUserById() {
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(1);
            System.out.println(user);
            sqlSession.close();
        }
    
        @Test
        public void addUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int res = mapper.addUser(new User(4, "小明", "123"));
            if(res>0){
                System.out.println("提交成功");
                //提交事务
                sqlSession.commit();
            }
            sqlSession.close();
        }
    
        @Test
        public void addUser2(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
            HashMap<String,Object> map= new HashMap<>();
            map.put("userid",5);
            map.put("userName","小绿");
            map.put("passWord","1234");
            int res = mapper.addUser2(map);
            if(res>0){
                System.out.println("提交成功");
                //提交事务
                sqlSession.commit();
            }
            sqlSession.close();
        }
    
        @Test
        public void updateUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int res = mapper.updateUser(new User(4, "小明", "123456"));
            if(res>0){
                System.out.println("提交成功");
                //提交事务
                sqlSession.commit();
            }
            sqlSession.close();
        }
    
        @Test
        public void deleteUser(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int res = mapper.deleteUser(4);
            if(res>0){
                System.out.println("提交成功");
                //提交事务
                sqlSession.commit();
            }
            sqlSession.close();
        }
    }
    

# 注意事项

  • 如果Mapper.xml创建在java目录里而不是resouces里,需要在Maven配置里配置过滤,否则会保错

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    <resources>
       <resource>
           <directory>src/main/java</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
       <resource>
           <directory>src/main/resources</directory>
           <includes>
               <include>**/*.properties</include>
               <include>**/*.xml</include>
           </includes>
           <filtering>false</filtering>
       </resource>
    </resources>
    
  • 当需要传入参数进行查询而不是传入实体类,有两种方法

    • 第一种:直接在方法中传递参数。

      在接口方法的参数前加@Param属性,sql语句编写的时候,直接取@Param中设置的值即可,不需要单独设置参数类型

      1
      2
      3
      4
      5
      6
      7
      
      User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd);
      
      /*
         <select id="selectUserByNP" resultType="top.lbqaq.pojo.User">
           select * from user where name = #{username} and pwd = #{pwd}
         </select>
      */
      
    • 第二种:通过map来传递参数。(推荐)

      在接口创建时就直接使用map作为参数

      1
      
      int addUser2(Map<String,Object> map);
      

      在写sql时直接设置入参为map即可

      1
      2
      3
      4
      
      <!--传递map的key-->
          <insert id="addUser2" parameterType="map">
          	insert into user (id,name,pwd) values (#{userid},#{userName},#{passWord})
          </insert>
      

# 配置解析

官方文档

# 核心配置

MyBatis官方给出的配置项有以下这么多:

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

我们需要关注的就是properties(属性)settings(设置)typeAliases(类型别名)plugins(插件)environments(环境配置)mappers(映射器)这几项配置,其余的可以忽略(基本用不到)

# properties

数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。

说白了,就是能将数据库那些配置项从核心配置中剥离出来,降低耦合。

# 用法

  1. resources目录下创建db.properties,填入下面的内容

    1
    2
    3
    4
    
    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis
    username=root
    password=root
    
  2. 在配置xml中导入

    1
    2
    3
    4
    5
    6
    
    <!--引用外部配置文件-->
    <properties resource="db.properties">
        <!--外部文件优先级大于内部(下面)-->
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </properties>
    

    正常情况下,只需要用<properties resource="db.properties"/>这短短一句就行了,上面的代码是用来测试优先级的

# settings

这里存放的是MyBatis的一些设置项,具体的内容在官方文档上可以查看

常用的设置项有以下几个:

设置名 描述 有效值 默认值
cacheEnabled 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 true | false true
mapUnderscoreToCamelCase 是否开启驼峰命名自动映射,即从经典数据库列名A_COLUMN 映射到经典 Java 属性名 aColumn。 true | false False
logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置

# typeAliases

类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。

也就是在resultTypeparameterType中不需要写那么长的类名了

# 用法

1
2
3
4
5
6
7
<!--可以给实体类起别名-->
<typeAliases>
    <!--手动指定-->
    <typeAlias type="top.lbqaq.pojo.User" alias="User"/>
    <!--自动扫描包下所有类,别名为类名全小写-->
    <package name="top.lbqaq.pojo"/>
</typeAliases>

有两种方法,第一种就是手动指定别名,第二种就是通过package标签自动扫描包下所有的类

在第二种方法下,默认别名是类名全小写(当然首字母大写也同样可用)

如果在类前加上@Alias注解,则别名就为手动设定的内容。

# plugins

这里是配置mybatis的插件的地方,常用的插件有以下几个:

这里是就不展开这些插件了,以后有需要再看

# environments

配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<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>
  • transactionManager: 事务管理器 。有两个选项:
    • JDBC:正常的
    • MANAGED:几乎啥都不干
  • dataSource:如何使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。有三个选项:
    • unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
    • pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
    • jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。

# mappers

用于告诉MyBatis我们写的xml文件到底在哪,每个xml文件都需要绑定!

常用的有以下几种方法:

1
2
3
4
<!-- 使用相对于类路径的资源引用 -->
<mappers>
    <mapper resource="top/lbqaq/dao/UserMapper.xml"/>
</mappers>
1
2
3
4
5
6
7
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
    <mapper class="top.lbqaq.dao.UserMapper"/>
</mappers>
1
2
3
4
5
6
7
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
 <package name="org.mybatis.builder"/>
</mappers>

# ResultMap

# 属性名和字段名不一致

在实际开发中,会遇到类的属性名和数据库的字段名不一致,这时MyBatis的自动匹配将无法产生作用,这时可以使用ResultMap来解决这个问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<mapper namespace="top.lbqaq.dao.UserMapper">
    <!--结果集映射-->
    <resultMap id="UserMap" type="User">
        <!--column:数据库中的字段;property:实体类的属性-->
<!--        <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 user where id=#{id}
    </select>
</mapper>

# 多对一

在开发时常常会有这样的需求,比如多个学生都被一个老师教,下面将对该实例具体展开来了解多对一如何处理。

# 环境搭建

  1. 添加Lombok插件

  2. 引入Maven依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
  3. 编写实体类

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    @Data
    public class Student {
        private int id;
        private String name;
    
        /**
         * 学生需要关联一个老师
         */
        private Teacher teacher;
    }
    
    1
    2
    3
    4
    5
    
    @Data
    public class Teacher {
        private int id;
        private String name;
    }
    

# 按查询嵌套处理

思路:

  1. 获取所有学生的信息

  2. 根据获取的学生信息的老师ID->获取该老师的信息

  3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?

    1. 做一个结果集映射:StudentTeacher
    2. StudentTeacher结果集的类型为 Student
    3. 学生中老师的属性为teacher,对应数据库中为tid。 多个 [1,…)学生关联一个老师=> 一对一,一对多
    4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>

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

<select id="getTeacher" resultType="Teacher">
    select * from teacher where id=#{tid}
</select>

注意点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<resultMap id="StudentTeacher" type="Student">
   <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
   <association property="teacher"  column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--
这里传递过来的id,只有一个属性的时候,下面可以写任何值
association中column多参数配置:
   column="{key=value,key=value}"
   其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
-->
<select id="getTeacher" resultType="teacher">
  select * from teacher where id = #{id} and name = #{name}
</select>

这个还是比较难以理解的,一般我选择用第二种方法

# 按结果嵌套处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<!--按照结果嵌套处理-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname
    from student s,teacher t
    where s.tid=t.id
</select>

<resultMap id="StudentTeacher2" type="Student">
    <result column="sid" property="id"/>
    <result column="sname" property="name"/>
    <association property="teacher" javaType="Teacher">
        <result column="tname" property="name"/>
    </association>
</resultMap>

这种就比较好理解了,难度主要在sql部分,好在之前数据库学的还行,这种方法还是比较适合我。

# 一对多

一个老师会教多个学生。

# 环境搭建

1
2
3
4
5
6
@Data
public class Student {
    private int id;
    private String name;
    private int tid;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Data
public class Teacher {
    private int id;
    private String name;

    /**
     * 一个老师有多个学生
     */
    private List<Student> students;
}

# 按结果嵌套查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from student s,teacher t
    where s.tid=t.id and t.id=#{tid}
</select>

<resultMap id="TeacherStudent" type="Teacher">
    <result column="tid" property="id"/>
    <result column="tname" property="name"/>
    <!--集合中的泛型信息用ofType获取-->
    <collection property="students" ofType="Student">
        <result column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="tid" property="tid"/>
    </collection>
</resultMap>

JavaTypeofType都是用来指定对象类型的。

JavaType是用来指定pojo中属性的类型。

ofType指定的是映射到list集合属性中pojo的类型。

# 按查询嵌套

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!--按查询嵌套-->
<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from 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 student where tid = #{tid}
</select>

# 日志工厂

如果一个数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。可见打印出SQL语句是十分重要的。(还记得之前项目综合实践为了搞日志忙了半天,结果还是没搞出来😭)

# 标准日志

在MyBatis里自带了通过控制台打印的日志功能,如果项目并不是很复杂,用它就足够了。

只需要在设置里设置启用就可以了。

1
2
3
<settings>
       <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

# log4j

log4j相比标准日志,那不知道高到哪里去了,具体的功能我就不写了,毕竟一百度就能出来了。

使用步骤:

  1. 导入log4j的包

    1
    2
    3
    4
    5
    
    <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
       <version>1.2.17</version>
    </dependency>
    
  2. resources目录下新建log4j.properties,并填入以下配置文件

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    #将等级为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/log.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. 在MyBatis里启用

    1
    2
    3
    
    <settings>
       <setting name="logImpl" value="LOG4J"/>
    </settings>
    
  4. 在程序中使用Log4j进行输出

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    import org.apache.log4j.Logger;
    import org.junit.Test;
    
    public class UserMapperTest {
        static Logger logger = Logger.getLogger(UserMapperTest.class);
    
        @Test
        public void testLog4j(){
            logger.info("info:进入了testLog4j");
            logger.debug("debug:进入了testLog4j");
            logger.error("error:进入了testLog4j");
        }
    }
    

# 使用注解开发

MyBatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。

而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建。

在注解开发中,主要使用这几个注解:@select ()@update ()@Insert ()@delete ()

举例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public interface UserMapper {

    @Select("select * from user")
    List<User> getUser();

    @Select("select * from user where id=#{id}")
    User getUserByID(@Param("id") int id);

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

改造MybatisUtils工具类的getSession() 方法,重载实现。

1
2
3
4
5
6
7
8
//获取SqlSession连接
public static SqlSession getSession(){
    return getSession(true); //事务自动提交
}

public static SqlSession getSession(boolean flag){
    return sqlSessionFactory.openSession(flag);
}

@Param注解用于给方法参数起一个名字。以下是总结的使用原则:

  • 在方法只接受一个参数的情况下,可以不使用@Param。
  • 在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
  • 如果参数是 JavaBean , 则不能使用@Param。
  • 不使用@Param注解时,参数只能有一个,并且是JavaBean。

# 动态SQL

动态SQL指的是根据不同的查询条件 , 生成不同的sql语句。

主要使用这几个标签:ifchoosewheresetforeach

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?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="top.lbqaq.dao.BlogMapper">

    <insert id="addBook" parameterType="Blog">
        insert into blog(id, title, author, create_time, views)
        values (#{id}, #{title}, #{author}, #{createTime}, #{views})
    </insert>

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

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

    <update id="updateBlog" parameterType="map">
        update blog
        <set>
            <if test="title != null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author}
            </if>
        </set>
        where id = #{id}
    </update>
    
    <select id="queryBlogForeach" parameterType="map" resultType="Blog">
        select * from blog
        <where>
                <!--collection:指定输入对象中的集合属性
        			item:每次遍历生成的对象
       	 			open:开始遍历时的拼接字符串
        			close:结束时拼接的字符串
        			separator:遍历对象之间需要拼接的字符串-->
            <foreach collection="ids" item="id" open="(" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
</mapper>

# SQL片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。

提取SQL片段:

1
2
3
4
5
6
7
8
<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

引用SQL片段:

1
2
3
4
5
6
7
8
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
        <include refid="if-title-author"></include>
        <!-- 在这里还可以引用其他的 sql 片段 -->
    </where>
</select>

注意:

①、最好基于 单表来定义 sql 片段,提高片段的可重用性

②、在 sql 片段中不要包括 where

# 缓存

# 简介

1、什么是缓存 [ Cache ]?

  • 存在内存中的临时数据。
  • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

2、为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率。

3、什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据。

# MyBatis缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

# 一级缓存

一级缓存也叫本地缓存:

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

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

  1. sqlSession不同
  2. sqlSession相同,查询条件不同
  3. sqlSession相同,两次查询之间执行了增删改操作!
  4. sqlSession相同,手动清除一级缓存

# 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;

# 使用方法

  1. 开启全局缓存 mybatis-config.xml

    1
    
    <setting name="cacheEnabled" value="true"/>
    
  2. 去每个mapper.xml中配置使用二级缓存,这个配置非常简单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    <cache/>
    
    官方示例
    <cache
      eviction="FIFO"
      flushInterval="60000"
      size="512"
      readOnly="true"/>
    这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
    
Licensed under CC BY-NC-SA 4.0
最后更新于 2021-08-11 15:20:00