05mybatis

一.认识MyBatis

1)Mybatis是什么

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

2)ORM是什么

对象-关系映射(OBJECT/RELATIONALMAPPING,简称ORM),是随着面向对象的软件开发方法发展而产生的。用来把对象模型表示的对象映射到基于SQL 的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作实体对象的属性和方法 。ORM 技术是在对象和关系之间提供了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化。

3)ORM框架和MyBatis的区别

对比项MybatisHibernate
市场占有率
适合的行业互联网 电商 项目传统的(ERP CRM OA)
性能
Sql灵活性
学习门槛
Sql配置文件全局配置文件、映射文件全局配置文件、映射文件
ORM半自动化完全的自动化
数据库无关性

4)设计模式的类型

总体来说,设计模式分为三类23种

  • 创建型(5种) : 工厂模式、抽象工厂模式、单例模式、原型模式、构建者模式
  • 结构型(7种):适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式
  • 行为型(11种):模板方法模式、策略模式、观察者模式、中介者模式、状态模式、责任链模式、命令模式、迭代器模式、访问者模式、解释器模式、备忘录模式

二.应用篇

1)mapper代理开发方式

代理分为静态代理动态代理。此处先不说静态代理,因为Mybatis中使用的代理方式是动态代理。动态代理分为两种方式:

  • 基于JDK的动态代理–针对有接口的类进行动态代理
  • 基于CGLIB的动态代理–通过子类继承父类的方式去进行代理。

2)自增主键新增时,返回id

  • 注解方式

    /*
    * statement="select max(empNo)+1 as myNo from emp":表示定义的子查询语句
    * before=true:表示在之前执行,booler类型的,所以为true
    * keyColumn="myNo":表示查询所返回的类名
    * resultType=int.class:表示返回值得类型
    * keyProperty="empNo" :表示将该查询的属性设置到某个列中,此处设置到empNo中
    * 注意:使用MySQL的last_insert_id()函数时,before必为false,也就是说必须先插入然后执行last_insert_id()才能获得刚刚插入数据的ID
    */
    @Insert("insert into people(name) values(#{name})")
    @SelectKey(statement = "SELECT LAST_INSERT_ID()",keyColumn = "id", keyProperty = "id",
               resultType = Integer.class, before = false)
    void insertPeople(People people);
    
  • xml方式

    <insert id="insertPeople1">
        <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="integer">
            select last_insert_id()
        </selectKey>
        insert into people(name) values(#{name})
    </insert>
    

3)一对一,一对多查询

一对一

  • 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.monkey.mybatis.dao.OrderDao">
    <resultMap id="oneForOne" type="com.monkey.mybatis.dto.OneForOneDTO">
        <id column="p_id" property="pId"/>
        <result column="order_name" property="orderName"/>
        <result column="order_money" property="orderMoney"/>
        <association property="people" javaType="com.monkey.mybatis.model.People">
            <id column="id" property="id"/>
            <result column="name" property="name"/>
        </association>
    </resultMap>

    <select id="selectOrder" resultType="com.monkey.mybatis.dto.OneForOneDTO" resultMap="oneForOne">
        select
            a.p_id,
            a.order_name,
            a.order_money,
            b.id,
            b.name
        from `order` a
        left join people b on a.p_id=b.id
    </select>
</mapper>
  • 注解

@Mapper
public interface OrderDao {
    @Select("        select\n" +
            "            a.p_id,\n" +
            "            a.order_name,\n" +
            "            a.order_money,\n" +
            "            b.id,\n" +
            "            b.name\n" +
            "        from `order` a\n" +
            "        left join people b on a.p_id=b.id")
    @Results(value = {
            @Result(property = "pId", column = "p_id", id = true),
            @Result(property = "orderName", column = "order_name"),
            @Result(property = "orderMoney", column = "order_money"),
            @Result(property = "people",  one = @One(select = "selectPeople"), column = "id"),
    })
    List<OneForOneDTO> selectOrder1();

    @Select("select * from people where id=#{id}")
    People selectPeople(int id);
}

一对多

  • 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.monkey.mybatis.dao.PeopleDao">
    <resultMap id="oneForSome" type="com.monkey.mybatis.dto.OneForSomeDTO">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <collection property="orders" ofType="com.monkey.mybatis.model.Order">
            <result column="order_name" property="orderName"/>
            <result column="order_money" property="orderMoney"/>
        </collection>
    </resultMap>

    <select id="selectPeople" resultType="com.monkey.mybatis.model.People" resultMap="oneForSome">
        select
            a.id,
            a.name,
            b.order_name as order_name,
            b.order_money as order_money
        from people a
        left join `order` b on b.p_id=a.id
    </select>
</mapper>
  • 注解
@Mapper
public interface PeopleDao {
    @Select("select id,name from people")
    @Results(value = {
            @Result(property = "id", column = "id", id = true),
            @Result(property = "name", column = "name"),
            @Result(property = "orders", javaType = List.class,
                    many = @Many(select = "selectOrder"), column = "id"),
    })
    List<OneForSomeDTO> selectPeople1();

    @Select("select p_id as pId, order_name as orderName,order_money as orderMoney 
            from `order` where p_id = #{id}")
    List<Order> selectOrder(@Param("id") int id);
}

4)#{}和${}区别

  • 区别1
#{}  :相当于JDBC SQL语句中的占位符? (PreparedStatement)
${}  : 相当于JDBC SQL语句中的连接符合 + (Statement)
  • 区别2
#{}  : 进行输入映射的时候,会对参数进行类型解析(如果是String类型,那么SQL语句会自动加上’’)
${}  :  进行输入映射的时候,将参数原样输出到SQL语句中
  • 区别3
#{}  : 如果进行简单类型(String、Date、8种基本类型的包装类)的输入映射时,#{}中参数名称可以任意
${}  :  如果进行简单类型(String、Date、8种基本类型的包装类)的输入映射时,${}中参数名称必须是value
  • 区别4
${} : 存在SQL注入问题 ,使用OR 1=1 关键字将查询条件忽略

5)延迟加载

1. 什么是延迟加载

  • MyBatis中的延迟加载,也称为懒加载,是指在进行关联查询时,按照设置延迟规则推迟对关联对象的select查询。延迟加载可以有效的减少数据库压力
  • Mybatis的延迟加载,需要通过resultMap标签中的associationcollection子标签才能演示成功。
  • Mybatis的延迟加载,也被称为是嵌套查询,对应的还有嵌套结果的概念,可以参考一对多关联的案例。
  • 注意:MyBatis的延迟加载只是对关联对象的查询有延迟设置,对于主加载对象都是直接执行查询语句的。

2. 延迟加载的分类

  • 延迟加载的分类
    MyBatis根据对关联对象查询的select语句的执行时机,分为三种类型:直接加载、侵入式加载与深度延迟加载
    1.直接加载: 执行完对主加载对象的select语句,马上执行对关联对象的select查询。
    2.侵入式延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的某个属性(该属性不是关联对象的属性)时,就会马上执行关联对象的select查询
    3.深度延迟:执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的select查询。

    延迟加载策略需要在Mybatis的全局配置文件中,通过标签进行设置。

3. 代码

  • 直接加载
<settings>
    <!-- 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="false"/>
</settings>
  • 侵入式加载
<settings>
    <!-- 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 侵入式延迟加载开关 -->
    <setting name="aggressiveLazyLoading" value="true"/>
</settings>
  • 深度延迟加载
<settings>
    <!-- 延迟加载总开关 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 侵入式延迟加载开关 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

4.N+1问题

  • 深度延迟加载的使用会提升性能
  • 如果延迟加载的表数据太多,此时会产生N+1问题,主信息加载一次算1次,而从信息是会根据主信息传递过来的条件,去查询从表多次

6)mybatis缓存

  • 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
  • 二级缓存是**Mapper(namespace)**级别的缓存。多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。

一级缓存

Mybatis默认开启了一级缓存

1. 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据
库查询用户信息,将查询到的用户信息存储到一级缓存中。
2. 如果中间sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓
存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
3. 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从
缓存中获取用户信息。

二级缓存

1. 第一次调用mapper下的SQL去查询用户信息。查询到的信息会存到该mapper对应的二级缓存区
域内。
2. 第二次调用相同namespace下的mapper映射文件中相同的SQL去查询用户信息。会去对应的二级
缓存内取结果。
3. 如果调用相同namespace下的mapper映射文件中的增删改SQL,并执行了commit操作。此时会
清空该namespace下的二级缓存。
  • 开启二级缓存
<!-- 配置文件中,开启二级缓存总开关 -->
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
<!-- 开启本mapper下的namespace的二级缓存,默认使用的是mybatis提供的PerpetualCache -->
<cache></cache>
  • 实现序列化
由于二级缓存的数据不一定都是存储到内存中,它的存储介质多种多样,比如说存储到文件系统中,所以需要给缓存的对象执行序列化。

7)动态SQL

1.if标签

<select id="findUserList" parameterType="queryVo" resultType="user">
	SELECT * FROM user where 1=1
	<if test="user != null">
		<if test="user.username != null and user.username != ''">
			AND username like '%${user.username}%'
		</if>
	</if>
</select>

2.where标签

<select id="findUserList" parameterType="queryVo" resultType="user">
	SELECT * FROM user
	<!-- where标签会处理它后面的第一个and -->
		<where>
			<if test="user != null">
				<if test="user.username != null and user.username != ''">
					AND username like '%${user.username}%'
				</if>
			</if>
		</where>
</select>

3.sql片段

在映射文件中可使用sql标签将重复的sql提取出来,然后使用include标签引用即可,最终达到sql重用的目的

  • 将where条件抽取出来
<sql id="query_user_where">
        <if test="user != null">
            <if test="user.username != null and user.username != ''">
                AND username like '%${user.username}%'
            </if>
        </if>
</sql>
  • 使用include引用
<!-- 使用包装类型查询用户 使用ognl从对象中取属性值,如果是包装对象可以使用.操作符来取内容部的属性 -->
<select id="findUserList" parameterType="queryVo" resultType="user">
    SELECT * FROM user
    <!-- where标签会处理它后面的第一个and -->
    <where>
        <include refid="query_user_where"></include>
    </where>
</select>

注意:

如果引用其它mapper.xml的sql片段,则在引用时需要加上namespace

<include refid="namespace.sql片段”/>

4.foreach标签

<sql id="query_user_where">
    <if test="ids != null and ids.size() > 0">
        <!-- collection:指定输入的集合参数的参数名称 -->
        <!-- item:声明集合参数中的元素变量名 -->
        <!-- open:集合遍历时,需要拼接到遍历sql语句的前面 -->
        <!-- close:集合遍历时,需要拼接到遍历sql语句的后面 -->
        <!-- separator:集合遍历时,需要拼接到遍历sql语句之间的分隔符号 -->
        <foreach collection="ids" item="id" open=" AND id IN ( "
                 close=" ) " separator=",">
            #{id}
        </foreach>
    </if>
</sql>

8)逆向工程

  • generatorConfig.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <context id="testTables" targetRuntime="MyBatis3">
        <commentGenerator>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
            connectionURL="jdbc:mysql://localhost:3306/ssm" userId="root" password="root">
        </jdbcConnection>
        <!-- <jdbcConnection driverClass="oracle.jdbc.OracleDriver" 
			connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg" 
            userId="yycg" password="yycg"> </jdbcConnection> -->
        <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 
            和 NUMERIC 类型解析为java.math.BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!-- targetProject:生成PO类的位置 -->
        <javaModelGenerator targetPackage="com.kkb.ms.po"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
            <!-- 从数据库返回的值被清理前后的空格 -->
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- targetProject:mapper映射文件生成的位置 -->
        <sqlMapGenerator targetPackage="com.kkb.ms.mapper"
            targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- targetPackage:mapper接口生成的位置 -->
        <javaClientGenerator type="XMLMAPPER"
            targetPackage="com.kkb.ms.mapper" targetProject=".\src">
            <!-- enableSubPackages:是否让schema作为包的后缀 -->
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- 指定数据库表 -->
        <table schema="" tableName="user"></table>
        <table schema="" tableName="order"></table>
    </context>
</generatorConfiguration>

注意事项

每次执行逆向工程代码之前,先删除原来已经生成的mapper xml文件再进行生成

  • mapper.xml文件的内容不是被覆盖而是进行内容追加,会导致mybatis解析失败。
  • po类及mapper.java文件的内容是直接覆盖没有此问题。

9)PageHelper分页插件

  • 添加依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.6</version>
</dependency>
  • 项目中使用PageHelper
//获取第1页,10条内容,默认查询总数count
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectAll();
//用PageInfo对结果进行包装
PageInfo page = new PageInfo(list);
//测试PageInfo全部属性
//PageInfo包含了非常全面的分页属性
assertEquals(1, page.getPageNum());
assertEquals(10, page.getPageSize());
assertEquals(1, page.getStartRow());
assertEquals(10, page.getEndRow());
assertEquals(183, page.getTotal());
assertEquals(19, page.getPages());
assertEquals(1, page.getFirstPage());
assertEquals(8, page.getLastPage());
assertEquals(true, page.isFirstPage());
assertEquals(false, page.isLastPage());
assertEquals(false, page.isHasPreviousPage());
assertEquals(true, page.isHasNextPage()

注意事项

1. 需要分页的查询语句,必须是处于PageHelper.startPage(1, 10);后面的第一条语句。
2. 如果查询语句是使用resultMap进行的嵌套结果映射,则无法使用PageHelper进行分页
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值