spring整合mybatis原理

1.MyBatis整合Spring实现

我们先来实现MyBatis和Spring的整合操作。

1.1什么事MyBatis?

MyBatis 是一个可以自定义 SQL、存储过程和高级映射的持久层框架。

1.2添加相关的依赖

<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>2.0.4</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-orm</artifactId>
	<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>5.1.6.RELEASE</version>
</dependency>
<dependency>

2.创建数据库

use mydb;

drop table if exists t_user;
-- 创建表
create table t_user(
uid int primary key auto_increment,
username varchar(20),
password varchar(20),
phone varchar(11),
address varchar(50)
);


insert into t_user(username,password,phone,address) values('张三','666','18965423548','南阳');
insert into t_user(username,password,phone,address) values('李四','333','18754263548','许昌');
insert into t_user(username,password,phone,address) values('小美','123','18565234759','信阳');

select * from t_user;

3.创建项目并导入所需jar包,并创建结构如下

 4.创建实体类

package com.zhao.bean;

public class User {
    private Integer uid;
    private String username;
    private String password;
    private String phone;
    private String address;

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "uid=" + uid +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", phone='" + phone + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

5.创建dao层的接口及其mapper映射文件

接口类

package com.zhao.dao;

import com.zhao.bean.User;

import java.util.List;

public interface UserDao {
    //全查
    List<User> selectAll();

}

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="com.zhao.dao.UserDao">
    <select id="selectAll" resultType="user">
       select * from t_user;
    </select>
</mapper>

5.1 Mapper DAO层开发规范

  1. 接口的全路径要和映射文件的namespace保持一致

  2. 接口的方法名要和映射文件中的statementId保持一致

  3. 接口方法的参数类型,返回类型要和映射文件中的parameterType,resultType保持一致     d

  4. 接口和映射文件的名字最好保持一致 例如:UserMapper.java/UserMapper.xml

  5. 接口和映射文件最好放到同一个目录

5.2 Mybatis 动态 sql 是做什么的?都有哪些动态 sql?能简述一下动态 sql 的执行原理不?

  1. Mybatis 动态 sql 可以让我们在 Xml 映射文件内,以标签的形式编写动态 sql,完成逻辑判断和动态拼接 sql 的功能。

  2. Mybatis 提 供 了 9 种 动 态 sql 标 签 : trim|where|set|foreach|if|choose|when|otherwise|bind。

  3. 其执行原理为,使用 OGNL 从 sql 参数对象中计算表达式的值,根据表达式的值动态拼接 sql,以此来完成动态 sql 的功能。

5.3 #{}和${}的区别是什么?

  1. #{}是预编译处理,${}是字符串替换。
  2. Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;

  3. Mybatis 在处理${}时,就是把${}替换成变量的值。

  4. 使用#{}可以有效的防止 SQL 注入,提高系统安全性。

6.创建service接口及其实现类

service接口

package com.zhao.service;

import com.zhao.bean.User;

import java.util.List;

public interface UserService {
    List<User> findAll();

}

service接口实现类

package com.zhao.service.impl;

import com.zhao.bean.User;
import com.zhao.dao.UserDao;
import com.zhao.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserDao userDao;

    @Override
    public List<User> findAll() {
        return userDao.selectAll();
    }
}

7.创建mybatis配置文件

由于整合时相关连接数据库 / 实体类起别名 / 扫描 mapper 文件等操作都在 spring 配置文件中定义, 所以此处只剩日志的配置
<?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>
    <!--1.配置mybatis的运行 此处配置运行时使用log4j-->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>
</configuration>

8.创建Spring配置文件

添加Spring的配置文件,并在该文件中实现和Spring的整合操作

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描注解:1)在service的接口实现类定义注解 2)dao接口没有实现类,直接在接口上定义
    注解,通过下来自动获得代理对象-->
    <context:component-scan base-package="com.zhao" />
    <!--1.定义连接数据库的数据源DriverManagerDataSource:实际开发使用第三方连接池管理数据
    源-->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--2.在IOC容器中定义SqlSessionFactoryBean,配置数据源-->
    <bean id="factoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--给实体类起别名-->
        <property name="typeAliasesPackage" value="com.zhao.bean" />
        <!--加载mybatis的核心配置:如果不需要设置mybatis的运行配置,则不需要加载-->
        <property name="configLocation" value="mybatis.xml" />
    </bean>
    <!--3.在IOC容器中定义MapperScannerConfigurer 用来扫描dao层接口的mapper文件-->
    <bean id="scanner"
          class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.zhao.dao"/>
    </bean>
</beans>

8.1 整合Spring的原理

把MyBatis集成到Spring里面,是为了进一步简化MyBatis的使用,所以只是对MyBatis做了一些封装,并没有替换MyBatis的核心对象。也就是说:MyBatis jar包中的SqlSessionFactory、SqlSession、MapperProxy这些类都会用到。mybatis-spring.jar里面的类只是做了一些包装或者桥梁的工作。


只要我们弄明白了这三个对象是怎么创建的,也就理解了Spring继承MyBatis的原理。我们把它分成三步:

  • SqlSessionFactory在哪创建的。
  • SqlSession在哪创建的。
  • 代理类在哪创建的。

8.1.1 SqlSessionFactory

首先我们来看下在MyBatis整合Spring中SqlSessionFactory的创建过程,查看这步的入口在Spring 的配置文件中配置整合的标签中

 我们进入SqlSessionFactoryBean中查看源码发现,其实现了InitializingBean 、FactoryBean、ApplicationListener 三个接口

 

 对于这三个接口,学过Spring生命周期的小伙伴应该清楚他们各自的作用

项目ValueValue
接口方法作用
FactoryBeangetObject()返回由FactoryBean创建的Bean实例
InitializingBeanafterPropertiesSet()bean属性初始化完成后添加操作
ApplicationListeneronApplicationEvent()对应用的事件进行监听

8.1.1.1  afterPropertiesSet

我们首先来看下 afterPropertiesSet 方法中的逻辑

 public SqlSessionFactory getObject() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }
        return this.sqlSessionFactory;
}

可以发现在afterPropertiesSet中直接调用了buildSqlSessionFactory方法来实现 sqlSessionFactory对象的创建

在这里插入图片描述

 方法小结一下:通过定义一个实现了InitializingBean接口的SqlSessionFactoryBean类,里面有一个afterPropertiesSet()方法会在bean的属性值设置完的时候被调用。Spring在启动初始化这个Bean的时候,完成了解析和工厂类的创建工作。
8.1.1.2  getObject

另外SqlSessionFactoryBean实现了FactoryBean接口。
FactoryBean的作用是让用户可以自定义实例化Bean的逻辑。如果从BeanFactory中根据Bean的ID获取一个Bean,它获取的其实是FactoryBean的getObject()返回的对象。
也就是说,我们获取SqlSessionFactoryBean的时候,就会调用它的getObject()方法。

public SqlSessionFactory getObject() throws Exception {
 if (this.sqlSessionFactory == null) {
		this.afterPropertiesSet();
 }
 return this.sqlSessionFactory;

getObject方法中的逻辑就非常简单,返回SqlSessionFactory对象,如果SqlSessionFactory对象为 空的话就又调用一次afterPropertiesSet来解析和创建一次。

8.1.1.3 onApplicationEvent

实现ApplicationListener接口让SqlSessionFactoryBean有能力监控应用发出的一些事件通知。比如 这里监听了ContextRefreshedEvent(上下文刷新事件),会在Spring容器加载完之后执行。这里做的 事情是检查ms是否加载完毕。

public void onApplicationEvent(ApplicationEvent event) {
if (this.failFast && event instanceof ContextRefreshedEvent) { 
	this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
	}
}

8.1.2 SqlSession

8.1.2.1 DefaultSqlSession的问题

在前面介绍MyBatis的使用的时候,通过SqlSessionFactory的open方法获取的是
DefaultSqlSession,但是在Spring中我们不能直接使用DefaultSqlSession,因为DefaultSqlSession是 线程不安全的。所以直接使用会存在数据安全问题,针对这个问题的,在整合的MyBatis-Spring的插件包中给我们提供了一个对应的工具SqlSessionTemplate。

 也就是在我们使用SqlSession的时候都需要使用try catch 块来处理

try (SqlSession session = sqlSessionFactory.openSession()) {
	// 你的应用逻辑代码
}
	// 或者
SqlSession session = null;
 try {
	session = sqlSessionFactory.openSession();
	// 你的应用逻辑代码
}finally{
	session.close();
}

8.1.2.2 SqlSessionTemplate

在mybatis-spring的包中,提供了一个线程安全的SqlSession的包装类,用来替代SqlSession,这个类就是SqlSessionTemplate。因为它是线程安全的,所以可以在所有的DAO层共享一个实例(默认是单例的)。

 总结一下:因为DefaultSqlSession自己做不到每次请求调用产生一个新的实例,我们干脆创建一个代理 类,也实现SqlSession,提供跟DefaultSqlSession一样的方法,在任何一个方法被调用的时候都先创建 一个DefaultSqlSession实例,再调用被代理对象的相应方法。
MyBatis还自带了一个线程安全的SqlSession实现:SqlSessionManager,实现方式一样,如果不集成到Spring要保证线程安全,就用SqlSessionManager。

9.创建测试类

package com.zhao.test;

import com.zhao.bean.User;
import com.zhao.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class UserDaoTest {
    UserService userService;

    @Test
    public void testSelectAll(){
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
        userService=context.getBean(UserService.class);

        List<User> userList = userService.findAll();
        for (User user:userList){
            System.out.println(user);
        }
    }
}

测试结果

总结一下,Spring是怎么把MyBatis继承进去的?

  1. 提供了SqlSession的替代品SqlSessionTemplate,里面有一个实现了实现了InvocationHandler的内部SqlSessionInterceptor,本质是对SqlSession的代理。
  2. 提供了获取SqlSessionTemplate的抽象类SqlSessionDaoSupport。
  3. 扫描Mapper接口,注册到容器中的是MapperFactoryBean,它继承了SqlSessionDaoSupport,可 以获得SqlSessionTemplate。
  4. 把Mapper注入使用的时候,调用的是getObject()方法,它实际上是调用了SqlSessionTemplate的getMapper()方法,注入了一个JDK动态代理对象。
  5. 执行Mapper接口的任意方法,会走到触发管理类MapperProxy,进入SQL处理流程。

核心对象:

对象生命周期
SqlSessionTemplateSpring中SqlSession的替代品,是线程安全的
SqlSessionDaoSupport用于获取SqlSessionTemplate
SqlSessionInterceptor(内部类)代理对象,用来代理DefaultSqlSession,在SqlSessionTemplate中使用
MapperFactoryBean代理对象,继承了SqlSessionDaoSupport用来获取SqlSessionTemplate
SqlSessionHolder控制SqlSession和事务

设计模式总结:

设计模式
工厂模式SqlSessionFactory、ObjectFactory、MapperProxyFactory
建造者模式XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuidler
单例模式SqlSessionFactory、Configuration、ErrorContext
代理模式绑定:MapperProxy延迟加载:ProxyFactory 插件:PluginSpring集成MyBaits: SqlSessionTemplate的内部SqlSessionInterceptorMyBatis自带连接池:PooledConnection日志打印:ConnectionLogger、StatementLogger
 
适配器模式Log,对于Log4j、JDK logging这些没有直接实现slf4j接口的日志组件,需要适配器
模板方法BaseExecutor、SimpleExecutor、BatchExecutor、ReuseExecutor
装饰器模式LoggingCache、LruCache对PerpetualCacheCachingExecutor对其他Executor
责任链模式Interceptor、InterceptorChain

9.MyBatis 的好处是什么?

  1. MyBatis 把 sql 语句从 Java 源程序中独立出来,放在单独的 XML 文件中编写,给程序的维护带来了很大便利。

  2. MyBatis 封装了底层 JDBC API 的调用细节,并能自动将结果集转换成 Java Bean 对象, 大大简化了 Java 数据库编程的重复工作。

  3. 因为 MyBatis 需要程序员自己去编写 sql 语句,程序员可以结合数据库自身的特点灵活控制 sql 语句,因此能够实现比 Hibernate 等全自动 orm 框架更高的查询效率,能够完成复杂查询。

10.接口绑定有几种实现方式,分别是怎么实现的?

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select@Update 等注解里面包含 Sql 语句来绑定,另外一种就是通过 xml 里面写 SQL 来绑定,在这种情况下,要指定 xml 映射文件里面的 namespace 必须为接口的全路径名.

11.当实体类中的属性名和表中的字段名不一样,如果将查询的结果封装到指定 pojo?

  1. 通过在查询的 sql 语句中定义字段名的别名。

  2. 通过<resultMap>来映射字段名和实体类属性名的一一对应的关系。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值