MyBatis入门
一、ORM和JDBC对比
JDBC | ORM |
---|---|
编程复杂 | 相对简单 |
需要手动关闭连接 | 引入连接池,不需要手动关闭 |
业务与技术代码耦合 | 代码解耦 |
二、MyBatis和Hibernate对比
MyBatis | Hibernate |
---|---|
半自动ORM框架 | 全自动ORM框架 |
高度灵活,必须写SQL | 全表映射不方便,无法自定义组装SQL,不要写SQL |
基于SQL优化方便 | HQL黑盒封装,调优复杂 |
开发人员自定义SQL | 复杂SQL支持若 |
性能好 | 性能相对低 |
三、MyBatis三要素
- POJO:Java实体类
- 映射规则:每一个Java接口对应一个xml文件,接口文件中方法对应xml中的一个节点,节点内包含sql语句和映射规则
- SQL语句:对接数据库
四、MyBatis核心类引入
- SqlSessionFactoryBuilder:通过建造者模式,辅助构建SqlSessionFactory对象
- SqlSessionFactory:创建SqlSession的工厂,单例模式,存在与整个程序的生命周期
- SqlSession:代表一次数据库链接,线程不安全,需要线程独享(方法级),具备执行sql的能力
- SQL Mapper:由java接口文件和Xml文件组成,包含需要执行上午sql语句和结果集映射,方法级别生命周期,一次SqlSession可能会访问多次数据库。
五、入门案例
5.1 Maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.19</version>
</dependency>
<!-- mybatis相关依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1-moping</version>
</dependency>
5.2 创建属性文件
- 使用属性文件db.properties保存数据库配置,然后再MyBatis的主配置文件引入该文件,保存在resources目录下面
jdbc_driver=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://192.168.11.27:3306/demo?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
jdbc_username=root
jdbc_password=introcks1234
5.3 创建MyBatis配置文件
- MyBatis主配置文件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="db.properties"/>
<!--配置environment环境 -->
<environments default="development">
<!-- 环境配置1,每个SqlSessionFactory对应一个环境 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc_driver}"/>
<property name="url" value="${jdbc_url}"/>
<property name="username" value="${jdbc_username}"/>
<property name="password" value="${jdbc_password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射文件,mapper的配置文件 -->
<mappers>
<!--直接映射到相应的mapper文件 -->
<mapper resource="mybatis/mapper/ItemMapper.xml"/>
</mappers>
</configuration>
5.4 创建xml映射文件
- ItemMapper.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="item">
<insert id="addItem" parameterType="com.intellif.mozping.entity.Item">
insert into tb_item(id,description)values(#{id},#{description})
</insert>
<delete id="deleteItemById" parameterType="java.lang.Integer">
DELETE FROM tb_item where id = #{id}
</delete>
<update id="updateItemById" parameterType="com.intellif.mozping.entity.Item">
UPDATE tb_item set description=#{description} where id = #{id}
</update>
<select id="findItemById" resultType="com.intellif.mozping.entity.Item" parameterType="java.lang.Integer">
SELECT * FROM tb_item where id = #{id};
</select>
</mapper>
5.5 测试
- 注意这里的测试还没有和Java接口文件有任何关系,我们获取sqlsession之后,直接通过
SqlSession来调用xml映射文件里面的sql语句,sqlSession.insert方法的第一个参数就是定位xml文件中sql语句,第二个参数就是调用Sql的传参,因此我们看到其实MyBatis在没有Java接口文件的情况下是可以直接通过映射文件操作数据库的,而这就是ibatis的编程模式,mybatis只是基于此增加了java接口,使得我们可以面向接口编程,在有Java接口文件的情况下无非就是根据接口方法名去找xml中的Sql而已,默认我们在Java接口文件中的方法名和xml中的id是要一致的。
@Data
public class Item {
private int id;
private String description;
}
public class Test01 {
@Test
public void add() throws IOException {
String resource = "mybatis/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 1.读取mybatis配置文件创SqlSessionFactory
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(inputStream).openSession();
Item item = new Item();
item.setId(1);
item.setDescription("No 1");
int rowAffected = sqlSession.insert("item.addItem", item);
System.out.println("The rows be affected :" + rowAffected);
//显示提交事务
sqlSession.commit();
sqlSession.close();
}
}
打印:The rows be affected :1
数据库也有新记录插入。
5.6 优化
- SqlSessionFactory在程序生命周期中是单例的,因此可以抽取出来
public class SqlSessionFactoryUtil {
private static SqlSessionFactory factory;
public static SqlSessionFactory getSqlSessionFactoryInstace() {
if (factory == null) {
InputStream in = null;
try {
in = Resources.getResourceAsStream("mybatis/mybatis-config.xml");
} catch (Exception e) {
e.printStackTrace();
}
synchronized (SqlSessionFactoryUtil.class) {
if (factory == null) {
factory = new SqlSessionFactoryBuilder().build(in);
}
}
}
return factory;
}
}
5.7 完整增删改查
public class Test01 {
@Test
public void add() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
Item item = new Item();
item.setId(1);
item.setDescription("No 1");
int rowAffected = sqlSession.insert("item.addItem", item);
System.out.println("The rows be affected :" + rowAffected);
//显示提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void query() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
List<Object> list = sqlSession.selectList("item.findItemById", 1);
for (Object obj : list) {
System.out.println(((Item) obj).toString());
}
}
@Test
public void update() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
Item item = new Item();
item.setId(1);
item.setDescription("No 1 be updated");
int rowAffected = sqlSession.update("item.updateItemById", item);
System.out.println("The rows be affected :" + rowAffected);
//显示提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void delete() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
int rowAffected = sqlSession.delete("item.deleteItemById", 1);
System.out.println("The rows be affected :" + rowAffected);
//显示提交事务
sqlSession.commit();
sqlSession.close();
}
}
5.8 使用代理的方式访问
- 在之前我们小结了,我们没有和Java接口有任何的关系,我们直接就是使用Mybatis加载它的主配置文件,在主配置文件中配置了xml映射文件,然后获得了sqlsession之后,我们就可以通过xml中的id和namespace来访问调用里面的sql语句了。相当于是sqlsession —> xml —> DB
但是我们一般是通过Java接口来使用的,而且通常我们在Java中的接口名字和xml中的id是保持一致的,这个过程是如何实现的,具体深层原理我们暂时无法捋清楚,但是底层其实是使用了动态代理,这里手动使用动态代理来做一次数据库的访问(如果我们做到了通过接口访问数据库,里面使用动态代理技术,那么实际上这里看起来就像是已经做到了mybatis框架所做的事情,而mybatis实际上就是这么干的)。
5.8.1 创建Java接口
public interface ItemMapper {
int addItem(Item item);
int deleteItemById(int id);
int updateItemById(Item item);
List<Object> findItemById(int id);
}
5.8.2 通过Java接口访问数据库
@Test
public void addInterface() {
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
Item item = new Item();
item.setId(1);
item.setDescription("No 1");
ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);
int rowAffected = mapper.addItem(item);
System.out.println("The rows be affected :" + rowAffected);
//显示提交事务
sqlSession.commit();
sqlSession.close();
}
验证保存数据成功,但是这里注意需要将xml中的命名空间修改一下,前面是item,这里需要修改为ItemMapper的全类名:
com.intellif.mozping.dao.ItemMapper
5.8.3 自己通过代理方式
- 下面代码中,我们通过动态代理生产一个ItemMapper接口对象,这个对象调用addItem方法,但是实际上底层,我们是通过动态代理
对象来真正的访问数据库的,我们获得了mapper.addItem这个方法的全限定名称,实际上就是xml文件中的namespace+id,通过这个来调用,是不是又回到了之前的sqlsession的最原始的不需要Java接口的工作方式,调用方式和前面的add都是一模一样的,这样我们就可以简单的通过接口来访问数据库了。而实际上MyBatis框架就是按照这样的思想来做的,程序员面向接口编程,调用接口中的方法,底层通过动态代理创建了代理对象去真正的访问数据库,通过接口方法Mybatis会知道你想调用xml中的哪一个sql,然后让代理对象去做,底层还是sqlsession在做一系列的事情,但是却简化了编程的工作,对于单纯的使用,我们甚至可以不care sqlsession的存在。
@Test
public void proxyMybatis() {
ItemMapper mapper = (ItemMapper) Proxy.newProxyInstance(ItemMapper.class.getClassLoader()
, new Class[]{ItemMapper.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(ItemMapper.class.getName() + "." + method.getName());
Object obj = null;
for (Object object : args) {
System.out.println(object);
obj = object;
}
// 实现逻辑
SqlSession sqlSession = SqlSessionFactoryUtil.getSqlSessionFactoryInstace().openSession();
int result = sqlSession.insert(ItemMapper.class.getName() + "." + method.getName(), obj);
//显示提交事务
sqlSession.commit();
sqlSession.close();
return result;
}
});
Item item = new Item();
item.setId(1);
item.setDescription("No 1");
int rowAffected = mapper.addItem(item);
}
- 对比动态代理方式的实现代码和通过Java接口访问数据库的代码,我们完全可以把创建代理对象的代码和下面的代码等价起来
,我们看到前后的代码都是一致的,我们可以这样理解sqlSession.getMapper(ItemMapper.class)其实就是创建了一个ItemMapper
类型的代理对象,后期通过mapper.addItem的方式访问数据库,其实都是代理对象去做的,代理对象通过方法名找到xml中的sql来执行。如果再结合Spring,通过注解实现代理对象的创建并交给IOC容器去管理,那么将大大简化代码。
ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);
5.8.4 注意事项
- 在使用Java接口的方式访问数据库我们也发现了几点需要注意的地方
序号 | 注意点 |
---|---|
1 | 接口方法的全限定名称必须和xml中的namespace+id保持一致,接口类名对应namespace,方法名对应id |
2 | 映射文件的名称必须和接口的名称一致,映射文件是XXX.xml,接口文件是XXX.java |
六、小结
- 我们首先通过简单的入门案例了解了mybatis的使用,在测试代码中我们用ibatis的编程方式来访问数据库,然后使用动态代理实现了使用接口访问数据库,而mybatis做
的工作就是基于ibatis的方式,让程序员可以面向接口编程,其底层也是使用动态代理来实现,这些细节我们在后循序渐进的学习。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。