1.MyBatis的工作原理
1.1、传统的JDBC编程
JAVA程序通过JDBC链接数据库,这样我们就可以通过SQL对数据库进行编程。JAVA链接数据库大致分为五步,如下所示:
- 1、使用JDBC编程需要链接数据库,注册驱动和数据库信息。
- 2、操作Connection,打开Statement对象。
- 3、通过Statement执行SQL语句,返回结果放到ResultSet对象。
- 4、使用ResultSet读取数据。
- 5、关闭数据库相关的资源。
JDBC 代码示例:
import java.sql.*;
public class JdbcDemo {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
String username = "db1";
String password = "db1";
String url = "jdbc:mysql://192.168.124.10:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";
// 使用JDBC编程需要链接数据库,注册驱动和数据库信息。
Connection conn = DriverManager.getConnection(url,username,password);
// 编写待执行sql
String sql = "select * from user1";
// 通过Statement执行SQL语句,返回结果放到ResultSet对象。
PreparedStatement preparedStatement = conn.prepareStatement(sql);
ResultSet rs = preparedStatement.executeQuery();
//使用ResultSet读取数据。
// 获取 ResultSetMetadata 对象,它包含了 ResultSet 的结构信息
ResultSetMetaData metaData = rs.getMetaData();
// 打印列名
for (int i = 1; i <= metaData.getColumnCount(); i++) {
System.out.print(metaData.getColumnName(i) + "\t");
}
System.out.println(); // 换行
// 遍历 ResultSet 并打印数据
while (rs.next()) {
for (int i = 1; i <= metaData.getColumnCount(); i++) {
System.out.print(rs.getString(i) + "\t");
}
System.out.println(); // 每行数据后换行
}
// 关闭数据库相关的资源。
preparedStatement.close();
conn.close();
}
}
传统的JDBC方式存在一些弊端:
(1)工作量比较大。我们需要先建立链接,然后处理JDBC底层业务,处理数据类型。我们还需要处理Connection对象,Statement对象和Result对象去拿数据,并关闭它们。
(2)我们对JDBC编程处理的异常进行捕获处理并正确的关闭资源。
1.2、MyBatis工作原理:对JDBC进行了封装
MyBatis的四大核心组件:
- 1、SQLSessionFactoryBuilder(构造器):它会根据配置信息或者代码生成SqlSessionFactory。
- 2、SqlSessionFactory(工厂接口):依靠工厂生成SqlSession。
- 3、SqlSession(会话):是一个既可以发送SQL去执行并且返回结果,也可以获取Mapper接口。
- 4、SQL Mapper:是由一个JAVA接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。SQL是由Mapper发送出去,并且返回结果。
MyBatis工作原理示意图:
从上面的流程图可以看出MyBatis和JDBC的执行时相似的。MyBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用步骤一脉相承,MyBatis的核心对象(SqlSession,Executor)与JDBC的核心对象(Connection,Statement)相互对应。
1.3 @Mapper执行sql原理
Mapper 接口与 @Mapper 注解:
- Mapper 接口是用户定义的,其中包含了与数据库表交互的方法。
@Mapper
注解是 MyBatis-Spring 集成库中的一个注解,用于标识一个接口作为 MyBatis 的 Mapper 接口。- 当接口被
@Mapper
注解标记后,MyBatis 会自动为这个接口创建一个代理实现类。
MyBatis 映射器(Mapper)的代理实现:
- MyBatis 使用 Java 动态代理技术为 Mapper 接口创建代理实现类。
- 代理实现类会拦截对 Mapper 接口方法的调用,并根据方法的定义(包括方法名、参数等)来查找并执行相应的 SQL 语句。
SQL 语句的查找与执行:
- MyBatis 会在配置的 SQL 映射文件(通常是 XML 文件)中查找与 Mapper 接口方法对应的 SQL 语句。
- SQL 映射文件定义了 Mapper 接口中每个方法对应的 SQL 语句,包括查询、插入、更新、删除等操作。
- MyBatis 会根据 Mapper 接口方法的定义和 SQL 映射文件中的配置,生成具体的 SQL 语句并执行。
@Param 注解与参数绑定:
- 如果 Mapper 接口方法中有参数,可以使用
@Param
注解来指定参数名。 - MyBatis 会根据
@Param
注解指定的参数名,在 SQL 语句中进行参数绑定。 - 参数绑定是将 Java 对象的属性值或方法参数值设置到 SQL 语句的占位符中的过程。
- 如果 Mapper 接口方法中有参数,可以使用
结果映射与返回类型:
- MyBatis 会根据 Mapper 接口方法的返回类型,将 SQL 语句执行的结果映射为相应的 Java 对象或集合。
- 结果映射可以在 XML 映射文件中进行配置,包括结果集的列名与 Java 对象属性的对应关系等。
Spring 集成与 SqlSessionTemplate:
- 在 Spring 与 MyBatis 的集成中,
SqlSessionTemplate
是一个关键的组件。 SqlSessionTemplate
封装了SqlSession
的使用,并提供了线程安全的 SQL 执行环境。- 当通过 Spring 容器注入 Mapper 接口时,实际上注入的是
SqlSessionTemplate
创建的 Mapper 代理实现类的实例。
- 在 Spring 与 MyBatis 的集成中,
总结:
@Mapper
注解使得 MyBatis 能够自动为 Mapper 接口创建代理实现类。- MyBatis 通过动态代理和 SQL 映射文件来执行 Mapper 接口中定义的 SQL 语句。
- Spring 集成通过
SqlSessionTemplate
提供了线程安全的 SQL 执行环境,并简化了 Mapper 接口的注入和使用。
2.MyBatis的优点
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。MyBatis是对JDBC的封装。相对于JDBC,MyBatis有以下优点:
SQL映射:
- MyBatis 支持定制化 SQL、存储过程以及高级映射。它允许你直接在 XML 映射文件中编写 SQL 语句,或者使用注解的方式将 SQL 语句直接写在 Mapper 接口的方法上。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的麻烦,极大地简化了数据库操作。
对象关系映射(ORM):
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
- 提供了映射标签,支持对象与数据库的 ORM 字段关系映射,降低了耦合度,提高了代码的复用性和可维护性。
动态SQL:
- MyBatis 提供了 XML 标签,支持编写动态 SQL 语句。你可以根据传入参数的不同,动态地生成不同的 SQL 语句,实现更复杂的数据库操作。
事务管理:
- MyBatis 通过与 Spring 等框架的集成,提供了完整的事务管理功能。它支持编程式事务和声明式事务,可以确保数据的一致性。
插件机制:
- MyBatis 提供了插件机制,允许你通过编写插件来扩展 MyBatis 的功能。例如,你可以编写一个插件来拦截 SQL 语句的执行,进行日志记录、性能监控等操作。
缓存机制:
- MyBatis 提供了一级缓存(SqlSession 级别的缓存)和二级缓存(Mapper 级别的缓存)来提高查询性能。一级缓存默认是开启的,而二级缓存需要手动开启并进行配置。
3.快速集成步骤
3.1 引入mybatis-starter依赖
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
其他必备依赖
<!-- springboot基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<!-- mysql数据库连接驱动-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 引入Spring封装的jdbc,内部默认依赖了 HikariDataSource 数据源-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
测试辅助依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.3.0</version>
</dependency>
3.2 配置数据源
在 application.properties
或 application.yml
文件中配置你的数据源(如MySQL)。
spring:
#配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.124.10:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: db1
password: db1
type: com.zaxxer.hikari.HikariDataSource
hikari:
# 连接池最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
minimum-idle: 10
# 连接池最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值。
maximum-pool-size: 20
# 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
max-lifetime: 600000
# 空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
idle-timeout: 600000
# 连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒,如果在这个时间内无法建立连接,将会抛出异常。
connection-timeout: 30000
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.hikaridatasource.domain.entity
3.3 创建数据源和Mybatis必备bean的Config文件
@Configuration
public class Db1DataSourceConfig {
// jdbc连接信息
@Value(value = "${spring.datasource.url:}")
private String url;
@Value(value = "${spring.datasource.username:}")
private String username;
@Value(value = "${spring.datasource.password:}")
private String password;
@Value(value = "${spring.datasource.driver-class-name:}")
private String driveClassName;
// HikariDataSource配置参数
// 连接池最小空闲连接,默认值10
@Value(value = "${spring.datasource.hikari.minimum-idle:10}")
private int minimumIdle;
// 连接池最大连接数,默认值10
@Value(value = "${spring.datasource.hikari.maximum-pool-size:10}")
private int maximumPoolSize;
// 连接最大存活时间,默认值30分钟.设置应该比mysql设置的超时时间短,配置单位毫秒
@Value(value = "${spring.datasource.hikari.max-lifetime:600000}")
private long maxLifetime;
// 空闲连接超时时间,默认值600000(10分钟)配置单位毫秒
@Value(value = "${spring.datasource.hikari.idle-timeout:600000}")
private long idleTimeout;
// 连接超时时间,配置单位毫秒
@Value(value = "${spring.datasource.hikari.connection-timeout:60000}")
private long connectionTimeout;
// mybatis配置
// mapperXml文件地址
@Value(value = "${spring.datasource.hikari.mapper-locations:}")
private String mapperLocations;
/**
* 配置db1 数据源
*/
@Bean(name = "db1DataSource")
public DataSource db1DataSource(){
HikariDataSource dataSource = new HikariDataSource();
// 设置jdbc连接信息
dataSource.setJdbcUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driveClassName);
// 设置HikariDataSource配置参数
dataSource.setMinimumIdle(minimumIdle);
dataSource.setMaximumPoolSize(maximumPoolSize);
dataSource.setMaxLifetime(maxLifetime);
dataSource.setIdleTimeout(idleTimeout);
dataSource.setConnectionTimeout(connectionTimeout);
return dataSource;
}
/**
* 配置db1 SqlSessionFactory mybatis必备(实现数据库连接会话管理)
*/
@Bean(name = "db1SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
// 设置mapperXml文件地址
//factory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(mapperLocations));
return factory.getObject();
}
/**
* 配置db1 SqlSessionTemplate mybatis必备(实现数据库连接sql执行和结果映射)
*/
@Bean(name = "db1SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 配置db1 DataSourceTransactionManager 事务管理器(Spring的JDBC事务增强)
*/
@Bean(name = "db1DataSourceTransactionManager")
public DataSourceTransactionManager db1DataSourceTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.4 创建实体类(Entity)
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class User1Entity {
private Integer id;
private String username;
private String password;
}
3.5 创建 Mapper 接口
Mapper 接口用于定义 SQL 语句和数据库交互。
import com.example.hikaridatasource.domain.entity.User1Entity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface User1Mapper {
@Select("SELECT * FROM user1")
List<User1Entity> selectAllUsers();
}
3.6 使用 Mapper测试
自动注入Mapper代理对象的方式
@SpringBootTest
@Slf4j
class HikariDataSourceApplicationTests {
@Resource
User1Mapper user1Mapper1;
@Test
void contextLoads() throws SQLException {
List<User1Entity> user1List1Entity = user1Mapper1.selectAllUsers();
log.info("user1List1={}", user1List1Entity);
}
}
2024-06-22T17:19:15.053+08:00 INFO 15316 --- [ main] c.e.h.HikariDataSourceApplicationTests : user1List1=[User1Entity(id=1, username=username1, password=password1), User1Entity(id=2, username=username1, password=password1), User1Entity(id=3, username=username1, password=password1), User1Entity(id=4, username=username1, password=password1), User1Entity(id=5, username=username1, password=password1)]
通过SqlSessionTemplate手动获取Mapper代理对象的方式
@SpringBootTest
@Slf4j
class HikariDataSourceApplicationTests {
@Resource
SqlSessionTemplate sqlSessionTemplate;
@Test
void contextLoads() throws SQLException {
User1Mapper user1Mapper2 = sqlSessionTemplate.getMapper(User1Mapper.class);
List<User1Entity> user1EntityList2 = user1Mapper2.selectAllUsers();
log.info("user1List2={}", user1EntityList2);
}
}
2024-06-22T17:21:11.392+08:00 INFO 8368 --- [ main] c.e.h.HikariDataSourceApplicationTests : user1List2=[User1Entity(id=1, username=username1, password=password1), User1Entity(id=2, username=username1, password=password1), User1Entity(id=3, username=username1, password=password1), User1Entity(id=4, username=username1, password=password1), User1Entity(id=5, username=username1, password=password1)]
评论 (0)