mybatis学习
一、传统JDBC 操作存在的问题
- 原始jdbc开发存在一些问题:
(1)数据库创建连接、释放频繁,造成系统资源的浪费,进而影响系统性能。
(2)sql语句再代码中硬编码,造成代码不宜维护,实际应用sql变化的可能性比较大,而sql的变化需要改变java代码。
(3)查询操作时,需要手动将结果集中封装到实体中去。插入时,需要手动将实体数据设置到sql语句的占位符位置。 - 针对上面问题,mybatis给出了具体的解决方案。
(1)使用数据库连接池初始化连接资源。
(2)将sql语句抽取到xml配置文件里。
(3)使用反射等底层技术,将实体与表进行属性与字段的自动映射。
二、Mybatis的介绍和使用
2.1、介绍
2.2、mybatis的快速入门
-首先, 添加mybatis的坐标
- 其次,创建数据表。并给表里面添加一些数据。
create table user(
id int primary key auto_increment,
username varchar(10),
password varchar(10)
);
- 编写User实体类。
- 然后编写映射文件:userMapper.xml文件。在写这个的时候需要注意的是namespace不敢忘了写,至于namespace所对的值随便写,但是一般都是按照自己的事务去命名。其次就是id属性不敢忘了写。后面调用需要用到。最后就是注意在sql标签里面参数类型,和返回值类型的书写。
- 下图已经编写增删改查的常见操作方式。但是有些地方还是需要提一下他们的注意事项。
(1)插入操作,在映射文件中使用parameterType属性指定要插入的数据类型。
(2)插入操作设计数据库数据的变化,所以要是有sqlSession对象显示的提交事务,即:sqlSession.commit()。
(3)sql语句中使用#{实体属性名}方式引用实体中的属性值。
(4)sql语句中使用#{参数名称}方式引用传递的单个参数。
- 编写核心文件SqlMapConfig.xml。在这个里面可以配置数据类型的别名、配置数据源环境、以及加载我们上一步编写的映射文件。**注意:在配置数据源的时候,我们需要用到数据在properties里面写着,因此在这里面引入了properties文件。
- 上面引入的properties配置文件。
- 最后,就是编写测试类。在编写时,按照下图的步骤,前三步一般都是一样的,主要就是到了第四步,根据SqlSession对象调用不同的方法来操作数据库。其次就是在调用的的方法里面要将自己在前面映射文件里面写的sql语句映射进来,现在知道写namespace和id的作用了吧。在使用完之后,别忘了关闭相应的流。
- 到这里,快速入门就讲完了。
2.3、mybatis的映射文件概述
- 下面是映射文件的标注,通过下图,就可以对映射文件的大概书写就了解了。
2.4、mybatis的核心配置文件概述
- 下图就是快速入门时编写的核心配置文件,在该文件里面一般就是用来起别名、配置数据源环境、加载映射文件、类型的转换等等。
- 可以配置的属性有:
(1)properties属性:加载properties文件。
(2)typeAliases:类型别名
(3)typeHandlers:类型处理器
(4)plugins:插件
(5)enviroments:环境
(6)mappers:映射器
2.4.1、enviroments标签的使用
- 这个标签主要是用来配置数据库环境。
- 该标签的具体使用
2.4.2、mapper标签的使用
- 该标签主要就是用来加载映射文件的。加载的方式一共有一下几种。
2.4.3、properties标签的使用
- 在开发中,我们习惯于将数据源信息抽取成一个单独的properties文件,该标签就是用来将properties文件导入到核心配置文件里面。
2.4.4、typeAliases标签的使用
- 这个标签就是用来为Java类型设置一个短名字。具体使用参考下图;
- 上图就是用来起别名,但是mybatis框架也替我们设置一些常用类型的别名。具体请看下图:
2.5、mybatis的相应API
- 在上面快速入门的测试案例中,我们是直接使用mybatis的API,下面我们来具体讲讲这写API的使用。
2.5.1、加载文件的流对象——Resource
- 这个不是属于mybatis的API,他是org.apache.ibatis.io包中的一个类。它主要帮我们从类路径下、文件系统、一个web URL中加载资源文件。而在这里,主要就是用来加类路径的载核心配置文件。
2.5.2、工厂构造器SqlSessionFactoryBuilder
- 通过使用该工厂构造器的build方法来加载核心文件的输入流从而构造一个 SqlSessionFactory对象。
2.5.3、SqlSession实例
- 上一步,我们通过工厂构造器创建了一个工厂对象。然后通过该工厂对象来创建一个SqlSession对象。这个实例是非常强大的一个类,我们使用SQL语句、提交事务、回滚事务和获取映射器实例的方法都要依赖与这个实例。
- 通过工厂构造器构造该实例的方法有两种:
(1)oppenSession():或默认开启事务,但是事务不会自动提交,也就意味着需要手动提交事务,更新操作才能持久化到数据库中。
(2)oppenSession(boolean b):如果参数设置为true,则不需要手动提交事务。如果设置为false,就和没有参数的那个一样。 - 它执行SQL语句的方法主要有:注意:下面的方法里面使用到的参数我没有写。
(1)selectOne()
(2)selectList()
(3)insert()
(4)update()
(5)delete() - 操作事务的方法主要有:
(1)commite()
(2)rollback()
三、接口代理的开发方式
- 在上面我们讲到的快速入门案例,是利用传统的开发方式进行编写。但是在实际开发中,我们常常用到是接口代理的开发方式。下面我们就来看看,如何使用接口代理的方式进行开发。
3.1、代理开发方式遵循的规范
- mapper接口开发需要遵循以下规范:也可以参考下图
(1)mapper.xml文件中namespace的值就是mapper接口的全类名。
(2)mapper接口里面的方法名和mapper.xml文件中定义的每一个statement的id相同。
(3)mapper接口方法的参数类型和mapper.xml文件中定义的每一个的statement的parameterType的类型相同。
(4)mapper接口方法的返回值类型和mapper.xml文件中定义的每一个的statement的resultType的类型相同。
3.2、代理开发的实现步骤
- 第一步:导入坐标
和快速入门案例的一样 - 第二步:编写核心配置文件
和快速入门案例的一样。 - 第三步:编写User类
和快速入门案例的一样 - 第四步:编写UserMapper接口
- 第五步:在接口里定义方法,这里我为了节省空间就只写了两个方法。
- 第六步:编写映射文件userMapper.xml,将接口里面方法所要实现的业务sql语句写进来。注意我用红色标注的,也就是namespace的值要和mapper接口的全类名一样。id的值和方法名称一致。参数类型、返回值类型也要和方法定义的一致。
- 第七步:测试,这里我就只将find这个操作的测试贴出来,另外一个和这个大同小异。注意下图的第四步
四、mybatis映射文件的深入学习
4.1、动态sql语句
- 根据实际的业务场景需求不一样,往往SQL语句也就不一样。但是通过上面的学习,我们应该也发现,我们常常都是直接将sql语句写死到映射文件里面。那么该如何动态的写sql语句呢?接下来我将讲解动态SQL语句的书写。
4.1.1、动态SQL之< if >
- 根据实体类的不同取值,使用不同的SQL语句进行查询。比如在id不为空时可以根据id查询,如果username不为空还要加入用户名作为查询条件。这种情况在多条件组合查询中经常会碰到,很明显这就需要动态SQL语句。下面我们就说说这重动态的sql语句如何写。
- 动态SQL,是在映射文件中进行书写,在按照上面的快速入门案例或者代理接口的案例搭建好项目之后,就可以在映射文件中开始书写。通过下图,我们很容易就能看出来添加一个if标签,在test里面写上条件判断,如果条件成立,则就将if标签包裹的语句片段添加到SQL语句里面,从而达到动态书写SQL语句的效果。
4.1.2、动态SQL之< foreach >
- 这个动态语句常常用于带有in关键字的SQL语句中。
- 例如:select * from user where id in(1, 2, 3 )。则我们就可以按照下图去书写。猛地一看,哇偶,这是什么鬼,看不懂。其实这个你仔细看了之后就会发现这其实就是一个字符串拼接过程,他将存储在list里面的参数用逗号分隔,按照sql语句的格式一个一个的拼接起来,从而形成我们需要的sql语句。
- 写到这里,我再多说一下,动态SQL语句还有其他标签,我就不再写了,学会这两个,相信大家在学其他的也就会很简单。
4.2、动态SQL语句的片段抽取
- 在映射文件里面有时候在不同的statement里面会出现相同的sql语句,如果现在这些相同的SQL语句都需要同一处,那么一个一个去改不便于维护,于是就有了抽取这一想法。将这些相同的SQL语句抽取出来,然后给起个名字,然后在使用的地方将这个名字引入。以后在修改时,只需要修改提取出来的那个就行了。下面就让我们来看看这是怎么做到的。
使用场景:
(1)当出现重复代码时,例如下面这两个查询都出现了select * from user。那么我们
就可以考虑将其共同部分抽取出来。
<select id="findByCondition" resultType="user" parameterType="user">
select * from user
<where>
<if test="id != 0">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="password != 0">
and password = #{password}
</if>
</where>
</select>
<select id="findByIds" resultType="user" parameterType="list">
select * from user
<where>
<foreach collection="list" open="id=(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
(2)共同部分的抽取过程:将共同部分的sql语句抽取出来放到<sql></sql>标签里面,然后
再在使用这些共同sql语句的地方配置上<include></include>标签。如下面所示。
<!-- 片段的抽取-->
<sql id="selectUser">select * from user</sql>
<select id="findByCondition" resultType="user" parameterType="user">
<include refid="selectUser"></include>
<where>
<if test="id != 0">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="password != 0">
and password = #{password}
</if>
</where>
</select>
<select id="findByIds" resultType="user" parameterType="list">
<include refid="selectUser"></include>
<where>
<foreach collection="list" open="id=(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
五、mybatis配置文件的深入学习
5.1、typeHandlers标签的使用
- 该标签是用来做类型转换的。比如,我现在将一个日期数据2022/6/26按照该格式赋值给Java类的一个属性。但是为了节省空间,在将该数据存储到数据库的时候我希望将他按照long类型的数据进行存储。所以我们就需要做类型转换,在存储的时候将字符串格式的日期转换为long类型,在读取的时候,将long类型的数据再转化为字符串格式。下面就来看看该如何做。
- 第一步:定义一个转换类并继承BaseTypeHandler类。注意这个泛型就是需要转换的类型。
- 第二步:实现该接口里面的方法,该接口里面一共有四个方法需要实现。其中第一个是用来将java里面的数据类转为数据库里面的类型,为了将数据存储到数据库中。后面三个都是用来将数据库里面的类型转为java的数据类型,为了从数据库读取出来后仍然还是Java得数据类型。具体的使用请参考下图。
- 第三步:将写好的转换器配置到中心配置文件里面
- 第四步:测试
mybatis_config项目里面有测试
5.2、plugins标签的使用
- mybatis可以使用第三方插件来对功能进行扩展。例如下面我们以分页助手PageHelper为例。
- 第一步:导入PageHelper坐标
- 第二步:在核心配置文件里面进行配置插件。
- 测试
这一部分的代码还没写,回头补上
六、mybatis的多表操作
6.1、一对一查询
- 我们上面的案例都是一对一的单表查询方式,所以在这一步的就不再多说什么了。
6.2、一对多查询
- 我们先来看一个场景。一个客户要查询自己的订单,那么就需要将该客户的所以订单都反馈给客户。而在数据库中,我们通常是将客户存到一张表里,将订单存到另一张表里。因此这个业务操作就是一个典型的多对多操作。
- 接下来,我们首先创建一个Java类,用来存储客户和他的所以订单的类。如下图所示:注意:别忘了写这个类的get/set方法。我这里是为了节省空间,所以就没有写。
- 然后,我们在mapper接口里面添加上相应的方法。
- 接着在映射类里面写上相应的SQL语句。下面写了这么多肯定就会有人看不懂了,接下来我就介绍一下这一部分都是什么意思。
- 由于现在的场景是一对多的方式,所以在User类里面也包含一个订单类的集合,接下来我们就来看看这个order集合属性的值该如何封装。首先,prperties的值就是user类里面这个oredr集合的属性名,ofType是该集合的泛型类型,也就是order。接下来就是封装order类里面的属性,和封装user的方式一样。具体请参考下图。写到这里可能想的多的人就会问,现在里面是一个order类的集合。那如果只是一个简简单单的order类成员,那该如何对其进行封装?请看6.3部分
- 测试
6.3、6.2的疑问解决
- 在6.2篇章时,我们提到了一个问题:**如果user类里面现在只有一个简简单单的order类,那么我们该如何把从数据表查询到的有关order类里面的字段值属性封装到user类里面的order类成员的属性里面?**一共有两种方式。接下来我们就开始第一种方式的封装,注意:上面我们说的是在user类里面有一个order成员,但是下面举例的时候我写的是order里面有一个user类的成员。封装方式都是一样,希望不要混淆。
- 第一种:这种方式其实很容易看懂,封装的时候通过类成员来点上一个它里面的属性就可以了。这个知识点在javaSE阶段就已经学习过了。有的人可能会说,你这个类里面的成员都被private修饰了,不能使用点来调用。能想到这一点很好,但是这里的配置文件语法就是这样规定的,对于规定好的东西,不要想得太多,知道怎么用就行。
- 第二种:上面我们说了通过类成员来点上他的属性值就可以了。下面我们换另外一种方式来解决这个问题。我们可以通过association这个标签来解决这个问题。这个标签里面的property的值就是类里面的成员名称,后面的javaType的值就是该成员的类型,(注意:我们这里之所以写的是user,而不是user类的全类名,原因就是因为我们在配置文件里面使用了起别名的方式)。然后,剩下的就按照正常的配置写法去写就行了。太多的废话就不说了,具体使用请参考下图:
6.3、多对多查询
- 多表查询和代表查询的做法一样,都是先在mapper接口里面将需要进行多对多的业务方法定义好,然后去映射文件中编写相应的SQL语句,最后就可以使用了。
七、注解开发
- 之所以学习注解开发,原因是注解开发不仅可以不用写配置文件,而且注解开发慢慢的已经成为一种趋势。
7.1、注解开发快速入门
- 第一步:导坐标
** 和上面的入门案例一样** - 第二步:编写User类
** 和上面的入门案例一样** - 第三步:mapper接口,其实相当于写映射文件,只不过是将映射的SQL语句写到了注解里。这个接口里面主要是用注解配置了单表操作的方法,多表操作在下面在进行讲解
- 第四步:编写核心配置文档,其他的和之前都一样,唯独这一块不一样,之前这里写核心映射文件的目录结构,现在这里写mapper接口的包目录结构。
- mapper接口的目录结构。
- 测试
- 到这里,快速入门就写完了。
7.2、mybatis常见注解的讲解
- @Insert:往数据库里新增数据;
- @Delete:删除数据;
- @Update:更新数据;
- @Select:查询数据
- 上面这四个注解在快速入门里面都已经使用过了,相信大家应该可以看懂。接下来我们就讲讲下面这几个注解该如何去使用。首先,先让我们看看官方定义的他们的作用是什么。
- @Result:实现结果集封装;
- @Results:可以与@Result一起使用,封装多个结果集;
- @One:实现一对一结构封装;
- @Many:实现一对多结果封装;
- 看完之后相信很多人和我一样,一脸懵逼,这是啥呀,看不懂。废话不多说,看不懂就上例子,结合例子相信很多人很快就能看懂。仔细看看这个就会发现@Results注解和我们前面学的resultMap标签很像,没错,这里的这个注解和那个标签是一个作用。那么很容易就可以看出@Result注解和result标签是一个作用。为了方便对比,我将使用配置文件的那个照片放在了下面,方便去对比。来,接着往下看,现在就到了这个@Many注解了,仔细想一下前面讲的那个一对多的案例,很容易就能想到这就是那个多。@Result注解里面的property属性的值就是那个order类集合的名称,javaType的值就是集合的类型。但是这个column的值为啥是id,想不明白。其实也不难,仔细想一下,多对多的数据表,我们通常使用一张中间表来将两张表结合起来。通过将这个id从而查询到这个user_id所对应的其他order_id,因此就可以查询到符合条件的所以order类的数据。现在我们就来说说这个id,其实这里是将这个id作为方法的一个形参传递到findById这个方法里面(这个方法的照片我也放在了下面),而这个方法就帮我们完成了那个多的查询从而返回回来。
- 讲完@Many注解,可能很多小伙伴就已经懵了。说实话,我在刚刚学到这个的时候也是很懵的,但是记住了“万事开头难”。接下来我们说说@One这个注解,如果上面的那个注解都理解了,那么下面这个对你而言就是手到擒来。@One这个注解就是用于我们上面6.3那一章节所解决的那种情况。和上面一样,@Result里面的property的值就是属性名称,javaType就是该属性的类型,而这个uid就是用来查询该order对象里面的那个user对象的。具体的查询是通过selectById这个方法完成的。我将这个方法的照片也放到下面。
- 到这里mybatis的基础用法就将完了,希望对大家有帮助。