Mybatis框架高级

1,几个核心对象

1.1, SqlSessionFactory

在这里插入图片描述
在这里插入图片描述

  1. SqlSessionFactory通过类名可以看出其作用是通过工厂模式创建SqlSession.
  2. 先看看SqlSessionFactory的类结构和相关的方法。SqlSessionFactory实现了有两个子类分别是DefaultSqlSessionFactory和SqlSessionManager.
  3. 其中,DefaultSqlSessionFactory是构建Sqlsession最常用的一个类,可以直接通过构造器方式进行实例化,也可以使用mybatis提供的SqlSessionFactoryBuilder来构建一个SqlSessionFactory.
  4. SqlSessionFactory重载了多个openSession()方法,其目的就是希望实例化出一个Sqlsession,并且通过不同的参数形式构建出不同功能的Sqlsession,可以为即将实例化出的SqlSession设置事务隔离等级,执行器类型,是否自动提交等.

1.2, SqlSession对象

在这里插入图片描述
在这里插入图片描述

1.3, 类型转换器TypeHandler

当我们借助mybatis将对象更新到数据库,或者从数据库获取数据,然后映射到内存中的对象时,有一个非常关键的步骤.
就是把jdbc数据类型与java对象的数据类型相互转换,这种转换在MyBatis中会借助TypeHandler类型处理器实现。
在这里插入图片描述
MyBatis中已提供了很多内置的类型转换器(参考TypeHandlerRegistry.class),如:BooleanTypeHandler,ByteTypeHandler,ShortTypeHandler 等…
在TypeHandlerRegistry类中进行注册就行,但当我们业务中需要一些特殊的类型转换时,还是需要自定义类型转换器。

1.4, 拦截器Interceptor

Mybatis中提供一种插件应用机制,本质上就是采用责任链模式InterceptorChain,通过动态代理使用多个拦截器,形成拦截器链,为目标对象添加更多扩展功能,例如对所有dao接口方法做一个日志记录和接口耗时记录.

拦截器对象编写,配置及创建:

  1. 实现拦截器接口Interceptor,并对其要拦截的对象进行描述
  2. 在mybatis核心配置文件中通过plugin标签,配置自定义拦截器
  3. Mybatis框架初始化时,会创建拦截器对象然后添加到拦截器链,一起执行

拦截器对象应用过程分析:
Mybatis 会在Executor,StatementHandler,ResultSetHandler或ParameterHandler对象创建时,
假如定义了拦截器,就会为指定对象创建代理对象,并在执行代理对象业务方法时,执行Interceptor对象的intercept方法。

例如:Executor对象创建时的时序图如下:
在这里插入图片描述
案例实现:基于拦截器实现分页插件,并掌握市场上PageInterceptor插件的应用原理,大多数是拦截器来实现的。

1.5, 插件Plugin

我们也可以自己定义一个基于代理对象实现的插件功能.这种思想和Spring的AOP是一样的.
在这里插入图片描述

2, Mybatis的设计模式

2.1, 建造者模式

  1. SqlSessionFactoryBuilder :创建SqlSessionFactory,进而产生SqlSession
  2. XMLStatementBuilder: 解析mybatis的映射文件,构建SQL相关的信息,并封装给MapperedStatement
  3. XMLConfigBuilder: 解析mybatis配置文件来构建Configuration,读取配置文件信息

2.2, 工厂模式

  1. SqlSessionFactory
  2. TransactionFactory

2.3, 责任链模式

  1. InterceptorChain : 拦截器链,由多个拦截器形成(如: List),可以结合拦截器和动态代理来完成自定义插件.

2.4, 代理模式

  1. UserMapper mapper = sqlSession.getMapper(UserMapper.class);//JDK产生代理对象,可以参考DefaultSqlSession.class

3, Mybatis的缓存

3.1,一级缓存

在这里插入图片描述
在这里插入图片描述
Mybatis 一级缓存应用特点:

  1. 一级缓存默认开启,不需要配置,其生命周期和SqlSession一致
  2. 其本质就是,在内部设计是一个没有容量限定的HashMap(k,v),key就是statement的标识,value就是查到的数据

3.2,二级缓存

在这里插入图片描述

  1. 二级缓存是SqlSessionFactory级别的,
    通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存,此后若再次执行相同的查询语句,结果就会从缓存中获取
  2. 使用步骤:
    a. 在MyBatis的配置文件中开启二级缓存(默认开启)
<setting name="cacheEnabled" value="true"/>
1、true:只读缓存,会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改,这提供了很重要的性能优势
2、false:读写缓存,会返回缓存对象的拷贝(通过序列化),这会慢一些,但是安全,因此默认是false

b. 在MyBatis的映射文件XML中配置cache

<cache/>
cache标签用于声明这个namespace使用二级缓存,并且可以自定义配置。
  1. Mybatis二级缓存应用特点:
    a. 二级缓存允许SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
    b. 在多表查询时,二级缓存可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
    c. 默认的MyBatis Cache实现都是基于本地的,分布式环境下会出现读取到脏数据,建议直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高。
    d. 注意SqlSession关闭之后,一级缓存中的数据才会写入二级缓存
    e. 查询顺序:先查询二级缓存,如果二级缓存没有命中,再查询一级缓存, 如果一级缓存也没有命中,则查询数据库

4, ResultMap的复杂使用

4.1,对象间的关系

分为一对一和一对多,分别使用resultMap提供的不同方案来处理:
一对一:使用association + javaType
一对多:使用collection + ofType

4.2,pom.xml的编写

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.tedu</groupId>
	<artifactId>mybatis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>mybatis</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.4</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>


4.3,准备数据库表

create table user_info(
	id int primary key auto_increment,
	user_name varchar(100),
	user_addr varchar(200),
	user_age int);
insert into user_info values(null,'韩梅梅','上海',20);
insert into user_info values(null,'王海涛','北京',30);
insert into user_info values(null,'张慎政','河南',10);

create table user_extra(
	id int primary key auto_increment,
	user_id int,
	work varchar(100),
	salary double);
insert into user_extra values(null,'1','程序员',100000);
insert into user_extra values(null, '2','教师',1000);
insert into user_extra values(null, '3','CTO',100000);

4.4,准备实体类

UserInfo.java

package domain;

import java.io.Serializable;

public class UserInfo implements Serializable {

    private static final long serialVersionUID = 1182073125744165828L;
    private UserExtra userExtra;

    public UserExtra getUserExtra() {
        return userExtra;
    }

    public void setUserExtra(UserExtra userExtra) {
        this.userExtra = userExtra;
    }

    private Integer id;
    private String userName;
    private String userAddr;
    private Integer userAge;

    public UserInfo() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserAddr() {
        return userAddr;
    }

    public void setUserAddr(String userAddr) {
        this.userAddr = userAddr;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "userExtra=" + userExtra +
                ", id=" + id +
                ", userName='" + userName + '\'' +
                ", userAddr='" + userAddr + '\'' +
                ", userAge=" + userAge +
                '}';
    }

    public Integer getUserAge() {
        return userAge;
    }

    public void setUserAge(Integer userAge) {
        this.userAge = userAge;
    }

    public UserInfo(Integer id, String userName, String userAddr, Integer userAge) {
        this.id = id;
        this.userName = userName;
        this.userAddr = userAddr;
        this.userAge = userAge;
    }
}

UserExtra.java

package domain;

import java.io.Serializable;

public class UserExtra implements Serializable {
    private static final long serialVersionUID = -2054928697926972055L;
    private Integer id;
    private Integer userId;
    private String work;
    private Double salary;

    @Override
    public String toString() {
        return "UserExtra{" +
                "id=" + id +
                ", userId=" + userId +
                ", work='" + work + '\'' +
                ", salary=" + salary +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getWork() {
        return work;
    }

    public void setWork(String work) {
        this.work = work;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    public UserExtra() {
    }

    public UserExtra(Integer id, Integer userId, String work, Double salary) {
        this.id = id;
        this.userId = userId;
        this.work = work;
        this.salary = salary;
    }
}

4.5,一对一

4.5.1 需求

user_extra 和 user_info表是一对一的关系

根据用户查询一对一关系的用户扩展信息,使用association + javaType描述关联表的信息

4.5.2 改造UserInfo类
package cn.tedu.pojo;
import java.util.List;
public class UserInfo {
	private int id;
	private String userName;
	private String userAddr;
	private int userAge;
	
	**//一对一
	private UserExtra userExtra;
	
	public UserExtra getUserExtra() {
		return userExtra;
	}
	public void setUserExtra(UserExtra userExtra) {
		this.userExtra = userExtra;
	}**
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getUserAddr() {
		return userAddr;
	}
	public void setUserAddr(String userAddr) {
		this.userAddr = userAddr;
	}
	public int getUserAge() {
		return userAge;
	}
	public void setUserAge(int userAge) {
		this.userAge = userAge;
	}
	
	**@Override
	public String toString() {
		return "UserInfo [id=" + id + ", userName=" + userName + ", userAddr=" + userAddr + ", userAge=" + userAge
				+ ", userExtra=" + userExtra + "]";
	}**
	
}


4.5.3 创建UserInfoMapper.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="mapper.UserInfoDao">

    <!-- 一对一: 固定搭配association+javaType,如果命名规则是有规则的,就很简单了-->
    <resultMap id="UIUERM" type="domain.UserInfo" autoMapping="true">
        <!--UserInfo类里,一对一的UserExtra,property写属性名,javaType写属性的类型-->
        <association property="userExtra" javaType="domain.UserExtra" autoMapping="true">
        </association>
    </resultMap>
    <select id="getInfo" resultMap="UIUERM">
        select * from user_info a,user_extra b
        where a.id=b.user_id
        and a.id=#{id}
    </select>
</mapper>

4.5.4 引入sqlMapConfig.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">

<!-- mybatis的核心配置文件 -->
 <configuration>
 
 	<!-- 配置别名,在Mapper.xml文件里就可以直接用别名了,不用写那么全 -->
  	<typeAliases>
 		<typeAlias type="cn.tedu.pojo.Dept" alias="Dept"/>
 		<typeAlias type="cn.tedu.pojo.Emp" alias="Emp"/>
 		<typeAlias type="cn.tedu.pojo.UserInfo" alias="UserInfo"/>
 		<typeAlias type="cn.tedu.pojo.UserExtra" alias="UserExtra"/>
 	</typeAliases>
 	
 	<environments default="test">
 		<environment id="test">
 			<transactionManager type="JDBC"></transactionManager>
 			<dataSource type="POOLED">
 				<property name="driver" value="com.mysql.jdbc.Driver"/> 
				<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai" /> 
				<property name="username" value="root"/> 
				<property name="password" value="root"/> 
 			</dataSource>
 		</environment>
 	</environments>
 
 <mappers>
 	<mapper resource="mappers/UserMapper.xml"/>
 	<mapper resource="mappers/DeptMapper.xml"/>
 	<mapper resource="mappers/EmpMapper.xml"/>
 	<mapper resource="mappers/UserInfoMapper.xml"/>
 </mappers>
 
 </configuration>


4.5.5 创建UserInfoDao接口
package mapper;

import domain.UserInfo;

public interface UserInfoDao {
    //根据id获取用户信息,并关联查询出用户的扩展信息
    UserInfo getInfo(Integer id);
}


4.5.6 测试类
package test;

import domain.UserInfo;
import mapper.UserInfoDao;
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 org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class TestRM {
    SqlSessionFactory factory ;
    @Before
    public void init() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
    }
    @Test
    public void selectAll(){
        SqlSession session = factory.openSession();
        UserInfoDao dao = session.getMapper(UserInfoDao.class);
        //根据用户编号,查出用户信息和关联的用户扩展信息
        UserInfo info = dao.getInfo(2);
        //UserInfo{userExtra=UserExtra{id=2, userId=2, work='教师', salary=1000.0}
        // , id=2, userName='王海涛', userAddr='北京', userAge=30}
        System.out.println("info = " + info);
    }
}


4.6,一对多

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值