一、MyBatis的整体架构
MyBatis 的整体架构分为三层 , 分别是基础支持层 、 核心处理层和接口层
![](https://img.haomeiwen.com/i20513332/6d9d7e63e25b12cf.png)
二、基础
1、MyBatis的核心组件
- SqlSessionFactoryBuilder (构造器):它会根据配置信息或者代码来生成 SqlSessionFactory (工厂接口)
- SqlSessionFactory:依靠工厂来生成 SqlSession (会话)
- SqlSession:是一个既可以发送 SQL 去执行并返回接口,也可以获取 Mapper 的接口。
- SQL Mapper:它是 MyBatis 新设计的组件,它是有一个 Java 接口和 XML 文件(或注解)构成的,需要给出对应的 SQL 和映射规则。它负责发送 SQL 去执行,并返回结果。
![](https://img.haomeiwen.com/i20513332/85d7b7d282db5ac9.png)
2、Configuration
这里我们的 Configuration 的类全限定名为 org.apache.ibatis.session.Configuration,它在 MyBatis 中将以一个 Configuration 类对象的形式存在,而这个对象将存在于整个 MyBatis 应用的生命周期中,以便重复读取和运用。在内存中的数据是计算机系统中读取速度最快的,我们可以解析一次配置的 XML 文件保存到 Configuration 对象中,方便我们从这个对象中读取配置信息,性能高。单例占用空间小,基本不占用存储空间,而且可以反复使用。Configuration 类对象保存着我们配置在 MyBatis 的信息。在 MyBatis 中提供了两个 SqlSessionFactory 的实现类, DefualtSqlSessionFactory 和 SqlSessionManager。不过 SqlSessionManager 目前还没有使用,MyBatis 中目前使用的是 DefualtSqlSessionFactory。
![](https://img.haomeiwen.com/i20513332/d483872c4394d6cd.png)
3、映射器
映射器是由 Java 接口和 XML 文件(或注解)共同组成的,它的作用如下:定义参数类型、描述缓存、描述 SQL 语句、定义查询结果和 POJO 的映射关系。
一个映射器的实现方式有两种:一种是通过 XML 的方式实现,一种是通过代码+注解实现。第二种方式实现复杂、代码可读性差等,所以建议使用第一种方式。第一种方式其实就是由一个 Java 接口和一个 XML 文件构成(即 MyBatis 代码生成器生成的一样)。
4、映射器,一个没有实现类的接口怎么能运行呢?
这里需要用到 Java 的动态代理:我们会在 MyBatis 上下文中描述这个接口,而 MyBatis 会为这个接口生成代理类对象,代理对象会根据“接口全路径+方法名”去匹配,找到对应的 XML 文件(或注解)去完成它所需要的任务,返回我们需要的结果。
5、生命周期
想要保证多线程中 MyBatis 的正确性、高性能、没有并发问题,就必须掌握 MyBatis 中 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper 的生命周期。
SqlSessionFactoryBuilder:利用 XML 或者 Java 编码获得资源来构建 SqlSessionFactory 的,通过它可以构建多个 SessionFactory。它的作用就是一个构建起,一旦我们构建了 SqlSessionFactory,它的作用就已经完结了,失去了存在的意义,这是我们就应该毫不犹豫的废弃它,将它回收。所以它的生命周期只存在于方法的局部,它的作用就是生成 SqlSessionFactory 对象。
SqlSessionFactory:作用是创建 SqlSession,而 SqlSession 就是一个会话,相当于 JDBC 中的 Connection 对象。每次应用程序需要访问数据库,我们就要通过 SqlSessionFactory 创建 SqlSession,所以** SqlSessionFactory 应该在 MyBatis 应用的整个生命周期中。而如果我们多次创建同一个数据库的 SqlSessionFactory,则每次创建 SqlSessionFactory 会打开更多的数据库连接(Connection)资源,那么连接资源就很快会被耗尽。因此 SqlSessionFactory 的责任是唯一的,它的责任就是创建 SqlSession,所以我们果断采用单例模式**。如果我们采用多例,那么它对数据库连接的消耗是很大的,不利于我们统一的管理,这样便嗅到了代码的坏味道。
SqlSession:是一个会话,相当于 JDBC 的一个 Connection 对象,它的生命周期应该是在请求数据处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候,我们需要特别的当心,操作数据库需要注意其隔离级别,数据库锁等高级特性。此外,每次创建的 SqlSession 都必须及时关闭它,它长期存在就会使数据库连接池的活动资源减少,对系统性能的影响很大。
Mapper:是一个接口,而没有任何实现类,它的作用是发送 SQL,然后返回我们需要的结果,或者执行 SQL 从而修改数据库的数据,因此它应该在一个 SqlSession 事务方法之内,是一个方法级别的东西。它就如果 JDBC 中的一个 SQL 语句的执行,它最大的范围和 SqlSession 是相同的。
![](https://img.haomeiwen.com/i20513332/70c66dc3728b6dbe.png)
三、配置
1、全部 MyBatis 的配置元素
MyBatis 配置 XML 文件的层次结构。这些层次是不能够颠倒顺序的,如果颠倒顺序,MyBatis 在解析 XML 文件的时候就会出现异常了。
<?xml version="1.0" encoding="UTF-8"?>
<configuration><!--配置-->
<properties/><!--属性-->
<settions/><!--设置-->
<typeAliases/><!--类型命名,不区分大小写-->
<typeHandlers/><!--类型处理器-->
<objectFactory/><!--对象工厂-->
<plugins/><!--插件-->
<environments><!--配置环境-->
<environment>
<transactionManager/><!--事务管理器-->
<dataSource/><!--数据源-->
</environment>
</environments>
<databaseIdProvider/><!--数据库厂商标识-->
<mappers/><!--映射器-->
</configuration>
四、映射器
MyBatis 是针对映射器构造的 SQL 构建的轻量级框架。
1、select 元素
有3中传递多个参数的方式:map;@Param;实体。
这三种方式对比:
- 使用 Map 传递参数。因为 Map 导致代码的可读性下降,会导致后面的扩展和维护比较困难,所以建议少使用这种方式。
- 使用 @Param 注解方式。这种方式可读性高,但是当参数个数很多的时候,同样会导致可读性和维护性下降,所以,当参数个数小于等于 5 时,建议采取这种方式。
- 使用实体 JavaBean 的方式。当参数个数大于 5 时,建议采取这种方式。
2、insert 元素(写demo)
实现主键回填,即执行完新增后返回主键 id。
3、级联(写demo)
级联的延迟加载实现原理是通过动态代理来实现的。有一个动态代理对象,里面保存着相关的 SQL 和参数,一旦我们使用这个代理对象的方法,它会进入到动态代理对象的代理方法里,方法里面会通过发送 SQL 和参数,就可以把对应的结果从数据库里查回来,这便是其实现原理。
4、缓存(写demo)
目前流行的缓存服务器有 MongoDB、Redis、Ehcache 等。无需从磁盘上读入,具有快速读取和使用的特点。如果缓存的命中率高,那么可以极大的提高系统的性能,所以使用缓存的关键在于存储内容访问的命中率。
五、实际操作代码展示
1、利用注解的方式操作数据库
分为4个部分,配置文件、接口(里面带上注解)、实体类、测试类
配置文件
<?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>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<package name="com.bjsxt.pojo"/>
</typeAliases>
<!-- default引用environment的id,当前所使用的环境 -->
<environments default="default">
<!-- 声明可以使用的环境 -->
<environment id="default">
<!-- 使用原生JDBC事务 -->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:7625/ssm"/>
<property name="username" value="root"/>
<property name="password" value="762513499"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.bjsxt.mapper"/>
</mappers>
</configuration>
接口+注解
package com.bjsxt.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import com.bjsxt.pojo.Teacher;
public interface TeacherMapper {
@Select("select * from teacher")
List<Teacher> selAll();
@Insert("insert into teacher values(default,#{name})")
int insOne(Teacher teacher);
@Update("update teacher set name=#{name} where id=#{id}")
int updOne(Teacher teacher);
@Delete("delete from teacher where id=#{0}")
int delById(int id);
}
实体类
package com.bjsxt.pojo;
public class Teacher {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher() {
super();
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + "]";
}
}
测试类
package com.bjsxt.test;
import java.io.IOException;
import java.io.InputStream;
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 com.bjsxt.mapper.TeacherMapper;
import com.bjsxt.pojo.Teacher;
public class test01 {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
TeacherMapper t = session.getMapper(TeacherMapper.class);
// List<Teacher> list = t.selAll();
// System.out.println(list);
Teacher teacher = new Teacher();
teacher.setName("haha");
teacher.setId(1);
// int updOne = t.updOne(teacher);
// System.out.println(updOne);
System.out.println(t.selAll());
t.insOne(teacher);
// int delById = t.delById(2);
// System.out.println(delById);
session.commit();
session.close();
}
}
2、通过xml的方式操作数据库
MyBatis配置文件
<?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>
<!-- default引用environment的id,当前所使用的环境 -->
<environments default="default">
<!-- 声明可以使用的环境 -->
<environment id="default">
<!-- 使用原生JDBC事务 -->
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:7625/ssm"/>
<property name="username" value="root"/>
<property name="password" value="762513499"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/bjsxt/mapper/FlowerMapper.xml"/>
</mappers>
</configuration>
xml配置文件,里面的namespace可以任意配置
<?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">
<!-- namespace:理解成实现类的全路径(包名+类名) -->
<mapper namespace="a.b">
<!-- id:方法名
parameterType:定义参数类型
resultType:返回值类型
如果方法返回值是list,在resultType中写List的泛型,因为
mybatis对jdbc进行封装,一行一行的读取数据
-->
<select id="selectAll" resultType="com.bjsxt.pojo.Flower">
select * from flower
</select>
<select id="selectById" resultType="int">
select count(*) from flower
</select>
<select id="c" resultType="com.bjsxt.pojo.Flower">
select id,name,price,production from flower
</select>
</mapper>
实体类
package com.bjsxt.pojo;
public class Flower {
private int id;
private String name;
private double price;
private String production;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getProduction() {
return production;
}
public void setProduction(String production) {
this.production = production;
}
public Flower() {
super();
}
public Flower(int id, String name, double price, String production) {
super();
this.id = id;
this.name = name;
this.price = price;
this.production = production;
}
@Override
public String toString() {
return "Flower [id=" + id + ", name=" + name + ", price=" + price + ", production=" + production + "]";
}
}
测试类
package com.bjsxt.test01;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test01 {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
//使用设计工厂模式
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//生产SqlSession
SqlSession session=factory.openSession();
/*List<Flower> list=session.selectList("a.b.selectAll");
for(Flower flower:list) {
System.out.println(flower.toString());
}*/
/*int n = session.selectOne("a.b.selectById");
System.out.println(n);*/
//Map<Object, Object> map = session.selectMap("a.b.c", "name");先显示名字,再显示全部,适合根据名字快速查询
Map<Object, Object> map = session.selectMap("a.b.c", "price");//先显示价格,再显示全部
System.out.println(map);
session.close();
}
}
网友评论