[框架]MyBatis框架

目录

关于MyBatis框架

MyBatis框架的依赖项

关于Mapper接口

关于@Param注解

关于使用注解来配置SQL语句

关于使用XML来配置SQL语句

MyBatis的缓存机制

关于MyBatis的#{}和${}格式的占位符


 

关于MyBatis框架

MyBatis框架的主要作用:实现并简化数据库编程。

MyBatis框架的依赖项

MyBatis框架的依赖项是:mybatis,但,通常还应该再添加:mybatis-springspring-jdbcmysql或其它数据库的依赖项、数据库连接池的依赖项(commons-dbcp / commons-dbcp2 / Druid / Hikari等)、测试

在Spring Boot项目中,只需要添加mybatis-spring-boot-starter即可包含mybatismybatis-springspring-jdbc、默认的数据库连接池。

关于Mapper接口

使用MyBatis时,需要使用Mapper接口来封装访问数据的抽象方法,这些接口可以自行放在任何位置,但需要通过注解来指定,使得MyBatis框架能找到这些接口!可以:

  • 【推荐】在配置类上使用@MapperScan注解,来配置Mapper接口所在的根包(其子孙级包中的Mapper接口也会被找到)
    • 注意:配置的包下,不能有Mapper接口以外的其它接口

  • 【不推荐】在各Mapper接口上添加@Mapper注解

 所有的数据访问功能都应该在接口中定义抽象方法,关于抽象方法:

  •  返回值类型:如果需要执行的SQL语句是增、删、改类型的,应该使用`int`作为返回值类型,表示“受影响的行数”,其实也可以使用`void`,表示不关心受影响的行数,但不推荐;如果需要执行的SQL语句是查询类型的,只需要保证声明的返回值类型足以存放你需要的查询结果即可
  • 方法名称:自定义,不建议重载
《阿里巴巴Java开发手册》中的参考:
1) 获取单个对象的方法用 get 做前缀。
2) 获取多个对象的方法用 list 做前缀。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
  • 参数列表:根据需要执行的SQL语句中的参数来设计,并且,如果参数数量较多,可以封装成自定义数据类型,并使用自定义数据类型作为方法的参数,另外,对于未封装的简单数据类型的参数(例如基本数据类型及对应的包装类、String类),只要参数的数量超过1个,强烈建议在各参数上使用@Param注解配置参数名称,例如:
void getLoginInfoByUsernameAndPassword(
    @Param("username") String username, @Param("password") String password);

关于@Param注解

通常,如果抽象方法中声明了简单类型的参数,在配置SQL语句时,可以使用#{}格式的占位符来表示参数的值,例如:

AlbumStandardVO getStandardById(Long id);
<select id="getStandardById" resultType="cn.tedu.csmall.product.pojo.vo.AlbumStandardVO">
    SELECT id, name, description, sort
    FROM pms_album
    WHERE id=#{xxx}
</select>

其实,以上配置SQL语句时,#{}中的名称是很随意的,并不一定需要是抽象方法中的参数的名称!之所以是这样,主要因为:

  • 在Java语言中,默认情况下,所有局部的量(方法内部的局部变量、方法的参数)的名称都会在编译时丢失

  • 此抽象方法只有1个参数,MyBatis会自动的去找那唯一的参数,所以,名称并不重要

由于局部的量的名称会丢失,且方法只有1个参数时,MyBatis就会使用唯一的那个参数值,但是,如果抽象方法有2个或更多个参数,MyBatis就不知道把哪个参数值传到哪个#{}占位符对应的位置!为了解决此问题,MyBatis使用了@Param注解,通过此注解来配置参数的名称,并且,在配置SQL时,应该使用注解中配置名字,例如:

AdminStandardVO getLoginInfoByUsernameAndPassword(
    @Param("username") String var1, @Param("password") String var2);
<select id="getLoginInfoByUsernameAndPassword" 
        resultType="cn.tedu.csmall.product.pojo.vo.AdminStandardVO">
    SELECT *
    FROM ams_admin
    WHERE username=#{username} AND password=#{password}
</select>

另外,在整合了Spring Boot后,使用的mybatis-spring-boot-starter对编译期进行了干预,保留了抽象方法的参数名称,所以,在Spring Boot中,即使不使用@Param注解来配置参数名称,也能够正确的找到各参数!通常,为了避免出问题,仍建议使用@Param注解配置参数名称!

关于使用注解来配置SQL语句

在使用MyBatis时,可以在抽象方法上使用@Insert / @Delete / @Update / @Select注解来配置SQL语句,例如:

public interface AlbumMapper {
    
    @Select("select count(*) from pms_album")
    int count();
    
}

但是,并不推荐使用这种做法,主要原因有:

  • 不适合编写较长篇幅的SQL语句

  • 不适合需要添加其它配置的数据访问

  • 不适合与DBA协同工作

关于使用XML来配置SQL语句

在Spring Boot项目中,需要通过配置文件中的mybatis.mapper-locations属性来配置XML文件的位置,如果使用的是MyBatis-Plus,则需要通过mybatis-plus.mapper-locations属性来配置XML文件的位置。

提示:如果使用的不是Spring Boot项目,则需要配置SqlSessionFactoryBean类型的对象来指定XML文件的位置。

在XML文件内部的配置:

  • 此文件的根标签必须是<mapper>,且必须配置namespace属性,以指定对应的Mapper接口的全限定名

  • <mapper>标签的子级,使用<insert> / <delete> / <update> / <select>标签配置SQL语句,这些标签必须配置id属性,取值为抽象方法的名称,在这些标签内部,配置抽象方法对应的SQL语句

  • 如果对应的数据表中的ID是自动编号的,在配置<insert>标签时,强烈建议配置useGeneratedKeys="true"keyProperty="id"这2个属性,以获取成功插入的数据的自动编号ID值

  • <select>标签上,必须配置resultTyperesultMap这2个属性中的其中1个,使用resultType时,取值为抽象方法的返回值类型的全限定名,使用resultMap时,取值为对应的<resultMap>标签的id属性值

  • 动态SQL之<foreach>标签:用于对参数进行遍历

    • collection属性:指定被遍历的参数,当抽象方法的参数只有1个,且没有添加@Param注解时,如果参数是List集合,则此属性取值为list,如果参数是数组或可变参数,则此属性取值为array,如果抽象方法的参数有多个或配置了@Param注解,则此属性取值为@Param注解中配置的名称

    • item属性:自定义此值,表示遍历过程中各元素的变量名

    • separator属性:遍历过程中生成各值时,各值之间的分隔符号

  • 动态SQL之<if>标签:用于对参数进行判断,例如:

<!-- int update(Album album); -->
<update id="update">
    UPDATE 
    	pms_album
    <set>
    	<if test="name != null">
    		name=#{name},
        </if>
    	<if test="description != null">
    		description=#{description},
        </if>
        <if test="sort != null">
    		sort=#{sort},
        </if>
    </set>
    WHERE
    	id=#{id}
</update>

动态SQL之<choose>系列标签,用于实现类似Java语言中if...else的效果,例如:

<choose>
	<when test="条件">
    	满足条件时的代码片段
    </when>
    <otherwise>
    	不满足条件时的代码片段
    </otherwise>
</choose>

<sql>标签与<include>标签,用于封装SQL语句片段(通常是字段列表)并引用封装的SQL语句片段,例如:

<!-- List<RoleListItemVO> list(); -->
<select id="list" resultType="cn.tedu.csmall.passport.pojo.vo.RoleListItemVO">
    SELECT
        <include refid="ListItemQueryFields"/>
    FROM
        ams_role
    ORDER BY
        sort DESC, id
</select>

<sql id="ListItemQueryFields">
    id, name
</sql>

提示:在IntelliJ IDEA中,在<sql>标签中直接写字段列表会提示错误,但这并不影响运行,如果不希望看到报错的红色波浪线,可以改为:

<sql id="ListItemQueryFields">
    <if test="true">
        id, name
    </if>
</sql>

[SpringBoot]xml写mapper&设置自动提示_万物更新_的博客-CSDN博客

MyBatis的缓存机制

MyBatis框架存在缓存机制,表现为:当执行查询后,可以将查询结果暂时保存到应用程序服务器中(即使返回了查询结果,也不会销毁这个数据),后续再次查询时,就可以将此前的查询结果直接返回,以此提高“查询”效率。

MyBatis框架有2种缓存机制,分别称之为一级缓存和二级缓存。

一级缓存也称之为“会话缓存”,它是基于SqlSession的,默认是开启的,且无法关闭,当使用同一个Mapper、执行同样的查询、传递的是同样的参数时,后续的查询将直接返回前序的查询结果。

一级缓存的结果会因为手动清除(调用SqlSession对象的clearCache()方法)而消失,也会因为此Mapper执行了任何写操作(增、删、改)而自动清除,无论这个写操作是否改变了任何数据!

关于一级缓存的示例:

@Autowired
SqlSessionFactory sqlSessionFactory;

@Test
void l1Cache() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    AlbumMapper albumMapper = sqlSession.getMapper(AlbumMapper.class);

    System.out.println("准备执行第1次查询……");
    Object queryResult1 = albumMapper.getStandardById(1L);
    System.out.println("第1次查询结束!");
    System.out.println(queryResult1);

    System.out.println("准备执行第2次查询……");
    Object queryResult2 = albumMapper.getStandardById(1L);
    System.out.println("第2次查询结束!");
    System.out.println(queryResult2);

    // 清除此前的缓存结果
    // sqlSession.clearCache();
    albumMapper.deleteById(13000000L);

    System.out.println("准备执行第3次查询……");
    Object queryResult3 = albumMapper.getStandardById(1L);
    System.out.println("第3次查询结束!");
    System.out.println(queryResult3);

    System.out.println("准备执行第4次查询……");
    Object queryResult4 = albumMapper.getStandardById(2L);
    System.out.println("第4次查询结束!");
    System.out.println(queryResult4);

    System.out.println("准备执行第5次查询……");
    Object queryResult5 = albumMapper.getStandardById(1L);
    System.out.println("第5次查询结束!");
    System.out.println(queryResult5);
}

二级缓存也称之为“namespace缓存”,是基于namespace的(其实就是配置SQL的XML对应的Mapper),在Spring Boot中,默认全局开启,但各namespace默认并未开启,可以在XML文件中添加<cache/>标签以开启当前XML对应的Mapper的二级缓存,只要是同样的查询,且传入的参数相同,后续的查询就可以直接使用前序的查询结果。

二级缓存也会因为调用了此XML中的任何写数据的操作而清除!

注意:二级缓存还可以通过在<select>标签上配置useCache属性来开启或关闭,默认取值为true

注意:当使用二级缓存后,查询结果的类型必须实现Serializable接口,如果没有实现,在查询时会抛出NotSerializableException异常!

二级缓存的示例代码:

@Autowired
AlbumMapper albumMapper;

@Test
void l2Cache() {
    System.out.println("准备执行第1次查询……");
    Object queryResult1 = albumMapper.getStandardById(1L);
    System.out.println("第1次查询结束!");
    System.out.println(queryResult1);

    System.out.println("准备执行第2次查询……");
    Object queryResult2 = albumMapper.getStandardById(1L);
    System.out.println("第2次查询结束!");
    System.out.println(queryResult2);

    // albumMapper.deleteById(12L);

    System.out.println("准备执行第3次查询……");
    Object queryResult3 = albumMapper.getStandardById(1L);
    System.out.println("第3次查询结束!");
    System.out.println(queryResult3);

    System.out.println("准备执行第4次查询……");
    Object queryResult4 = albumMapper.getStandardById(2L);
    System.out.println("第4次查询结束!");
    System.out.println(queryResult4);

    System.out.println("准备执行第5次查询……");
    Object queryResult5 = albumMapper.getStandardById(1L);
    System.out.println("第5次查询结束!");
    System.out.println(queryResult5);
}

MyBatis在每次查询时,都会优先检查二级缓存,如果命中,将直接返回缓存结果,如果未命中,则会检查一级缓存,如果仍未命中,才会执行真正的查询。

关于命中率:

关于MyBatis的#{}${}格式的占位符

在使用MyBatis时,配置SQL语句中的参数可以使用#{}${}格式的占位符。

提示:在SQL语句中,所有值都需要使用一对单引号框住,使得数据库在执行时知道这是一个值!只要没有被正确识别为“值”,数据库就会认为这是一个字段名(或列名),由于数字值、布尔值不可能作为字段名,所以,这种值可以不使用单引号框住!例如:

 如果在用$时,不用单引号,则需要在传过来的值上加单引号:

提示:当需要执行某个SQL语句时,数据库收到SQL语句后,会先执行词法分析,再执行语义分析,再执行编译,最后再执行!

当使用#{}格式的占位符时,会使用预编译的处理方式,这种处理方式会先使用问号对值进行占位,在值还没有代入之前就进行编译,即在执行之前已经完成了词法分析、语义分析、编译,其中,语义分析过程就已经确定了这条SQL语句的“意思”,最终将值代入执行时,无论这是什么样的值,都不会被误以为是字段名或列名!所以,在使用#{}格式的占位符表示任何值时,都不需要使用一对单引号框住!同时,由于是预编译的,所以,使用#{}格式的占位符时,不会出现SQL注入的问题!注意,使用#{}格式的占位符只能表示某个值!

当使用${}格式的占位符时,会先将值代入到原SQL语句,然后执行编译过程,然后执行,由于在执行语义分析之前就已经将值代入,那么,如果字符串值没有添加一对单引号,就会被误以为是字段名或列名!所以,在使用${}格式的占位符表示字符串值时,需要使用一对单引号框住!同时,由于不是预编译的,所以,代入的值可能改变SQL语句原本的语义,则存在SQL注入风险!注意,使用${}格式的占位符可以表示SQL语句的任何片段,只需要保证将值代入后是合法的语句即可,则整体设计可以更加灵活,但需考虑语法格式的问题和SQL注入风险!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值