Mybatis产生的背景
如果使用过JDBC操作关系型数据库,就会知道数据库连接配置、sql语句、参数、结果解析的过程存在硬编码的问题,同时每次执行sql就会创建一个数据库的连接,也存在连接频繁创建和销毁的问题。Mybatis就是对JDBC的封装,也解决了JDBC存在的这些问题,它是一个半自动的ORM(关系对象映射)的框架。
什么是ORM?
对象关系映射是指java的对象与数据库表中字段的映射,因为数据类型、列的名称与对象名称有对应的命名约定,所以数据库中查询的数据到java对象以及查询参数到列字段转换的过程,mybatis是一个半自动的对象关系映射框架,因为sql语句还是需要手动编写。
springboot如何集成
配置依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
配置数据库连接
spring:
application:
name: test-service
redis:
host: localhost
port: 6379
datasource:
url: jdbc:mysql://localhost:3306/order?useUnicode=true&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: order
password: ***
mybatis:
#配置别名
type-aliases-package: com.person.mybatis.dao
#配置扫描映射文件的路径
mapper-locations: classpath:mapper/*Mapper.xml
查询操作示例
配置Mapper.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= "com.person.mybatis.mapper.OrderMapper">
<insert id="addOrder" parameterType= "com.person.mybatis.dao.Order">
INSERT INTO t_order (user_id,commodity_code,count,amount) VALUES (# {userId}, # {commodityCode}, # {count}, # {amount})
</insert>
<select id="getOrderById" parameterType="int" resultType="com.person.mybatis.dao.Order">
SELECT * FROM t_order WHERE id=#{id}
</select>
</mapper>
Mapper 接口
@Mapper
public interface OrderMapper {
Order getOrderById(Integer id);
int addOrder(Order order);
}
实体类
@Data
public class Order {
private Integer id;
private String userId;
private String commodityCode;
private Integer count;
private Integer amount;
}
控制器
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("getOrder/{id}")
public Order getOrder(@PathVariable("id") Integer id) {
return orderService.getOrderById(id);
}
}
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public Order getOrderById(Integer id){
return orderMapper.getOrderById(id);
}
public int addOrder(Order order){
return orderMapper.addOrder(order);
}
}
添加 mapper 扫描配置
@SpringBootApplication
@MapperScan("com.person.mybatis.mapper")
public class HelloWordMainApplication {...}
mybatis 执行sql的原理分析
我们执行的是接口OrderMapper中的方法,在实际调用时是接口的代理实现类,根据statementId获取方法对应的sql语句,并根据xml中配置进行参数类型转换,并调用执行器执行sql。
从扫描注解查看
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {...}
// 导入了类MapperScannerRegistrar实现了接口ImportBeanDefinitionRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
...
//将定义注入到容器中
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
具体分析类这个类MapperScannerConfigurer,最终了解到,将带有注解@Mapper类的类型改为MapperFacatoryBean
获取bean时,调用getObject()方法
public T getObject() throws Exception {
return this.getSqlSession().getMapper(this.mapperInterface);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
//看到了代理类的实现
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
MapperProxy中的invoke()
// PlainMethodInvoker.invoke()方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
//根据sql的类型判断,执行的方法
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
//调用执行器Excutor中的执行方法,在封装结果
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
//代码太多了,省略
...
}