新公司底层的ORM用的是Mybatis,以前一直是用Hibernate,从来没用过mybatis.所以晚上闲着无聊就听听歌,敲敲demo,仅仅两个晚上,mybatis基本算入门了,发现其实就是把sql语句配置到了外部的XML中。把sql直接写在dao层和配置到XML中有什么区别?区别就是java类是需要编译的,而XML是不需要编译的,所以一般框架都喜欢把动态部分配置到XML或properties文件中。
首先需要搞清楚Mybatis是什么,这个你可以google,我只想强调一句,mybatis是ORM框架,跟hibernate类似,但hibernate是面向对象的,而Mybatis不是。学习一个框架首先你要去官网去下载它的源码以及开发文档,不要问我哪里可以下载。然后你可以得到mybatis-xx00.jar(mybatis核心包)。mybatis-spring-xx00.jar(与Spring结合包)。xxoo代表的是版本号。学习时建议下载最新版,因为一般旧版本可能有未解决的BUG。但实际项目一般应下载稳定版而不是最新版。
Mybatis一个重要的接口就是SqlSession,是不是有种似曾相识的感觉,嗯,是的,hibernate有个session接口,看来框架的设计思想都是类似的,只是各自侧重点不一样,Hibernate主要是为了让开发人员能以面向对象的方式来进行JDBC开发,从恼人的sql语句中摆脱出来。为什么要那么推崇面向对象呢?因为面向对象更符合人类看待问题的思维方式。那么SqlSession接口都定义了哪些行为呢?如图:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/e267b0c0b7a2ef06fdbeb447c8f7723d.jpeg)
看方法名就应该能猜个一知半解,selectOne明显是查询并返回单个对象,selectList就是返回一个对象集合,insert就是新增一个对象,参数s是什么意思呢,其实参数s表示的sql语句id,sql语句id?也许你会问何来的sql语句id,前面不是说了mybatis核心其实是把sql语句配置到XML配置文件中,下面是一个mybatis的sql语句映射文件:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/66648c80243ea1791fc5c1a1db5ba096.jpeg)
看XML的标签名就知道是干嘛的,明显是对应增删改查的,而且每个标签都有个id,我们刚才的sqlsession接口里就是通过这里的id就知道是要执行什么sql语句了.是不是很easy?
下面我以学生班级为例写个mybatis分页的例子
首先需要导入mybatis的核心jar包,以及和spring整合需要的jar包,
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/03ad5ad8f069f13214e9898b6db6d1cb.jpeg)
创建实体类Student和Class:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/d3c4a053a2e0c01e33facd0ba6a2719e.jpeg)
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/fe79fa9e118c3e0bb21764a1e347c7a8.jpeg)
实体类get/set都省略了哈...
接着就是mybatis的重头戏,sql语句映射配置文件xx00Mapper.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.nicolas.dao.StudentMapper">
<parameterMap type="Student" id="StudentParamMap">
<parameter property="studentID" />
<parameter property="studentName" />
<parameter property="sex" />
<parameter property="studentBirthday" />
<parameter property="classEntity" />
</parameterMap>
<resultMap type="Student" id="studentResultMap">
<id property="studentID" column="STUDENT_ID" />
<result property="studentName" column="STUDENT_NAME" />
<result property="sex" column="STUDENT_SEX" />
<result property="studentBirthday" column="STUDENT_BIRTHDAY" />
<association property="classEntity" column="CLASS_ID" select="com.nicolas.dao.ClassMapper.getClassById" />
</resultMap>
<!-- 查询学生,根据id -->
<select id="getStudent" parameterType="String" resultMap="studentResultMap">
<![CDATA[
SELECT * from STUDENT_TBL ST
WHERE ST.STUDENT_ID = #{studentID}
]]>
</select>
<sql id="getAll">SELECT * from STUDENT_TBL ST</sql>
<!-- 查询学生列表 -->
<select id="getStudentAll" resultMap="studentResultMap">
<include refid="getAll" />
</select>
<!-- select - where/if(判断参数) - 将实体类不为空的属性作为where条件 -->
<select id="findStudents" resultMap="studentResultMap" parameterType="Student">
SELECT ST.STUDENT_ID, ST.STUDENT_NAME, ST.STUDENT_SEX,
ST.STUDENT_BIRTHDAY FROM STUDENT_TBL ST
<where>
<if test="studentID !=null ">
ST.STUDENT_ID = #{studentID}
</if>
<if test="studentName !=null ">
ST.STUDENT_NAME LIKE CONCAT(CONCAT('%',
#{studentName,jdbcType=VARCHAR}),'%')
</if>
<if test="sex != null and sex != '' ">
AND ST.STUDENT_SEX = #{sex, jdbcType=INTEGER}
</if>
</where>
ORDER by ST.STUDENT_ID ASC
</select>
<!-- 查询学生list,根据班级id -->
<select id="getStudentByClassID" parameterType="String" resultMap="studentResultMap">
<include refid="getAll" />
WHERE ST.CLASS_ID = #{classID}
</select>
<!-- 插入学生 -->
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENT_TBL (STUDENT_ID, STUDENT_NAME, STUDENT_SEX,
STUDENT_BIRTHDAY,
CLASS_ID) VALUES (#{studentID},
#{studentName}, #{sex}, #{studentBirthday},
#{classEntity.classId})
</insert>
<!-- 更新学生信息 -->
<update id="updateStudent" parameterType="Student">
UPDATE STUDENT_TBL
SET STUDENT_TBL.STUDENT_NAME = #{studentName},
STUDENT_TBL.STUDENT_SEX = #{studentSex},
STUDENT_TBL.STUDENT_BIRTHDAY = #{studentBirthday},
STUDENT_TBL.CLASS_ID =
#{classEntity.classID}
WHERE STUDENT_TBL.STUDENT_ID = #{studentID};
</update>
<!-- 删除学生 -->
<delete id="deleteStudent" parameterType="Student">
DELETE FROM STUDENT_TBL WHERE STUDENT_ID = #{studentID}
</delete>
<sql id="getAll">SELECT * from STUDENT_TBL ST</sql>
<!-- 查询学生列表 -->
<select id="getStudentAll" resultMap="studentResultMap">
<include refid="getAll" />
</select>
</mapper>
可能比较难懂点就是parameterMap和resultMap配置,parameterMap主要为了解决sql语句参数太多导致dao方法入参太多,所以需要将参数都映射成javabean的属性,resultMap只要为了解决resultSet结果集到实际的javabean或者javabean集合的自动转换,再也不用我们自己遍历resultset结果集一个一个的get,然后set到我们自己的javabean里,这样原始的做法会导致一大推狗屎一般的垃圾代码,resultMap帮我们避免此类问题。
然后每个mapper都有个命名空间且该命名空间对象一个Mappper接口,该接口的方法都是此映射文件里的sql语句id值,说白了,此映射文件就是Mapper接口的具体实现,不要我们再编写MapperImpl实现类。
比如:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/ddefe37dccf337ba0ce82804ff2f773e.jpeg)
sql语句映射文件配置文件了,需要让mybatis自动加载此配置文件,所以需要mybatis-config.xml,
此配置文件是mybatis启动参数配置,如数据库链接相关配置,实体类别名配置,sql语句映射文件指定以及mybatis相关插件配置等等。
![Mybatis分页 Mybatis分页](http://s16.sinaimg.cn/middle/74af50ectca7336dd3e2f&690)
就不过多解释了,配置内容一看就知道是什么意思。dialect是配置数据方言,即mybatis分页需要的参数也就是告诉mybaits你使用的是什么类型数据库。
因为每种数据库的分页sql语句不尽相同的,所以有必要抽象出一个Dialect接口:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/0787802b6b8f8041d6acc7a78e742235.jpeg)
然后就是各种数据库的方言接口实现:
MySQL实现:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/9651b986c4d9359653105cf9e4020fbc.jpeg)
SQLServer实现:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/678a9d54070589db781a2faade2f9b40.jpeg)
Oracle实现:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/89a0f3065d9a96b1e1501cab3402c44a.jpeg)
然后就是实现mybatis提供的拦截器接口,编写我们自己的分页实现,原理就是拦截底层JDBC操作相关的Statement对象,把前端的分页参数如当前记录索引和每页大小通过拦截器注入到sql语句中,即在sql执行之前通过分页参数重新生成分页sql,而具体的分页sql实现是分离到Dialect接口中去。
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/d6afe092218795342d9e961f4f892dd5.jpeg)
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/f6bca0cf6649f09f378762c8c245a857.jpeg)
核心代码就是这些,至于分页插件怎么注册请查看mybatis-config.xml的配置。
最后编写一个测试类检验效果,为了简单我就没有写页面用web请求来模拟整个过程了,实际项目是需要在server层调用xxooMapper接口里的方法来操纵底层数据库执行sql语句,而Mapper接口实际也是通过解析Mapper.xml然后通过mybatis封装的接口去操作数据库,对于用户来说,具体操作数据库的细节都被屏蔽了,用户只需要告诉框架各种数据库操作的sql相关配置即可。
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/22d077a30154bcb85fc346452ea228ba.jpeg)
一对多的测试类:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/a0931e8c50ead76b3962e705dc626884.jpeg)
多对一的测试类代码:
![Mybatis分页 Mybatis分页](https://i-blog.csdnimg.cn/blog_migrate/eba17c05c355ab8120593bd20dfe09b0.jpeg)
好吧,就写这么多了,很累,自己也是偷懒所以只是写java类来测试,没有写jsp用web形式来测试,其实如果用web形式来写,唯一区别就是不要直接操作mybatis的sqlsession接口,而是mybatis会自动将xxoomapper.xml和XXOOMapper.java一一对应,mapper.xml的命名空间等于mapper.java的完整包路径,而mapper接口里的所有方法名和mapper配置文件里的sql语句id对应,仅次而已。太累了,不想写那么麻烦,如果各位不知道web形式怎么实现,具体再QQ上或群里问吧,下班回家就一直在酝酿博客并编写测试示例,真的累了,就此搁笔了,不为什么,只要对各位有所帮助或者一丝启发就足矣。