入坑码农第二天:MyBatis相关(一)

本专栏打算先记录一些深入学习MyBatis后了解到的一些比较好用的操作分享给大家,后续可以对MyBatis框架进行源码解析,希望大家在面试或者工作过程中可以游刃有余。

通过JDBC访问数据库

首先我们应该了解,对于Java而言,与数据库(这里主要针对MySQL数据库)交互的方法,我们第一个想到的就是JDBC,那么通过JDBC进行数据查询需要哪些步骤呢?

    // 1. 注册驱动和数据库信息获得connection
    Class.forName("com.mysql.cj.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306...","account","password");
    
    // 2.通过connection打开preparedStatement
    PreparedStatement ps = connection.prepareStatement("select name, age from employee where id = ?");
        ps.setInt(1,1);

    //3. 利用preparedStatement来执行sql语句,获取ResultSet
    ResultSet resultSet = ps.executeQuery();

    //4. 将ResultSet中的结果集转成Java的POJO对象或进行后续操作
    while (resultSet.next()){
        //执行查询结果后续操作
    }

    //5. 关闭连接
    connection.close();

从上述伪代码中我们可以了解到,使用JDBC进行数据库交互的步骤比较繁琐,有时候我们只是做一些查询操作,没必要如此麻烦,因此MyBatis的出现给我们带来了很多便捷。那么MyBatis有什么特点呢:

通过MyBatis执行数据库查询

通过MyBatis访问数据库的实现步骤:

1. 引入MyBatis的Maven依赖

2. 在mybatis-config.xml配置文件中配置数据源信息

3. 编写EmployeeMapper.java文件

4. 编写EmployeeMapper.xml配置文件,编写SQL,建立起Employee与tb_employee表的映射关系

优点:

1. MyBstis可以实现动态SQL,根据入参的实际情况B来进行SQL执行。

2. 动态映射,MyBatis可以在配置文件中通过配置来决定SQL的映射规则,可以自动映射。

3. 面向接口编程:一般来说,只需要某个DO (Database Object) 的mapper接口以及对应SQL的xml文件即可运行,与Java代码解耦。

此外,MyBatis底层也是基于JDBC来实现的,关于这里在后续源码解析部分进行详细介绍。

本文不会对MyBatis的安装及基本使用进行详细介绍,只是会介绍一些个人认为在日常工作中对我们有帮助的使用技巧。

MyBatis的使用技巧

自动映射

在mybatis-config.xml文件中进行自动映射配置

<settings>
	<setting name="autoMappingBehavior" value="PARTIAL"/>
    <!-- 这里必须要配置下划线转驼峰,否则失效 --> 
	<setting name="mapUnderscoreToCamelCase" value="true"/>	
</settings>

自动映射autoMappingBehavior共有三个值:

NONE:取消自动映射

PARTIAL:自动映射,不会映射嵌套结果集,MyBatis默认值

FULL:会自动映射任意复杂的结果集(无论是否嵌套)

使用自动映射时,必须开启下划线转驼峰的自动转换!!!

主键回填

我们在工作开发中经常会遇到这种场景:在tb_employee员工表中新增一个员工信息,并且在tb_contact联系方式表中添加对应的联系方式,两张表要用employee_id主键做关联,如果员工表主键是自增的,那么我们提前获取主键id就很困难,那么会怎么实现呢?一种解决办法是先插入员工表数据,再通过其他唯一性条件将这条数据查出来获取主键id,最后关联到contact表,这样操作相当不优雅,而主键回填就帮我们轻松解决:

    <insert id="insertNewEmployee" useGeneratedKeys="true">
        insert into tb_employee (name, age) values ("Judy", 18)
    </insert>

在SQL的xml对应insert语句中添加主键回填标签,并设置为true,即可完美解决上述问题,我们在mapper文件中对其结果进行接收即可获取主键id值

级联

MyBatis提供了两种级联方式。分别是association和collection,其中前者用于解决一对一的关系,表示一个对象最多有一个关联对象,例如员工信息和对应籍贯信息;而后者顾名思义,用于解决一对多的关联关系,表示一个对象可以有多个关联,例如员工信息对应不同的的联系方式。

association

实例:获取某一员工基本信息和毕业院校信息

员工信息(主表)

@Data
public class Employee {

    private Long id;

    private String name;

    private Integer age;

    private Long graduateId;

    private GraduateInfo graduateInfo;
}

学校信息

@Data
public class GraduateInfo {

    private Long id;

    private Long graduateId;

    private String graduatedSchoolName;
}

查询学校信息GraduateInfoMapper.xml

    <select id="getGraduateInfo" parameterType="long" resultType="graduateInfo">
        select
            id, graduate_id, graduated_school_name
        from
            graduate_info
        where
            graduate_id = #{graduateId}
    </select>

查询员工信息EmployeeMapper.xmlvv

    <resultMap id="employeeAndGraduateMap" type="employee">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="graduate_id" property="graduateId"/>
        <association property="graduateInfo" column="graduate_id"
                     select="mybatis.mapper.GraduateInfoMapper.getGraduateInfo"/>
    </resultMap>

    <select id="getEmployeeInfoAndGraduateInfoById" 
            parameterType="long" resultMap="employeeAndGraduateMap">
        select
            id, name, age, graduate_id
        from
            employee
        where id = #{id}
    </select>

 利用graduateId将两张表关联起来,由MyBatis解析查询数据,并将结果封装起来,这样我们就无需在Java代码中分别查询员工信息和学校信息,最后再自行封装。

collection

@Data
public class Employee {

    private Long id;

    private String name;

    private Integer age;
    // 使用list进行接收
    private List<Contact> contacts;
}

collection和association的xml配置区别不大,在仅修改标签即可,大家抽空可以尝试实现,

#与$的区别及SQL注入

我们会过头看一下JDBC的例子,在进行有传参的SQL执行时,JDBC通过使用?号作为预编译SQL中参数的占位符,而MyBatis底层也是JDBC的封装,使用#{}来代替JDBC的占位符。

${}仅仅是为了做字符串替换,无其他含义,而在ToC的场景或者外部条件下很容易产生SQL注入,那我们来说一下什么是SQL注入:

首先我们有一个场景:

在订单系统中我们只有部分订单的查看权限,当我们对某个订单进行查询,传入String类型orderId参数:30

通过系统进行数据库查询,那么在MyBatis中执行SQL语句时,#号和$号的SQL组装结果如下:

    <select id="getOrderById" resultSetType="orderData">
        select order_id from tb_order where id = #{orderId}
<!--    实际SQL为:    select order_id from tb_order where id = '30'-->
    </select>

    <select id="getOrderById" resultSetType="orderData">
        select order_id from tb_order where id = ${orderId}
<!--    实际SQL为:    select order_id from tb_order where id = 30-->
    </select>

表面看起来区别不大,且能正确查询数据,如果我们的传入参数为:30 or 1 = 1,这样SQL的组装结果如下:

    <select id="getOrderById" resultSetType="orderData">
        select order_id from tb_order where id = #{orderId}
<!--    实际SQL为:    select order_id from tb_order where id = '30 or 1 = 1'-->
    </select>

    <select id="getOrderById" resultSetType="orderData">
        select order_id from tb_order where id = ${orderId}
<!--    实际SQL为:    select order_id from tb_order where id = 30 or 1 = 1-->
    </select>

使用#号查询数据为空,但是使用$号竟然查询出了全部数据!这是我们所不能容忍的,

所谓SQL注入就是指web应用对用户输入的数据合法性过滤不严,导致攻击者可以在查询语句结尾添加额外的SQL语句实现非法操作,因此对于$号,我们应该谨慎使用!

缓存机制

MyBatis默认开启了一级缓存(也叫本地缓存localCache),当同一个SqlSession对象调用同一个Mapper方法时,MyBatis第一次会从数据库获取数据后将其存入一级缓存中,如果没有声明查询时需要刷新缓存并且缓存没过期的情况下,后续查询只会取当前缓存中的数据。

一级缓存什么时候刷新:

1. 执行数据更新操作,包括insert、update和delete;

2. 显式使用sqlSession.clearCache()方法会清空缓存;

3. 执行sqlSession.sqlcommit() 方法提交事务;

4. 执行sqlSession.close()关闭会话;

如需开启二级缓存,需要在对应Mapper文件中添加<cache/>标签,或者在对应DO类上面添加@CacheNamespace(blocking = true)来开启缓存, 同时要注意:查询数据时要执行SQL Session.commit()时二级缓存才会生效,其中POJO必须实现Serializable接口 ,具体原因在后续源码解析过程中会给大家详细介绍。

本次先给大家介绍这些常用操作,如果大家在工作中有哪些功能使用方便,也可以在评论区提供出来,感谢大家分享。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值