MyBatis学习笔记

Mybatis

1.0 什么是Mybatis
  • Mybatis是一款优秀的持久层框架

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

  • MyBatis官网:http://www.mybatis.org/mybatis-3/zh/index.html

2.0 持久化
  • 持久化是将程序数据在持久化状态和瞬时状态间转换的机制。
3.0 第一个Mybatis程序

1、搭建数据库

DROP TABLE IF EXISTS `product`;
CREATE TABLE `product`  (
  `pid` int(11) NOT NULL AUTO_INCREMENT,
  `pname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `picture` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `details` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` decimal(10, 2) NULL DEFAULT NULL,
  `count` int(10) NULL DEFAULT NULL,
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES (1, '手机', 'img/1.jpg', '手机', 1999.00, 150);
INSERT INTO `product` VALUES (2, 'HUAWEI', 'img/2.jpg', '平板', 399999.00, 50);
INSERT INTO `product` VALUES (3, '篮球', 'img/3.png', '锻炼身体', 1299.00, 200);
INSERT INTO `product` VALUES (4, '小汽车', 'img/4.jpg', '交通工具', 130299.00, 20);
INSERT INTO `product` VALUES (5, '鞋子', 'img/5.jpg', '生活用品', 2499.00, 50);
INSERT INTO `product` VALUES (6, '充电宝', 'img/6.jpg', '给电子设备供电', 199.00, 50);
INSERT INTO `product` VALUES (7, '伊利纯牛奶', 'img/7.jpg', '补充能量', 68.00, 100);
INSERT INTO `product` VALUES (8, '荣耀手机', 'img/8.jpg', '通信工具', 3799.00, 50);
INSERT INTO `product` VALUES (12, '计算机', 'computer.jpg', '办公', 19999.90, 100);

SET FOREIGN_KEY_CHECKS = 1;

2、导入相关依赖

<dependency>
 <groupId>org.mybatis</groupId>
 <artifactId>mybatis</artifactId>
 <version>3.5.2</version>
</dependency>
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>5.1.47</version>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.12</version>
</dependency>

3、编写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>

    <properties resource="db.properties"/>

    </settings>
    <!--  别名-->
    <typeAliases>
        <typeAlias type="com.yao.pojo.Product" alias="product"/>
    </typeAliases>



    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

<!--    mapper映射-->
    <mappers>
        <mapper resource="mappers/ProductMapper.xml"/>
    </mappers>
</configuration>

4、编写MyBatis工具类

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory = null;

    static {
        try {
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

//    获取连接
    public static SqlSession getCon(){
        return sqlSessionFactory.openSession();
    }

}

5、编写实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    private Integer pid;
    private String pname;
    private String picture;
    private String details;
    private BigDecimal price;
    private Integer count;
}

6、编写mapper接口

public interface ProductMapper {

//    查询所有
    List<Product> queryAll();

//    根据id查询
    Product queryById(Integer pid);

//    根据pname和details查询商品
    Product queryByPD(Map<String,Object> map);
}

7、编写mapper.xml文件

<?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">
<mapper namespace="com.yao.mapper.ProductMapper">
    <select id="queryAll" resultType="com.yao.pojo.Product">
        select * from product;
    </select>
    <select id="queryById" resultType="com.yao.pojo.Product">
        select * from product where pid=#{pid};
    </select>
    <select id="queryByPD" parameterType="map" resultType="com.yao.pojo.Product">
        select * from product where pname=#{pname} and details=#{details};
    </select>
</mapper>

8、数据库属性

db.driver = com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shoping?useSSL=false
db.username = root
db.password = 123456

9、测试运行

public class MybatisTest {

    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtils.getCon();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        List<Product> products = mapper.queryAll();
        products.forEach(System.out ::println);
    }

    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getCon();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        Product product = mapper.queryById(2);
        System.out.println(product);
    }

    @Test
    public void test3(){
        SqlSession sqlSession = MybatisUtils.getCon();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        Map<String,Object> map = new HashMap<>();
        map.put("pname","HUAWEI");
        map.put("details","平板");
        System.out.println(mapperueryByPD(map));
    }

}

可能出现的问题:maven静态资源过滤问题

<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>

4.0 MyBatis原理分析

MyBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用一脉相承,MyBatis的核心对象(SqlSession、Executor)与JDBC的核心对象(Connection、Statement)相互对应。

​ JDBC的四大核心对象:

(1)DriverManager, 用于注册数据库的连接

(2)Connection ,与数据库连接对象

(3)Statement/PrepareStatement ,操作SQL语句

(4)ResultSet, 结果集

​ MyBatis的四大核心对象:

(1)SqlSession, 该对象中包含了执行SQL语句的所有方法。

(2)Executor, 他根据SqlSession传递的参数动态生成需要执行的SQL语句

(3)MappedStatement,对映射SQL的封装

(4)ResultHandler, 对返回的结果进行处理

MyBatis工作原理以及核心流程详解

MyBatis的工作原理如下图所示:

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

说明

(1)读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。

(2)加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。

(3)构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。

(4)创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。

(5)Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。

(6)MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。

(7)输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。

(8)输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。

在JDBC中,Connection不直接执行SQL方法,而是利用Statement或者PrepareStatement来执行方法。

在使用JDBC建立了连接之后,可以使用Connection接口的createStatement()方法来获取Statement对象,也可以调用prepareStatement()方法获得PrepareStatement对象,通过executeUpdate()方法来执行SQL语句。

而在MyBatis中,SqlSession对象包含了执行SQL语句的所有方法。但是它是委托Executor执行的。

5.0 MyBatis配置文件解析

MyBatis核心配置文件

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

environments元素

<environments default="development">
 <environment id="development">
   <transactionManager type="JDBC">
     <property name="..." value="..."/>
   </transactionManager>
   <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元素

引入资源的方式

<!-- 方式一 -->
<!-- 使用相对于类路径的资源引用 -->
<mappers>
 <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>

<!-- 方式二 -->
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
 <mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>


<!-- 方式三 -->
<!--使用映射器接口实现类的完全限定类名需要配置文件名称和接口名称一致,并且位于同一目录下-->
<mappers>
 <mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>


<!-- 方式四 -->
<!--将包内的映射器接口实现全部注册为映射器但是需要配置文件名称和接口名称一致,并且位于同一目录下-->
<mappers>
 <package name="org.mybatis.builder"/>
</mappers>

properties元素

优化配置文件

<configuration>

    <properties resource="db.properties"/>
    
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

<!--    mapper映射-->
    <mappers>
        <mapper resource="mappers/ProductMapper.xml"/>
    </mappers>
</configuration>

typeAliases元素

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

方式一:
<typeAliases>
   <typeAlias type="com.yao.pojo.Product" alias="product"/>
</typeAliases>
当这样配置时,Product可以代替使用com.yao.pojo.Product的地方。

方式二:
<typeAliases>
   <package name="com.yao.pojo"/>
</typeAliases>
每一个在包 com.yao.pojo 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。

方式三:
@Alias("product")
public class Product {
    private Integer pid;
    private String pname;
    private String picture;
    private String details;
    private BigDecimal price;
    private Integer count;
}

其他配置元素

<settings>
 <setting name="cacheEnabled" value="true"/>
 <setting name="lazyLoadingEnabled" value="true"/>
 <setting name="multipleResultSetsEnabled" value="true"/>
 <setting name="useColumnLabel" value="true"/>
 <setting name="useGeneratedKeys" value="false"/>
 <setting name="autoMappingBehavior" value="PARTIAL"/>
 <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
 <setting name="defaultExecutorType" value="SIMPLE"/>
 <setting name="defaultStatementTimeout" value="25"/>
 <setting name="defaultFetchSize" value="100"/>
 <setting name="safeRowBoundsEnabled" value="false"/>
 <setting name="mapUnderscoreToCamelCase" value="false"/>
 <setting name="localCacheScope" value="SESSION"/>
 <setting name="jdbcTypeForNull" value="OTHER"/>
 <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
6.0 MyBatis 注解开发
  • mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建

  • sql 类型主要分成 :

    • @select ()
    • @update ()
    • @Insert ()
    • @Insert ()
    • @delete ()

利用注解开发,就不需要mapper.xml文件

编写查询注解

//    使用注解实现  不需要配置 *Mapper.xml
    @Select("select * from product limit #{starterIndex} , #{pageSize}")
    List<Product> getProductByLimit(Map<String ,Integer> map);

    @Select("select * from product")
    List<Product> getProductByRowBounds();

//    根据 id 查找商品
    @Select("select * from product where pid=#{id}")
    Product getProductById(@Param("id") Integer pid);

测试

    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        ProductMapper mapper = sqlSession.getMapper(ProductMapper.class);
        Map<String,Integer> map = new HashMap<>();
        int currentPage = 3;
        int pageSize = 3;
        map.put("starterIndex",(currentPage-1)*pageSize);
        map.put("pageSize",pageSize);
        mapper.getProductByLimit(map).forEach(System.out::println);
        sqlSession.close();
    }

本质:利用了动态代理

关于@Param

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

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

${} 与#{} 区别

#{} 是预编译,而${}是字符串替换
mybatis在处理 #{} 会将转化为 占位符 ? ,而在处理${}是直接替换为变量值
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);

INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('yao');

7.0 一对多与多对一处理

多对一的理解:

  • 多个学生对应一个人老师
  • 如果对于学生来说,就是多对一,

设计数据库

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` int(10) NOT NULL,
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `tid` int(10) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `fktid`(`tid`) USING BTREE,
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, '小明', 1);
INSERT INTO `student` VALUES (2, '小红', 1);
INSERT INTO `student` VALUES (3, '小张', 2);
INSERT INTO `student` VALUES (4, '小李', 1);
INSERT INTO `student` VALUES (5, '小王', 1);
INSERT INTO `student` VALUES (6, '张三', 2);
INSERT INTO `student` VALUES (7, '李四', 3);
INSERT INTO `student` VALUES (8, '王五', 3);

SET FOREIGN_KEY_CHECKS = 1;




DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher`  (
  `id` int(10) NOT NULL,
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, '张老师');
INSERT INTO `teacher` VALUES (2, '王老师');
INSERT INTO `teacher` VALUES (3, '李老师');

SET FOREIGN_KEY_CHECKS = 1;

添加依赖

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

编写实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private Integer id;
    private String name;
//    多个学生对应一个老师 (多对一)
    private Teacher teacher;

}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
    private Integer id;
    private String name;

//    一个老师拥有多个学生 (一对多)
    private List<Student> students;
}

db.properties文件

db.driver = com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/mybatis_db?useSSL=false
db.username = root
db.password = 123456

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>

    <properties resource="db.properties"/>

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

    <typeAliases>
        <typeAlias type="com.yao.pojo.Student" alias="student"/>
        <typeAlias type="com.yao.pojo.Teacher" alias="teacher"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"/>
        <mapper resource="mappers/TeacherMapper.xml"/>
    </mappers>
</configuration>

MyBatis工具类

package com.yao.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;

public class MybatisUtil {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

编写对应的mapper接口

package com.yao.mapper;

import com.yao.pojo.Student;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

public interface StudentMapper {

//    多对一
    Student getStudent(@Param("id") Integer id);

    //    查询多个学生姓名
    List<Student> queryByNames(List<String> list);

//    分组排序
    List<Student> groupBy(Map<String,Object> map);

}

package com.yao.mapper;

import com.yao.pojo.Teacher;
import org.apache.ibatis.annotations.Param;

public interface TeacherMapper {

//    根据老师id查出所对应的学生
    Teacher getTeacher(@Param("id") Integer id);

}

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">
<mapper namespace="com.yao.mapper.StudentMapper">

     <!--
   思路:
       1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
       2. 对查询出来的操作做结果集映射
           1. 集合的话,使用collection!
               JavaType和ofType都是用来指定对象类型的
               JavaType是用来指定pojo中属性的类型
               ofType指定的是映射到list集合属性中pojo的类型。
   -->
    
    <select id="getStudent" resultMap="studentMap">
        select s.id sid,s.name sname,t.name tname
        from student s, teacher t
        where s.tid = t.id and s.id=#{id};
    </select>
    
    <resultMap id="studentMap" type="student">
        <id column="sid" property="id"/>
        <result column="sname" property="name"/>
        <association property="teacher" javaType="teacher">
            <id column="tid" property="id"/>
            <result column="tname" property="name"/>
        </association>

    </resultMap>

    <select id="queryByNames" resultType="student">
        select * from student s where s.name in
        <foreach collection="list" item="item" open="(" close=");" separator=",">
            #{item}
        </foreach>
    </select>

    <select id="groupBy" resultType="student" parameterType="map">
        select * from student
        order by ${title} ${dir}
        limit #{start},#{size}
    </select>

</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">
<mapper namespace="com.yao.mapper.TeacherMapper">

    <select id="getTeacher" resultMap="teacherMap" >
        select s.id sid,s.name sname,t.id tid,t.name tname
        from teacher t ,student s
        where s.tid=t.id and t.id=#{id};
    </select>

    <resultMap id="teacherMap" type="teacher">
         <!--column是一对多的外键 , 写的是一的主键的列名-->
        <id column="tid" property="id"/>
        <result column="tname" property="name"/>
        <collection property="students" ofType="student">
            <id column="sid" property="id"/>
            <result column="sname" property="name"/>
        </collection>
    </resultMap>

</mapper>

测试

    /**
     * 一对多
     */
    @Test
    public void test1(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeacher(1);
        System.out.println(teacher);
    }
    
    
     /**
     * 多对一
     */
    @Test
    public void test2(){
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Student student = mapper.getStudent(1);
        System.out.println(student);
    }

小结

  • 多对一 使用 association
  • 一对多 使用 collection
  • 所以association是用于一对一和多对一,而collection是用于一对多的关系
  • JavaType和ofType都是用来指定对象类型的
    • JavaType是用来指定pojo中属性的类型
    • ofType指定的是映射到list集合属性中pojo的类型。

注意说明

1、保证SQL的可读性,尽量通俗易懂

2、根据实际要求,尽量编写性能更高的SQL语句

3、注意属性名和字段不一致的问题

4、注意一对多和多对一 中:字段和属性对应的问题

5、尽量使用Log4j,通过日志来查看自己的错误

8.0 动态SQL

什么是动态 SQL?

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

官网描述:
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。

  -------------------------------
  - 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

实体类略

MyBatis配置文件

<configuration>

    <properties resource="db.properties"/>

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

    <typeAliases>
        <typeAlias type="com.yao.pojo.Blog" alias="blog"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${db.driver}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mappers/BlogMapper.xml"/>
    </mappers>
</configuration>

mapper 接口

public interface BlogMapper {

//   新增一个博客
    int addBlog(Blog blog);

//    查询博客 <动态SQL IF>
    List<Blog> queryBlog(Map<String,Object> map);

//    <动态SQL CHOOSE>
    List<Blog> queryBlogChoose(Map<String,Object> map);

//    <动态SQL trim>
    int updateBlog(Map<String,Object> map);

//    <动态SQL Foreach>
    List<Blog> queryForeach(Map<String ,Object> map);
}

mapper.xml文件

<mapper namespace="com.yao.mapper.BlogMapper">

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

<!--    动态 SQL
        1. if
        2.choose
        3.trim
        4.foreach
 -->

<!--    SQL 片段:就是将一些公共的sql抽取出来-->
    <sql id="title-author">
        <if test="title != null">
            and title=#{title}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </sql>

    <select id="queryBlog" parameterType="map" resultType="blog">
        select * from blog
        <where>

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

        <include refid="title-author"></include>

        </where>
    </select>

    <select id="queryBlogChoose" resultType="blog" parameterType="map">
        select * from blog
        <where>
            <choose>
                <when test="title != null">
                    and title=#{title}
                </when>
                <when test="author != null">
                    and author=#{author}
                </when>
                <otherwise>
                    and views=#{views}
                </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>
    </update>

    <select id="queryForeach" parameterType="map" resultType="blog">
        select * from blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="and">
                id=#{id}
            </foreach>
        </where>
    </select>

</mapper>

编写测试类 测试

9.0 MyBatis 缓存

MyBatis缓存

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

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

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

一级缓存

一级缓存也叫本地缓存:

  • 与数据库同一次会话期间查询到的数据会放在本地缓存。
  • 以后获取相同的数据直接存缓存中拿。

测试

mapper接口

public interface UserMapper {

//查询一个用户
    User queryById(@Param("id") Integer id);

// 修改用户
    int updateUser(User user);

}

mapper.xml

    <select id="queryById" resultType="user">
        select * from users
        <where>
            and id = #{id}
        </where>
    </select>

测试

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

一级缓存失效的情况

  1. SqlSession不同 每个SqlSession中的缓存相互独立

  2. SqlSession相同,查询条件不同

  3. SqlSession相同, 手动清除一级缓存

       session.clearCache();//手动清除缓存
    
  4. 执行增删改会刷新缓存

二级缓存

  1. 开启全局缓存【mybatis-conf.xml】

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

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

结论

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_子栖_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值