浅谈mybatis如何半自动化解耦
在 JAVA 发展过程中,涌现出一系列的 ORM 框架,JPA,Hibernate,Mybatis 和 Spring jdbc,本系列,将来研究 Mybatis。
通过研究 mybatis 源码,可将 mybatis 的大致架构总结为下图:
1. 根据 Mybatis 源码,将其抽象为三层:基础支持层,核心处理层和接口层
2. 基础支持层包括:数据源、事务管理、日志、类型转换、缓存、Bind、解析器等
3. 核心处理层包括:配置解析、配置映射、SQL 解析、SQL 执行、结果集映射、插件等
4. 接口层主要提供 JAVA API
在本篇文章中,将基于该框架图,解决如下几个问题:
Q1:结合代码解析 mybatis 的 CRUD 原理是怎样的?
Q2:为什么半自动化的 Mybatis 比自动化的 Hibernate 受欢迎?
Q3:Mybatis 为什么能实现松耦合?
一 mybatis 的 CRUD 原理
为了解决该问题,我们先来看看如下代码:
该代码实现的功能是:根据 user_id 查询用户信息。 从代码中,我们可以看出,大致分为五步:
第一步:读取 mybatis 的全局配置文件 mybatis-config.xml 内容
第二步:创建 SqlSessionFactory 会话工厂
第三步:根据 SqlSessionFactory 创建 SQL 会话 SqlSession
第四步:执行查询操作
public static void main(String[] args) throws IOException { //读取配置文件内容 String resource = "demo/mybatis/resources/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //创建 SqlSessionFactory SqlSessionFactory sqlSF = new SqlSessionFactoryBuilder().build(inputStream); //创建 SqlSession SqlSession sqlS = sqlSF.openSession();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">根据id查询</span> <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">查询user_id=2的记录</span> List<UserInfo> list = sqlS.selectList("getUserInfoById", 2<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (UserInfo user : list) { System.out.println(</span>"UserName:" + user.getUser_name() + ",Addr:" +<span style="color: rgba(0, 0, 0, 1)"> user.getUser_addr()); } } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> { sqlS.close(); } }</span></pre>
那么,我们再来看看,Mybatis-config.xml 内容:
从内容中,可以看出 <configuration> 下面有三个子节点,<properties>,<environment> 和 <mapper> 节点。
那么,这三个节点到底是表示什么呢?
1.properties 节点表示属性节点,可用于动态从外部获取资源,将获取的资源供上下文使用,我们来看看 jdbc.properties 内容
#mysql
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db_test?characterEncoding=UTF-8
username=root
password=root
一看便知,这是访问数据库相关参数,那么哪个地方引用这些参数呢?<environment> 子节点。
2.environment 节点,环境节点配置节点,如用于配置数据库测试环境,开发环境等,很容易看出 dataSource 的相关子节点的占位符就引用了
properties 节点从 jdbc.properties 获取的内容。
3.mapper 节点,即映射节点,用来链接映射文件,我们来看看该映射文件内容:
<?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="mybatisEntity">
<!--Query all-->
<select id="listUserInfo" resultType="demo.mybatis.entity.UserInfo">
SELECT user_name ,user_addr FROM user_info
</select>
<!--Query by id-->
<select id="getUserInfoById" resultType="demo.mybatis.entity.UserInfo">
SELECT user_name ,user_addr FROM user_info WHERE user_id=#{user_id}
</select><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">add</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="addUserInfo"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="demo.mybatis.entity.UserInfo"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> INSERT INTO user_info(user_name,user_addr)VALUES(#{user_name},#{user_addr}) </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">delete</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">select </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="delUserInfoById"</span><span style="color: rgba(255, 0, 0, 1)"> resultType</span><span style="color: rgba(0, 0, 255, 1)">="demo.mybatis.entity.UserInfo"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> DELETE FROM user_info WHERE user_id=#{user_id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">select</span><span style="color: rgba(0, 0, 255, 1)">></span>
</mapper>
显而易见,这就 SQL 的增删改查。
通过如上分析,我们在文章开始提出的三个问题,现在基本可以解决了。
Q3:Mybatis 为什么能实现松耦合?
从如上分析,我们知道,使用 mybatis 作为 ORM 框架开发时,我们的 SQL 语句都写在 xml 配置文件中 (如上文的 userInfo-config.xml),从而解决了传统硬编码的
强耦合问题,巧妙地实现了从“硬编码”到“软编码”的过程。
除了松耦合的好处之外,有经验的开发人员应该清楚,硬编码存在一个重大问题,即当改变 SQL 代码后,需要重新编译、打包、部署等后,程序方可运行起来,
而通过可配置化的 xml 方式实现的 SQL 语句,却不需要。
Q4: 为什么半自动化的 Mybatis 比自动化的 Hibernate 受欢迎?
从功能上讲,Hibernate 是非常强大的,但其有存在一些比较难以解决的问题:
(1) 学习成本大。对于新手,学习 Hibernate 的时间成本比 Mybatis 大很多,Mybatis 很快就上手了
(2) 笨重。Hibernate 强大的另一面,折射出其笨重的一面
(3) 封装 SQL。Hibernate 封装 SQL,只向用户提供 API 接口,是造成其不灵活的根本因素
然而,mybatis 却将 SQL 独立出来,让用户自定义。
通过如上对比,之所以说 Hibernate 自动化,因为 SQL 生成,解析,执行等都是由 Hibernate 自动生成的;
之所以说 Mybatis 半自动化,是因为 SQL 语句需要用户自定义,SQL 的解析,执行等工作由 Mybatis 执行。
可以这么说,传统的 jdbc 是手工的,Hibernate 是自动化的,而 Mybati 是基于 jdbc 和 Hibernate 的半自动化 ORM 框架。
二 完整 Mybatis CRUD
(一)创建 Web Application 项目
打开 Intellij IDEA=>Create New Project=>Java Enterprise=> 勾选 Web Application=>Next=>
给项目命名 MybatisCRUD=>Finish
(二)导入 jar 包
这里主要导入两个 jar 包:MySQL 驱动 jar 包和 Mybatis jar 包
Project Structure(Ctrl+Alt+Shift+S)=>Modules=>MybatisCRUD=>Dependencies=> 选择 JARS or directories...
成功导入后的结构如下:
(三)创建测试数据
#创建数据库 DROP DATABASE IF EXISTS db_test CREATE DATABASE db_test#创建数据表
DROP TABLE IF EXISTS User_InfoCREATE TABLE user_info
(
user_id INT(5) AUTO_INCREMENT PRIMARY KEY NOT NULL,# 用户 id
user_name VARCHAR(50) NOT NULL,# 用户名
user_addr VARCHAR(100) NOT NULL #地址
)#插入模拟数据
INSERT INTO user_Info(user_name,user_addr)
VALUES('A','SH-PuDong'),('B','SH-YangPu'),
('C','SH-QingPu'),('D','SH-XuHui')
(四)创建 UserInfo 实体
package demo.mybatis.entity;public class UserInfo {
String user_name;
String user_addr;</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getUser_name() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> user_name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setUser_name(String user_name) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.user_name =<span style="color: rgba(0, 0, 0, 1)"> user_name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getUser_addr() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> user_addr; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setUser_addr(String user_addr) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.user_addr =<span style="color: rgba(0, 0, 0, 1)"> user_addr; }
}
(五)创建三个资源文件
1.jdbc.property
#mysql
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db_test?characterEncoding=UTF-8
username=root
password=root
2.mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--属性--> <properties resource="demo/mybatis/resources/jdbc.properties"> </properties><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">环境</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">environments </span><span style="color: rgba(255, 0, 0, 1)">default</span><span style="color: rgba(0, 0, 255, 1)">="development"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">environment </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="development"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">transactionManager </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="JDBC"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">dataSource </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="POOLED"</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="driver"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${driver}"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="url"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${url}"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="username"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${username}"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">property </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="password"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="${password}"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">dataSource</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">environment</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">environments</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">映射</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mappers</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">mapper </span><span style="color: rgba(255, 0, 0, 1)">resource</span><span style="color: rgba(0, 0, 255, 1)">="demo/mybatis/resources/userInfo-config.xml"</span><span style="color: rgba(0, 0, 255, 1)">/></span> <span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">mappers</span><span style="color: rgba(0, 0, 255, 1)">></span>
</configuration>
3.userInfo.config.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="mybatisEntity">
<!--无条件查询-->
<select id="listUserInfo" resultType="demo.mybatis.entity.UserInfo">
SELECT user_name ,user_addr FROM user_info
</select>
<!--通过 id 查询-->
<select id="getUserInfoById" resultType="myUserInfo">
SELECT user_name ,user_addr FROM user_info WHERE user_id=#{user_id}
</select><span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">add</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">insert </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="addUserInfo"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> INSERT INTO user_info(user_name,user_addr)VALUES(#{user_name},#{user_addr}) </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">insert</span><span style="color: rgba(0, 0, 255, 1)">></span> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)">delete</span><span style="color: rgba(0, 128, 0, 1)">--></span> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">delete </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="delUserInfoById"</span><span style="color: rgba(0, 0, 255, 1)">></span><span style="color: rgba(0, 0, 0, 1)"> DELETE FROM user_info WHERE user_id=#{user_id} </span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">delete</span><span style="color: rgba(0, 0, 255, 1)">></span>
</mapper>
(六)CRUD
1. 查询
package demo.mybatis.Test;import demo.mybatis.entity.UserInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class TestMybatis {
public static void main(String[] args) throws IOException {
//读取配置文件内容
String resource = "demo/mybatis/resources/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建 SqlSessionFactory
SqlSessionFactory sqlSF = new SqlSessionFactoryBuilder().build(inputStream);
//创建 SqlSession
SqlSession sqlS = sqlSF.openSession();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">根据id查询</span> <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">查询user_id=2的记录</span> List<UserInfo> list = sqlS.selectList("getUserInfoById", 2<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (UserInfo user : list) { System.out.println(</span>"UserName:" + user.getUser_name() + ",Addr:" +<span style="color: rgba(0, 0, 0, 1)"> user.getUser_addr()); } } </span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> { sqlS.close(); } }
}
2. 添加
package demo.mybatis.Test;import demo.mybatis.entity.UserInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class TestMybatis {
public static void main(String[] args) throws IOException {
//读取配置文件内容
String resource = "demo/mybatis/resources/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建 SqlSessionFactory
SqlSessionFactory sqlSF = new SqlSessionFactoryBuilder().build(inputStream);
//创建 SqlSession
SqlSession sqlS = sqlSF.openSession();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">添加数据</span> <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ UserInfo addUser </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UserInfo(); addUser.setUser_name(</span>"E"<span style="color: rgba(0, 0, 0, 1)">); addUser.setUser_addr(</span>"BJ-DongCheng"<span style="color: rgba(0, 0, 0, 1)">); sqlS.selectList(</span>"addUserInfo"<span style="color: rgba(0, 0, 0, 1)">,addUser); List</span><UserInfo> list=sqlS.selectList("listUserInfo"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (UserInfo user :list){ System.out.println(</span>"UserName:"+user.getUser_name()+",Addr:"+<span style="color: rgba(0, 0, 0, 1)">user.getUser_addr()); } }</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> { sqlS.close(); } }
}
3. 删除
package demo.mybatis.Test;import demo.mybatis.entity.UserInfo;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class TestMybatis {
public static void main(String[] args) throws IOException {
//读取配置文件内容
String resource = "demo/mybatis/resources/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建 SqlSessionFactory
SqlSessionFactory sqlSF = new SqlSessionFactoryBuilder().build(inputStream);
//创建 SqlSession
SqlSession sqlS = sqlSF.openSession();</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">删除</span> <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">{ sqlS.selectList(</span>"delUserInfoById",12<span style="color: rgba(0, 0, 0, 1)">); List</span><UserInfo> list=sqlS.selectList("listUserInfo"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (UserInfo user :list){ System.out.println(</span>"UserName:"+user.getUser_name()+",Addr:"+<span style="color: rgba(0, 0, 0, 1)">user.getUser_addr()); } }</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)"> { sqlS.close(); } }
}
(七)代码目录结构
三 版权区
感谢您的阅读,若有不足之处,欢迎指教,共同学习、共同进步。
博主网址:http://www.cnblogs.com/wangjiming/。
极少部分文章利用读书、参考、引用、抄袭、复制和粘贴等多种方式整合而成的,大部分为原创。
如您喜欢,麻烦推荐一下;如您有新想法,欢迎提出,邮箱:2098469527@qq.com。
可以转载该博客,但必须著名博客来源。