MyBatis学习笔记

MyBatis

  • mybatis官方文档:https://mybatis.org/mybatis-3/zh/
  • mybatis-spring官方文档:http://mybatis.org/spring/zh/
  • mybatis-spring-boot-autoconfigure
  • 教程:https://how2j.cn/k/mybatis/mybatis-tutorial/1087.html

xml方式使用步骤

1.引入依赖:数据库驱动+mybatis

<dependencies>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
</dependencies>

2.建库建表建项目

3.创建实体类

  • 需要Getter和Setter
  • 实体类不需要指定表名,不需要指定表字段名

4.配置文件mybatis-config.xml

  • 位置:maven工程放在resources目录下
  • 作用:
    • 配置数据库连接信息;
    • 注册XxxMapper.xml(注解方式中是注册XxxMapper.class)
    • 指定实体类的包路径
    • 配置插件
    • 配置二级缓存
  • 标签:
    • properties标签可以引入外部properties文件的内容(例如数据库配置信息),然后通过${key}来引用,这个标签少用
  • 也可以不使用 mybatis-config.xml 而使用Java代码配置类

5.SQL映射文件Xxx.xml

  • 位置:maven工程放在resources目录下

  • 作用:SQL 语句映射

  • 标签:

    • insert,delete,update,select,resultMap,sql,include
  • 输入映射:parameterTypeparameterMap,基本不用指定这两个属性

    • parameterType
      • 基本类型
      • String
      • POJO
      • Map
      • List
      • 数组
    • parameterMap,已废弃
  • 获取参数

    • 一个,直接#{变量名}
    • 多个,(因为多个参数会被封装成一个map,key:param1,param2…paramN,或者参数索引,value:传入的参数值),必须通过#{param1}引用,很直观;解决方法:需要在接口方法上用@Param指定别名,再引用#{别名} ,参考博客1博客2
    • POJO,直接#{属性名}
    • Map: #{key}
  • 输出映射:resultTyperesultMap

    • resultType,一般用于查询返回基本数据类型,或者没有组合其他引用对象的实体类,故一般不用指定
      • insert,delete,update可以返回int,Integer,long,Long,boolean,Boolean,void,MyBatis会根据接口方法的返回类型自动处理
    • resultMap,常用,用来告诉Mybaits如何将返回的查询结果进行封装,使用场景:引用了其他引用对象的实体类,或者属性和字段不一致的实体类,又或者多表查询时多表存在同名字段时需要为同名字段起别名
      • id标签指定主键,属性有column和property
      • result标签指定普通列,属性有column和property
      • collection将关联查询信息映射到一个pojo类中,属性有property和javaType(指定POJO类型)
      • association将关联查询信息映射到一个list集合中,属性有property和ofType(指定集合中元素类型)
      • collection和association中的关联查询可转换为分步查询,使用selectKey指定要查询的SQL语句,并用column指定第一步查询的结果中的哪个列作为参数传递给第二步查询;传递多个参数时,使用column={key1=column1,key2=column2}
      • 分步查询可以实现懒加载,需要在collection或association标签中指定fetchType=lazy,或者mybatis-configa.xml中指定懒加载配置
  • resultType和resultMap区别:博客1博客2

  • ofType和javaType区别:博客

  • select返回一条记录时,可以返回POJO(指定resultType或resultMap);也可以返回Map<String,Object>,指定resultType=“map”

  • select返回多条记录时,可以返回List(指定resultType或resultMap);也可以返回Map<Integer,Emp> ,这里Map的key是主键,Emp是一条记录,resultType=“map”

  • 占位符:#{}解析传递进来的参数数据,${}对传递进来的参数原样拼接在SQL中

  • ${} 有安全风险,不要拼接用户提交的内容。用途举例:分表,动态指定表名

  • #和$区别:博客

6.MyBatis API

  • 看源码

7.CURD

  1. 读取配置文件mybatis-config.xml,InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
  2. 获取全局SqlSessionFactory
  3. 获取当前线程的SqlSession(和Connection一样非线程安全)
  4. 默认事务开启,通过SqlSession执行SQL语句
  5. 提交事务(insert,update,delete需要)
  6. 关闭SqlSession

8.Mapper动态代理👍

  • 接口式编程
  • 可以自定义自己的接口,不使用MyBatis原生接口

原生:Dao ====> DaoImpl

mybatis:Mapper ====> xxxMapper.xml

  • 配置文件Xxx.xml改名为XxxMapper.xml
  • 在mapper包下新建XxxMapper.java接口,接口中的方法名即为XxxMapper.xml中的statement id
  • MyBatis会为接口生成代理对象

Mapper接口开发需要遵循以下规范:

1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。

2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同

4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

  • 动态代理对象调用sqlSession.selectOne()和sqlSession.selectList()是根据mapper接口方法的返回值决定,如果返回list则调用selectList方法,如果返回单个对象则调用selectOne方法。

9.获取自增主键

  • MySQL,insert标签上增加属性useGeneratedKeys="true" keyProperty="id" keyColumn="id"

  • Oracle,自增主键使用序列,insert标签内在insert语句前使用selectKey,先给实体类的id属性赋值

    <selectKey resultType="long" order="BEFORE" keyProperty="deptId">
        select seq_sys_dept.nextval as deptId from dual
    </selectKey>
    

10.模糊查询

  • MySQL:select * from category where name like concat(‘%’,#{0},‘%’)

  • Oracle:select * from category where name like ‘%’||#{0}||‘%’

注解方式使用步骤

  • 在XxxMapper.java接口方法上写SQL语句,@Insert,@Delete,@Update,@Select
  • 高级映射,多表查询,@Results,@Result
  • 动态SQL:SQL类,用Java代码拼接SQL,丑陋!

jdbcType和javaType对应关系

实体关系

一对多

  • 一侧实体类组合一个多侧实体类的list
  • 一侧映射文件resultMap指定一个collection
  • Category中有List<Product>属性
  • resultMap中指定collection

多对一

  • 多侧实体类组合一个一侧实体类对象
  • 多侧映射文件resultMap指定一个association
  • Product中有Category属性
  • resultMap中指定association

多对多

  • 需要一个中间实体类和一张中间表
  • 选一个多侧实体类维护一个中间实体类的list
  • 上述多侧映射文件resultMap指定一个collection,collection中又有association
  • Order中有List<OrderProduct>属性
  • Product中有List<OrderProduct>属性
  • OrderProduct中有Order,Product属性
  • 建立关系,删除关系,查询关系
  • 多对多不存在修改关系的做法,就是删除旧的,然后新增一条即达到修改的效果

动态SQL

ONGL表达式

if 标签

<select id="listProduct" resultType="Product">
    select * from product
    <if test="name!=null">
        where name like concat('%',#{name},'%')
    </if>        
</select>

where 标签

  • 会进行自动判断:如果任何条件都不成立,那么就在sql语句里就不会出现where关键字;如果有任何条件成立,会自动去掉多出来的 and 或者 or
<select id="listProduct" resultType="Product">
	select * from product
	<where>
		<if test="name!=null">
			and name like concat('%',#{name},'%')
		</if>		 	
		<if test="price!=null and price!=0">
			and price > #{price}
		</if>	
	</where>	 	
</select>

set 标签

  • 使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号
<update id="updateProduct" parameterType="Product" >
    update product
    <set>
        <if test="name!=null">name=#{name},</if>
        <if test="price!=null">price=#{price}</if>
    </set>
    where id=#{id}   
</update>

trim标签

  • 可以代替或增强where,set标签的功能
  • 少用

foreach 标签

<select id="listProduct" resultType="Product">
    SELECT * FROM product
    WHERE ID in
    <foreach item="item" index="index" collection="list" open="(" separator="," close=")">
        #{item}
    </foreach>
</select>
  • MySQL批量插入:insert into values
  • Oracle批量插入:insert into select from或者begin 多条插入语句 end;

choose when otherwise标签

  • 条件分支,choose标签下包含when,otherwise标签,when标签中判断

sql、include标签

  • SQL语句复用:sql标签定义要复用的SQL语句,并指定id属性;include标签通过refid指定要引用的已定义的SQL语句

bind标签

  • 对参数进行处理后封装成一个新的参数,例如模糊查询在变量前后加上%

日志

在运行日志中显示SQL语句,下面以log4j为例

  1. 导入依赖
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. 配置log4j.properties
# Global logging configuration
log4j.rootLogger=ERROR, stdout

# MyBatis logging configuration...
# 对映射文件配置日志记录
log4j.logger.com.lee=TRACE
# 包级别
#log4j.logger.com.lee.mapper=TRACE
# 接口级别
#log4j.logger.com.lee.mapper.CategoryMapper=TRACE
# 方法级别
#log4j.logger.com.lee.mapper.addCategory=TRACE

# 如需对 XML 文件记录日志,只要对命名空间增加日志记录功能即可
#log4j.logger.com.lee.mapper.CategoryMapper=TRACE
#log4j.logger.com.lee.mapper.CategoryMapper.addCategory=TRACE

# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

延迟加载

  • 关联查询变为分步查询
  • 需要配置文件mybatis-config.xml

分页

mysql

  • limit offset,size ,手动传入offset,size

oracle

  • ROWNUM

pagehelper插件

  • 官网:https://pagehelper.github.io/
  • 文档:https://pagehelper.github.io/docs/
  • GitHub:https://github.com/pagehelper/Mybatis-PageHelper
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.11</version>
</dependency>
PageHelper.startPage(1, 5);

批量处理

  • 拼接SQL的长度是有限的
  • 解决方法:获取sqlSession时指定BATCH处理器,SqlSession sqlSession =sqlSessionFactory.openSession(ExcutorType.BATCH)
  • SpringBoot,参考博客1博客2
//获取sqlsession
//从spring注入原有的sqlSessionTemplate
@Autowired
private SqlSessionTemplate sqlSessionTemplate;

void batchInsert() {
    // 新获取一个模式为BATCH,自动提交为false的session
    // 如果自动提交设置为true,将无法控制提交的条数,改为最后统一提交,可能导致内存溢出
    SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
    //通过新的session获取mapper
    fooMapper = session.getMapper(FooMapper.class);
    int size = 10000;
    try {
        for (int i = 0; i < size; i++) {
            Foo foo = new Foo();
            foo.setName(String.valueOf(System.currentTimeMillis()));
            fooMapper.insert(foo);
            if (i % 1000 == 0 || i == size - 1) {
                //手动每1000个一提交,提交后无法回滚 
                session.commit();
                //清理缓存,防止溢出
                session.clearCache();
            }
        }
    } catch (Exception e) {
        //没有提交的数据可以回滚
        session.rollback();
    } finally {
        session.close();
    }
}

缓存

一级缓存

  • SqlSession级别,默认开启

  • 原理:通过一个Map来实现同一个sqlsession再次发出相同的sql,就从缓存中取不走数据库。

  • 与Spring整合之后,使用的是Mappper代理对应,一级缓存是失效的!!!

    在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构**(HashMap****)**用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

    在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率

二级缓存

  • SqlSessionFactory级别,要手动开启,在XxxMapper.xml中添加<cache/>标签开启支持,而且使用二级缓存时候必须将实体类序列化。刷新缓存。

  • 二级缓存在Mapper动态代理中是mapper级别,不同的Mapper实例缓存不共享。

  • 作用域是mapper的同一个namespace

  • 实体类要实现序列化接口

  • 避免使用二级缓存(可能会读到脏数据)

    多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

    不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率

整合第三方缓存

  • ehcache

事务管理

  • 少用,一般使用Spring进行事务管理
  • mysql表的类型必须是INNODB

逆向工程

  • MyBatis Generator(了解即可)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值