Mybatis

简介

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架
  • 它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis。
  • 2013年11月迁移到Github。

如何获取MyBatis

持久化

  • 持久化就是将程序的数据在持久状态和顺势状态转化的过程
  • 内存:断电即失
  • 数据库(jdbc),io文件持久化

为什么需要持久化?

  • 有一些对象,不能让他丢掉。
  • 内存价格昂贵

持久层

Dao层,Service层,Controller层…

  • 完成持久化工作的代码块
  • 层界限十分明显

为什么需要Mybatis

  • 方便使用,帮助程序猿将数据存入到数据库中
  • 传统的JDBC代码太过复杂,Mybatis进行简化

第一个Mybatis程序

搭建环境

新建数据库

 
  1. DROP TABLE IF EXISTS `user` ;
  2. CREATE TABLE `user`(
  3. `id` INT(20) NOT NULL PRIMARY KEY,
  4. `name` VARCHAR(20) NOT NULL,
  5. `pwd` VARCHAR(50) NOT NULL
  6. )ENGINE=INNODB DEFAULT CHARSET=utf8;
  7. INSERT INTO `user` VALUES
  8. (1,'钱科如','123456'),
  9. (2,'狂神','123456'),
  10. (3,'刘浩汉','123456'),
  11. (4,'赵烨','123456'),
  12. (5,'张凤梅','123456');

新建项目

1.新建一个普通的Maven项目
2.删除src目录
3.导入maven依赖

  • mysql
  • mybatis
  • junit

创建模块

  • 编写mybatis核心配置文件
     
      
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE configuration
    3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
    5. <configuration>
    6. <environments default="development">
    7. <environment id="development">
    8. <transactionManager type="JDBC"/>
    9. <dataSource type="POOLED">
    10. <property name="driver" value="com.mysql.jdbc.Driver"/>
    11. <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
    12. <property name="username" value="root"/>
    13. <property name="password" value="123456"/>
    14. </dataSource>
    15. </environment>
    16. </environments>
    17. <mappers>
    18. <mapper resource="com/guan/dao/UserMapper.xml"></mapper>
    19. </mappers>
    20. </configuration>
  • 编写mybatis工具类(读取mybatis-config资源,并将方法封装成类,避免每次重复读取)

     
      
    1. //sqlSessionFactory----construct---->sqlSession
    2. public class MybatisUtils {
    3. private static SqlSessionFactory sqlSessionFactory;
    4. static {
    5. //使用Mybatis第一步,获取sqlSessionFactory对象
    6. try {
    7. String resource = "mybatis-config.xml";
    8. InputStream inputStream = Resources.getResourceAsStream(resource);
    9. sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    10. } catch (IOException e) {
    11. e.printStackTrace();
    12. }
    13. }
    14. //既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例
    15. //SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
    16. //你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
    17. public static SqlSession getSqlSession() {
    18. return sqlSessionFactory.openSession();
    19. }
    20. }

编写代码

  • 实体类

     
      
    1. public class User {
    2. private int id;
    3. private String name;
    4. private String pwd;
    5. public User() {}
    6. public User(int id, String name, String pwd) {
    7. this.id = id;
    8. this.name = name;
    9. this.pwd = pwd;
    10. }
    11. public int getId() {
    12. return id;
    13. }
    14. public void setId(int id) {
    15. this.id = id;
    16. }
    17. public String getName() {
    18. return name;
    19. }
    20. public void setName(String name) {
    21. this.name = name;
    22. }
    23. public String getPwd() {
    24. return pwd;
    25. }
    26. public void setPwd(String pwd) {
    27. this.pwd = pwd;
    28. }
    29. @Override
    30. public String toString() {
    31. return "User{" +
    32. "id=" + id +
    33. ", name='" + name + '\'' +
    34. ", pwd='" + pwd + '\'' +
    35. '}';
    36. }
    37. }
  • Dao接口
     
      
    1. public interface UserDao {
    2. List<User> getUserList();
    3. }
  • 接口实现类(由原来的Impl转变为一个Mapper配置文件)
     
      
    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.guan.dao.UserDao">
    6. <select id="getUserList" resultType="com.guan.pojo.User">
    7. select *
    8. from `mybatis`.`user`;
    9. </select>
    10. </mapper>

测试

注意点:
Type interface com.guan.dao.UserDao is not known to the MapperRegistry
MapperRegistry:每一个Mapper.xml都需要在Mybatis核心配置文件中注册

maven配置文件到处问题
由于maven约定大于配置,可能遇到配置文件无法被导出或生效,结局方案

 
 
  1. <build>
  2. <resources>
  3. <resource>
  4. <directory>src/main/resources</directory>
  5. <includes>
  6. <include>**/*.properties</include>
  7. <include>**/*.xml</include>
  8. </includes>
  9. <filtering>true</filtering>
  10. </resource>
  11. <resource>
  12. <directory>src/main/java</directory>
  13. <includes>
  14. <include>**/*.properties</include>
  15. <include>**/*.xml</include>
  16. </includes>
  17. <filtering>true</filtering>
  18. </resource>
  19. </resources>
  20. </build>
  • junit测试
 
  1. public class UserDaoTest {
  2. @Test
  3. public void test() {
  4. //获取sqlSession对象
  5. SqlSession sqlSession = MybatisUtils.getSqlSession();
  6. //方式一:执行SQL
  7. UserDao mapper = sqlSession.getMapper(UserDao.class);
  8. List<User> userList = mapper.getUserList();
  9. for (User user : userList) {
  10. System.out.println(user);
  11. }
  12. //关闭SqlSession
  13. sqlSession.close();
  14. }
  15. }

CRUD

1.namespace
namespace中的包名要和Mapper接口的包名保持一致
2.select
选择,查询语句

  • id:对应的namespace中的方法名
  • resultType:Sql语句执行的返回值
  • parameterType:传入参数类型

1.编写接口
2.编写对应的mapper中的sql语句
3.测试
3.insert

 
  1. <insert id="insertUser" parameterType="com.guan.pojo.User">
  2. insert into `mybatis`.`user` values (#{id},#{name},#{pwd})
  3. </insert>

4.update

 
  1. <update id="updateUser" parameterType="com.guan.pojo.User">
  2. update `mybatis`.`user`
  3. set name = #{name},pwd = #{pwd}
  4. where id = #{id};
  5. </update>

5.delete

 
  1. <delete id="deleteUser" parameterType="int">
  2. delete
  3. from `mybatis`.`user`
  4. where id = #{id};
  5. </delete>

万能Map

假设实体类,或者数据库中的表,字段或者参数过多,我们应当考虑使用Map!

User insertUser2(Map<String,Object> map);

 
  1. <insert id="insertUser2" parameterType="map">
  2. insert into `mybatis`.`user` values (#{userID},#{userName},#{password})
  3. </insert>
 
  1. public void insertUser2() {
  2. SqlSession sqlSession = MybatisUtils.getSqlSession();
  3. UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  4. Map<String, Object> map = new HashMap<String, Object>();
  5. map.put("userID",7);
  6. map.put("userName","李航");
  7. map.put("password","123456");
  8. mapper.insertUser2(map);
  9. sqlSession.commit();
  10. sqlSession.close();
  11. }

模糊查询

在sql拼接中使用通配符!

 
  1. <select id="vagueGetUser" resultType="com.guan.pojo.User" parameterType="String">
  2. select *
  3. from `mybatis`.`user`
  4. where name like "%"#{value}"%";
  5. </select>

配置解析

核心配置文件

  • mybatis-config.xml
  • MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息
 
  1. properties(属性)
  2. settings(设置)
  3. typeAliases(类型别名)
  4. typeHandlers(类型处理器)
  5. objectFactory(对象工厂)
  6. plugins(插件)
  7. environments(环境配置)
  8. environment(环境变量)
  9. transactionManager(事务管理器)
  10. dataSource(数据源)
  11. databaseIdProvider(数据库厂商标识)
  12. mappers(映射器)

环境配置(environments)

MyBatis可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
Mybatis默认的事务管理器为JDBC,连接池POOLED

属性(properties)

我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置且可以进行动态替换,既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。[db.properties
]
编写一个配置文件

 
  1. diver = com.mysql.cj.jdbc.Driver
  2. url = jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8
  3. name = root
  4. password = 123456

在核心配置中引入

 
  1. <properties resource="db.properties">
  2. <property name="password" value="123456"/>
  3. </properties>
  • 可以直接引入外部文件
  • 可以在其中增加属性配置
  • 同一字段引用顺序:
    • 元素体内指定属性
    • 外部文件属性
    • properties方法体内参数

类型别名(typeAliases)

  • 类型别名可为 Java 类型设置一个缩写名字。
  • 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
 
  1. <typeAliases>
  2. <typeAlias type="com.guan.pojo.User" alias="User"></typeAlias>
  3. </typeAliases>

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

 
  1. <typeAliases>
  2. <package name="com.guan.pojo"/>
  3. </typeAliases>

扫描实体类的包,默认别名为这个类的类名

实体类较少使用第一种,实体类较多使用第二种,也可以在实体类上添加注解:@Alias(“User”)

设置

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

映射器(mapper)

MapperRegistry:注册绑定Mapper文件
方式一:[推荐使用]

 
  1. <mappers>
  2. <mapper resource="com/guan/dao/UserMapper.xml"/>
  3. </mappers>

方式二:

 
  1. <mappers>
  2. <mapper class="com.guan.dao.UserMapper"></mapper>
  3. </mappers>

注意点:

  • 接口和Mapper配置文件必须同名!
  • 接口和Mapper配置文件必须在同一个包下!

方式三:

 
  1. <mappers>
  2. <package name="com.guan.dao"/>
  3. </mappers>

注意点:

  • 接口和Mapper配置文件必须同名!
  • 接口和Mapper配置文件必须在同一个包下!

生命周期和作用域

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


SqlSessionFactoryBuilder:

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

SqlSessionFactory:

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

SqlSession:

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


这里面的每一个Mapper,都代表了一个具体的业务

映射

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

制造问题

数据库中的字段:


新建一个项目,测试实体类字段不一致的情况


测试出现问题
User{id=3, name='刘浩汉', password='null'}

解决方法

  • 起别名
     
      
    1. <select id="getUserById" resultType="User" parameterType="int">
    2. select id,name,pwd as password
    3. from `mybatis`.`user`
    4. where id = #{id};
    5. </select>

    resultMap

    结果集映射
     
      
    1. <!--结果集映射-->
    2. <resultMap id="UserMap" type="User">
    3. <!--column数据库中的字段,property实体类中的属性-->
    4. <result column="pwd" property="password"></result>
    5. </resultMap>
  • resultMap 元素是 MyBatis 中最重要最强大的元素
  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
  • ResultMap 的优秀之处——你完全可以不用显式地配置它们

日志

日志工厂

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

  • SLF4J
  • LOG4J [掌握]
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING [掌握]
  • NO_LOGGING

在Mybatis中具体使用哪个日志,在设置中设定
STDOUT_LOGGING标准日志输出
在Mybatis核心配置文件中,配置我们的日志

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

Log4j

什么是Log4j?

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
  • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
    1.导入Maven包
     
      
    1. <dependency>
    2. <groupId>log4j</groupId>
    3. <artifactId>log4j</artifactId>
    4. <version>1.2.17</version>
    5. </dependency>
    2.log4j.properties
 
  1. #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
  2. log4j.rootLogger=DEBUG,console,file
  3. #控制台输出的相关设置
  4. log4j.appender.console = org.apache.log4j.ConsoleAppender
  5. log4j.appender.console.Target = System.out
  6. log4j.appender.console.Threshold=DEBUG
  7. log4j.appender.console.layout = org.apache.log4j.PatternLayout
  8. log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
  9. #文件输出的相关设置
  10. log4j.appender.file = org.apache.log4j.RollingFileAppender
  11. log4j.appender.file.File=./log/guan.log
  12. log4j.appender.file.MaxFileSize=10mb
  13. log4j.appender.file.Threshold=DEBUG
  14. log4j.appender.file.layout=org.apache.log4j.PatternLayout
  15. log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
  16. #日志输出级别
  17. log4j.logger.org.mybatis=DEBUG
  18. log4j.logger.java.sql=DEBUG
  19. log4j.logger.java.sql.Statement=DEBUG
  20. log4j.logger.java.sql.ResultSet=DEBUG
  21. log4j.logger.java.sql.PreparedStatement=DEBUG

3.配置log4j为日志的实现

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

4.Log4j的使用

简单使用
1.在要使用Log4j的类中,导入包Logger.getLogger(UserMapperTest.class);
2.日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);

3.日志级别

 
  1. public void TestLog4j() {
  2. logger.info("info:进入了testLog4j");
  3. logger.debug("debug:进入了testLog4j");
  4. logger.error("error:进入了testLog4j");
  5. }

分页

思考:为什么要分页?

  • 减少数据的处理量

使用Limit分页

 
  1. SELECT & FROM 表 LIMIT startIndex,pageSize

使用Mybatis实现分页,核心SQL
1.接口
List<User> getUserByLimit(Map<String,Object> map);

2.Mapper.xml

 
  1. <select id="getUserByLimit" resultMap="UserMap" resultType="User" parameterType="map">
  2. select *
  3. from `mybatis`.`user` limit #{startIndex},#{pageSize};
  4. </select>

3.测试

 
  1. public void getUserByLimit() {
  2. SqlSession sqlSession = MybatisUtils.getSqlSession();
  3. UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  4. HashMap<String, Object> map = new HashMap<>();
  5. map.put("startIndex",1);
  6. map.put("pageSize",2);
  7. List<User> user = mapper.getUserByLimit(map);
  8. for (User user1 : user) {
  9. System.out.println(user1);
  10. }
  11. sqlSession.close();
  12. }

注解

使用注解开发

1.注解在接口上实现

 
  1. @Select("select * from `user`")
  2. List<User> getUsers();

2.需要在核心配置文件中绑定接口

 
  1. <mappers>
  2. <mapper class="com.guan.dao.UserMapper"></mapper>
  3. </mappers>

3.测试
本质:反射机制代理
底层:动态代理


Mybatis详细的执行流程

CRUD

我们可以在工具类创建的时候实现自动提交事务
public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(true); }

1.编写接口,增加注释

 
  1. @Select("select * from `user`")
  2. List<User> getUsers();
  3. @Insert("insert into `user` values(#{id},#{name},#{pwd})")
  4. void insertUser(@Param("id") int id,@Param("name") String name,@Param("pwd") String pwd);

2.测试

关于@Param()注解

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

#{}和${}区别
preparedStatement和Statement的区别,#{}可以防止sql注入

Lombok

使用步骤:
1.在IDEA中安装
2.导入Maven包
3.在实体类上编写注解


说明:

 
  1. @Data:无参构造,get,set,toString,hashcode,equals
  2. @AllArgsConstructor:有参构造
  3. @NoArgsConstructor:无参构造

多对一

  • 多个学生,对应一个老师
  • 对于学生这边,关联..多个学生,关联一个老师
  • 对于老师而言,集合…一个老师,有很多个学生

创建数据库

 
  1. CREATE TABLE `teacher` (
  2. `id` INT(10) NOT NULL,
  3. `name` VARCHAR(30) DEFAULT NULL,
  4. PRIMARY KEY (`id`)
  5. ) ENGINE=INNODB DEFAULT CHARSET=utf8
  6. INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');
  7. CREATE TABLE `student` (
  8. `id` INT(10) NOT NULL,
  9. `name` VARCHAR(30) DEFAULT NULL,
  10. `tid` INT(10) DEFAULT NULL,
  11. PRIMARY KEY (`id`),
  12. KEY `fktid` (`tid`),
  13. CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
  14. ) ENGINE=INNODB DEFAULT CHARSET=utf8
  15. INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, '小明', 1);
  16. INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1);
  17. INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1);
  18. INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1);
  19. INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);

测试环境搭建
1.导入lombok
2.新建实体类Teacher,Student
3.建立Mapper接口
4.建立Mapper.xml文件
5.在核心配置文件中绑定注册Mapper接口或文件
6.测试查询是否成功

按照查询嵌套处理

 
  1. <select id="getStudent" resultType="Student" resultMap="StuCombTea">
  2. select * from `student`
  3. </select>
  4. <resultMap id="StuCombTea" type="Student">
  5. <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association>
  6. </resultMap>
  7. <select id="getTeacher" resultType="Teacher">
  8. select *
  9. from `teacher` where id = #{tid};
  10. </select>

按照结果嵌套处理

 
  1. <select id="getStudent" resultMap="StuCombTea">
  2. select s.id sid,s.name sname,t.name tname
  3. from `student` as s,`teacher` as t
  4. where s.tid = t.id;
  5. </select>
  6. <resultMap id="StuCombTea" type="Student">
  7. <result property="id" column="sid"></result>
  8. <result property="name" column="sname"/>
  9. <association property="teacher" javaType="Teacher">
  10. <result property="name" column="tname"/>
  11. </association>
  12. </resultMap>

回顾Mysql多对一查询

  • 子查询
  • 连表查询

一对多

1.环境搭建
老师的实体类

 
  1. public class Teacher {
  2. private int id;
  3. private String name;
  4. //一个老师拥有多个学生
  5. private List<Student> students;
  6. }

按照结果查询

 
  1. <select id="getTeacher" resultMap="Combine">
  2. select s.id sid,s.name sname,t.name tname,t.id tid
  3. from `teacher` t,`student` s
  4. where t.id = s.tid and t.id = #{id};
  5. </select>
  6. <resultMap id="Combine" type="Teacher">
  7. <result property="id" column="tid"/>
  8. <result property="name" column="tname"/>
  9. <collection property="students" ofType="Student">
  10. <result property="id" column="sid"/>
  11. <result property="name" column="sname"/>
  12. <result property="tid" column="tid"/>
  13. </collection>
  14. </resultMap>

动态SQL

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

 
  1. 借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
  2. if
  3. choose (when, otherwise)
  4. trim (where, set)
  5. foreach

搭建环境

数据库搭建

 
  1. CREATE TABLE `blog`(
  2. `id` VARCHAR(50) NOT NULL COMMENT '博客id',
  3. `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
  4. `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
  5. `create_time` DATETIME NOT NULL COMMENT '创建时间',
  6. `views` INT(30) NOT NULL COMMENT '浏览量'
  7. )ENGINE=INNODB DEFAULT CHARSET=utf8

创建一个基础工程(Mybatis-08)
1.导包
2.编写配置文件
3.编写实体类


4.编写实体类对应Mapper接口和Mapper.xml文件

生成随机id

生成随机id

 
 
  1. public class IdUtils {
  2. public static String getId() {
  3. return UUID.randomUUID().toString().replaceAll("-","");
  4. }
  5. @Test
  6. public void test() {
  7. System.out.println(IdUtils.getId());
  8. }
  9. }

IF

接口

 
  1. //查询博客(输入标题查指定,输入作者查指定作者的所有博客,不输入查所有博客)
  2. List<Blog> queryBlogIf(Map map);

xml文件

 
  1. <select id="queryBlogIf" resultType="Blog" parameterType="map">
  2. select *
  3. from `mybatis`.`blog` where 1 = 1
  4. <if test="title != null">
  5. and title = #{title};
  6. </if>
  7. <if test="author != null">
  8. and author = #{author};
  9. </if>
  10. <if test="views != null">
  11. and views > #{views};
  12. </if>
  13. </select>

测试类

 
  1. public void queryBlogIF() {
  2. SqlSession sqlSession = MybatisUtils.getSqlSession();
  3. BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
  4. HashMap map = new HashMap();
  5. map.put("views",5000);
  6. List<Blog> blogs = mapper.queryBlogIf(map);
  7. for (Blog blog : blogs) {
  8. System.out.println(blog);
  9. }
  10. sqlSession.close();
  11. }

choose(when,otherwise)

 
  1. <select id="queryBlogChoose" resultType="Blog" parameterType="map">
  2. select *
  3. from `mybatis`.blog
  4. <where>
  5. <choose>
  6. <when test="title != null">
  7. and title = #{title}
  8. </when>
  9. <when test="author != null">
  10. and author = #{author}
  11. </when>
  12. <otherwise>
  13. and views = #{views}
  14. </otherwise>
  15. </choose>
  16. </where>
  17. </select>

trim(where,set)

 
  1. <update id="updateBlog" parameterType="map">
  2. update `mybatis`.blog
  3. <set>
  4. <if test="title != null">
  5. title = #{title},
  6. </if>
  7. <if test="author != null">
  8. author = #{author}
  9. </if>
  10. </set>
  11. where id = #{id};
  12. </update>

SQL片段

有的时候,我们可能会讲一些功能抽取出来,方便复用
1.使用SQL标签抽取公共的部分

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

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

 
  1. <select id="queryBlogIf" resultType="Blog" parameterType="map">
  2. select *
  3. from `mybatis`.`blog`
  4. <where>
  5. <include refid="if-title-test"></include>
  6. </where>
  7. </select>

注意事项

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

Foreach


xml文件

 
  1. <select id="queryBlogForeach" parameterType="map" resultType="Blog">
  2. ##select * from `mybatis`.blog where 1=1 and (id=1 or id=2 or id=3)
  3. select *
  4. from `mybatis`.blog
  5. <where>
  6. <foreach collection="ids" item="id" open="and (" close=")" separator="or">
  7. id = #{id}
  8. </foreach>
  9. </where>
  10. </select>

测试类

 
  1. public void queryBlogForeach() {
  2. SqlSession sqlSession = MybatisUtils.getSqlSession();
  3. BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
  4. HashMap map = new HashMap();
  5. ArrayList<String> ids = new ArrayList<>();
  6. ids.add("b5acce451fa1481e90acaad1dcdfa994");
  7. map.put("ids",ids);
  8. List<Blog> blogs = mapper.queryBlogForeach(map);
  9. for (Blog blog : blogs) {
  10. System.out.println(blog);
  11. }
  12. sqlSession.commit();
  13. sqlSession.close();
  14. }

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

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

缓存

简介

查询:连接数据库,消耗资源!

  • 一次查询的结果,暂存在一个可以直接取到的地方!—> 内存:缓存
  • 再次查询相同数据的时候,直接走缓存,不用走数据库

1.什么是缓存?

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

Mybatis缓存

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

一级缓存

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

测试步骤:
1.开启日志
2.测试在一个Session中查询两次相同记录

缓存失效情况:
1.查询不同记录
2.增删改操作,可能改变原来数据,必定会刷新缓存
3.手动清理缓存

二级缓存

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

步骤:
1.开启缓存

 
  1. 开启全局缓存
  2. <setting name="cacheEnable" value="true"/>

2.在要使用二级缓存的Mapper中开启

 
  1. <cache
  2. eviction="FIFO"
  3. flushInterval="60000"
  4. size="512"
  5. readOnly="true"/>

3.测试

小结

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

Mybatis缓存原理

自定义缓存Ehcache

Ehcache是一种广泛使用的开源Java分布式缓存,主要面向通用缓存
要在程序中使用ehcacche,要先导包

 
  1. <dependency>
  2. <groupId>org.mybatis.caches</groupId>
  3. <artifactId>mybatis-ehcache</artifactId>
  4. <version>1.2.1</version>
  5. </dependency>

ehcache.xml

 
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
  4. updateCheck="false">
  5. <diskStore path="./tmpdir/Tmp_EhCache"/>
  6. <defaultCache
  7. eternal="false"
  8. maxElementsInMemory="10000"
  9. overflowToDisk="false"
  10. diskPersistent="false"
  11. timeToIdleSeconds="1800"
  12. timeToLiveSeconds="259200"
  13. memoryStoreEvictionPolicy="LRU"/>
  14. <cache
  15. name="cloud_user"
  16. eternal="false"
  17. maxElementsInMemory="5000"
  18. overflowToDisk="false"
  19. diskPersistent="false"
  20. timeToIdleSeconds="1800"
  21. timeToLiveSeconds="1800"
  22. memoryStoreEvictionPolicy="LRU"/>
  23. </ehcache>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值