从JDBC到MyBatis再到MyBatis-Plus:Java持久层技术演进全解析

引言

在Java企业级应用开发中,数据持久化是核心需求之一。本文将系统性地介绍Java持久层技术的演进过程,从最基础的JDBC开始,到广泛应用的MyBatis,再到功能强大的MyBatis-Plus。通过详细的源码解析和对比分析,帮助开发者深入理解这三种技术的实现原理、优缺点及适用场景。

一、原生JDBC:数据库操作的基石

1. JDBC核心架构

JDBC(Java Database Connectivity)是Java访问数据库的标准API,由以下核心组件构成:

  • DriverManager:管理数据库驱动
  • Connection:数据库连接对象
  • Statement/PreparedStatement:SQL执行接口
  • ResultSet:结果集对象
classDiagram
class DriverManager
class Connection
class Statement
class PreparedStatement
class ResultSet

DriverManager --> Connection
Connection --> Statement
Connection --> PreparedStatement
Statement --> ResultSet
PreparedStatement --> ResultSet

2. 完整CRUD实现

2.1 数据库连接管理

public class JdbcUtil {
private static final String URL = "jdbc:mysql://localhost:3306/test?useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "password"; // 静态代码块加载驱动(JDBC4.0+可省略)
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError("加载数据库驱动失败");
}
} /**
* 获取数据库连接
* @return Connection对象
* @throws SQLException 如果获取连接失败
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
} /**
* 关闭连接资源
* @param conn 连接对象
* @param stmt Statement对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

2.2 查询操作实现

public class JdbcQueryExample {
/**
* 查询单个用户
* @param id 用户ID
* @return User对象
*/
public User getUserById(int id) {
String sql = "SELECT id, name, age, email FROM users WHERE id = ?";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null; try {
// 1. 获取连接
conn = JdbcUtil.getConnection();
// 2. 创建PreparedStatement(预编译防止SQL注入)
pstmt = conn.prepareStatement(sql);
// 3. 设置参数
pstmt.setInt(1, id);
// 4. 执行查询
rs = pstmt.executeQuery(); // 5. 处理结果集
if (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setEmail(rs.getString("email"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6. 关闭资源
JdbcUtil.close(conn, pstmt, rs);
}
return user;
} /**
* 查询所有用户(使用try-with-resources简化资源管理)
*/
public List<User> getAllUsers() {
String sql = "SELECT id, name, age, email FROM users";
List<User> users = new ArrayList<>(); // try-with-resources自动关闭资源
try (Connection conn = JdbcUtil.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
user.setAge(rs.getInt("age"));
user.setEmail(rs.getString("email"));
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
}

2.3 更新操作实现

public class JdbcUpdateExample {
/**
* 更新用户信息
* @param user 用户对象
* @return 影响的行数
*/
public int updateUser(User user) {
String sql = "UPDATE users SET name = ?, age = ?, email = ? WHERE id = ?";
int affectedRows = 0; try (Connection conn = JdbcUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) { // 设置参数
pstmt.setString(1, user.getName());
pstmt.setInt(2, user.getAge());
pstmt.setString(3, user.getEmail());
pstmt.setInt(4, user.getId()); // 执行更新
affectedRows = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return affectedRows;
} /**
* 批量插入用户
* @param users 用户列表
* @return 每条SQL影响的行数数组
*/
public int[] batchInsert(List<User> users) {
String sql = "INSERT INTO users(name, age, email) VALUES (?, ?, ?)"; try (Connection conn = JdbcUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) { // 关闭自动提交,开启事务
conn.setAutoCommit(false); // 添加批处理
for (User user : users) {
pstmt.setString(1, user.getName());
pstmt.setInt(2, user.getAge());
pstmt.setString(3, user.getEmail());
pstmt.addBatch();
} // 执行批处理
int[] results = pstmt.executeBatch();
// 提交事务
conn.commit();
return results;
} catch (SQLException e) {
e.printStackTrace();
return new int[0];
}
}
}

3. JDBC的优缺点分析

优点

  1. 标准API,所有数据库厂商都提供实现
  2. 直接操作底层,性能最高
  3. 灵活性最强,可以执行任意SQL

缺点

  1. 样板代码多,开发效率低
  2. 需要手动管理连接和事务
  3. SQL与Java代码耦合度高
  4. 需要手动处理异常和资源释放
  5. 结果集到对象的映射需要手动实现

二、MyBatis:SQL与Java的优雅桥梁

1. MyBatis核心架构

MyBatis通过以下核心组件简化了JDBC操作:

  • SqlSessionFactory:创建SqlSession的工厂
  • SqlSession:执行CRUD操作的主要接口
  • Executor:SQL执行器
  • MappedStatement:封装SQL语句和映射信息
  • TypeHandler:处理Java与JDBC类型转换
classDiagram
class SqlSessionFactory
class SqlSession
class Executor
class MappedStatement
class TypeHandler

SqlSessionFactory --> SqlSession
SqlSession --> Executor
Executor --> MappedStatement
MappedStatement --> TypeHandler

2. MyBatis配置与映射

2.1 配置文件示例

mybatis-config.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">
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings> <typeAliases>
<typeAlias type="com.example.User" alias="User"/>
</typeAliases> <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments> <mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>

2.2 Mapper XML示例

UserMapper.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.example.mapper.UserMapper">
<resultMap id="userResultMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="email" column="email"/>
</resultMap> <select id="selectUserById" resultMap="userResultMap">
SELECT id, name, age, email FROM users WHERE id = #{id}
</select> <insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users(name, age, email) VALUES(#{name}, #{age}, #{email})
</insert> <update id="updateUser" parameterType="User">
UPDATE users SET name=#{name}, age=#{age}, email=#{email} WHERE id=#{id}
</update> <delete id="deleteUser" parameterType="int">
DELETE FROM users WHERE id=#{id}
</delete>
</mapper>

3. MyBatis核心源码解析

3.1 SqlSession创建过程

public class MyBatisExample {
public static void main(String[] args) {
// 1. 加载配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource); // 2. 构建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 3. 获取SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 4. 获取Mapper接口代理对象
UserMapper userMapper = session.getMapper(UserMapper.class); // 5. 执行CRUD操作
User user = userMapper.selectUserById(1);
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.2 Mapper代理实现

MyBatis通过动态代理将Mapper接口方法调用转换为SQL执行:

public class MapperProxy<T> implements InvocationHandler, Serializable {
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 处理Object方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} // 获取缓存的MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行SQL
return mapperMethod.execute(sqlSession, args);
} private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
}

3.3 SQL执行流程

public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor; @Override
public <E> List<E> selectList(String statement, Object parameter) {
try {
// 1. 获取MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 2. 委托给Executor执行
return executor.query(ms, wrapCollection(parameter), RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
}
}
} public class CachingExecutor implements Executor {
private final Executor delegate; @Override
public <E> List<E> query(MappedStatement ms, Object parameterObject,
RowBounds rowBounds, ResultHandler resultHandler,
CacheKey key, BoundSql boundSql) throws SQLException {
// 检查二级缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list);
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
}

4. MyBatis的优缺点分析

优点

  1. SQL与Java代码分离,易于维护
  2. 自动参数映射和结果集映射
  3. 支持动态SQL
  4. 提供一级和二级缓存
  5. 插件机制支持扩展

缺点

  1. 需要编写SQL和映射配置
  2. 复杂查询仍需手动编写SQL
  3. 分页功能需要插件支持
  4. 代码生成器功能较弱

三、MyBatis-Plus:MyBatis的增强工具包

1. MyBatis-Plus核心特性

  1. 通用Mapper:内置常用CRUD方法
  2. 条件构造器:链式API构建查询条件
  3. 代码生成器:自动生成Entity/Mapper/Service代码
  4. 分页插件:内置物理分页支持
  5. 性能分析插件:输出SQL执行时间
  6. 乐观锁插件:支持@Version注解

2. MyBatis-Plus配置与使用

2.1 Spring Boot集成配置

@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig { @Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
} @Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new MetaHandler());
return globalConfig;
}
}

2.2 实体类定义

@Data
@TableName("users")
public class User {
@TableId(type = IdType.AUTO)
private Long id; private String name;
private Integer age;
private String email; @Version
private Integer version; @TableField(fill = FieldFill.INSERT)
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}

3. MyBatis-Plus核心源码解析

3.1 通用Mapper实现

public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);
int deleteById(Serializable id);
int updateById(@Param(Constants.ENTITY) T entity);
T selectById(Serializable id);
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 其他方法...
} public class MybatisMapperProxy<T> implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 处理默认方法
if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
} // 处理Wrapper条件
if (args != null && args.length > 0 && args[0] instanceof Wrapper) {
processWrapper((Wrapper<?>) args[0]);
} // 转换为MyBatis的MapperMethod执行
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
} private void processWrapper(Wrapper<?> wrapper) {
if (wrapper instanceof AbstractWrapper) {
((AbstractWrapper<?, ?, ?>) wrapper).checkEntityClass();
}
}
}

3.2 条件构造器实现

public abstract class AbstractWrapper<T, R, Children extends AbstractWrapper<T, R, Children>>
implements Wrapper<T>, Compare<Children, R> { protected final List<SqlSegment> sqlSegments = new ArrayList<>();
protected Entity<T> entity; public Children eq(R column, Object val) {
return addCondition(column, SqlKeyword.EQ, val);
} protected Children addCondition(R column, SqlKeyword keyword, Object val) {
String columnName = columnToString(column);
sqlSegments.add(new SimpleSqlSegment(columnName + keyword.getSqlSegment() + "?"));
paramNameValuePairs.put(columnName, val);
return typedThis;
} public String getSqlSegment() {
mergeExpression();
return sqlSegments.stream()
.map(SqlSegment::getSqlSegment)
.collect(Collectors.joining(" "));
}
}

3.3 分页插件实现

@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
})
public class PaginationInnerInterceptor implements InnerInterceptor { @Override
public void beforeQuery(Executor executor, MappedStatement ms,
Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
if (parameter instanceof Map && ((Map<?, ?>) parameter).containsKey(IPage.class.getName())) {
// 获取分页参数
IPage<?> page = (IPage<?>) ((Map<?, ?>) parameter).get(IPage.class.getName()); // 执行COUNT查询
String countSql = generateCountSql(boundSql.getSql());
Long total = executeCount(executor, ms, parameter, boundSql, countSql);
page.setTotal(total); // 生成分页SQL
String pageSql = dialect.buildPaginationSql(boundSql.getSql(), page, buildCountKey(ms.getId())); // 修改BoundSql
resetSql(boundSql, pageSql);
}
}
}

4. MyBatis-Plus的优缺点分析

优点

  1. 极大减少样板代码
  2. 强大的条件构造器
  3. 内置分页和乐观锁支持
  4. 完善的代码生成器
  5. 保留MyBatis所有特性

缺点

  1. 复杂SQL仍需手写XML
  2. 学习成本比原生MyBatis高
  3. 自动生成的SQL可能不够优化

四、技术对比与选型建议

特性 JDBC MyBatis MyBatis-Plus
开发效率
性能 中高 中高
灵活性 最高 中高
学习曲线 中高
社区支持 标准 强大 强大
适用场景 需要极致性能/特殊需求 需要灵活SQL控制 快速开发CRUD功能

选型建议

  1. 需要极致性能或特殊数据库特性 → JDBC
  2. 需要灵活控制SQL且项目复杂 → MyBatis
  3. 常规业务系统快速开发 → MyBatis-Plus

五、扩展知识点

1. 连接池技术

  • HikariCP:目前性能最好的连接池
  • Druid:阿里开源,带监控功能
  • Tomcat JDBC Pool:Tomcat内置连接池

2. 分布式事务

  • XA协议:传统两阶段提交
  • TCC模式:Try-Confirm-Cancel
  • Saga模式:长事务解决方案
  • Seata:阿里开源的分布式事务框架

3. ORM框架对比

框架 优点 缺点
Hibernate 全自动ORM,开发效率高 性能较差,复杂查询难优化
JPA 标准规范,可移植性好 灵活性不足
MyBatis SQL可控,性能好 需要写SQL
MyBatis-Plus 开发效率高,功能丰富 复杂SQL支持不够

4. 性能优化建议

  1. 合理使用缓存(一级/二级/分布式)
  2. 批量操作代替循环单条操作
  3. 避免N+1查询问题
  4. 合理设计索引
  5. 使用连接池并正确配置参数

结语

通过本文的系统性讲解,我们从最基础的JDBC开始,逐步深入到MyBatis和MyBatis-Plus的核心实现原理。理解这些技术的演进过程和底层机制,有助于我们在实际项目中做出合理的技术选型,并根据业务需求进行适当的定制和优化。无论选择哪种技术,都要在开发效率、维护成本和系统性能之间找到平衡点。

【Java持久层技术演进全解析】从JDBC到MyBatis再到MyBatis-Plus的更多相关文章

  1. 面试之Java持久层(十)

    91,什么是ORM?         对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术: 简单的说,O ...

  2. 【Spring】对持久层技术的整合

    一.持久层技术 二.JdbcTemplate 开发步骤: 1. 导入相关的jar包 2. 配置连接池(数据源) 将参数设置到属性文件中: 3. 创建表 4. 编写实体类 5. Dao层实现 5.1 继 ...

  3. Java持久层框架Mybatis入门

    MyBatis是什么 MyBatis是Java的持久层框架,GitHub的star数高达15.8k,是Java技术栈中最热门的ORM框架之一.它支持自定义SQL.存储过程以及高级映射,可以通过XML或 ...

  4. Java 持久层框架之 MyBatis

    MyBatis 简介 MyBatis 是一个基于 Java 的持久层框架,它内部封装了 JDBC,使开发者只需关注 SQL 语句本身,而不用再花费精力去处理诸如注册驱动.创建 Connection.配 ...

  5. SpringBoot整合持久层技术--(三)Spring Data JPA

    简介: JPA(java Persistence API)和SpringData是两个范畴的概念.spring data jpa是spring公司下的spring data项目的一个模块. sprin ...

  6. java持久层框架mybatis如何防止sql注入

    看到一篇很好的文章:http://www.jfox.info/ava-persistence-framework-mybatis-how-to-prevent-sql-injection sql注入大 ...

  7. Java String 演进全解析

    前言 String 是我们使用最频繁的对象,使用不当会对内存.程序的性能造成影响,本篇文章全面介绍一下 Java 的 String 是如何演进的,以及使用 String 的注意事项. 下面的输出结果是 ...

  8. 多云架构下,JAVA微服务技术选型实例解析

    [摘要] 本文介绍了基于开源自建和适配云厂商开发框架两种构建多云架构的思路,以及这些思路的优缺点. 微服务生态 微服务生态本质上是一种微服务架构模式的实现,包括微服务开发SDK,以及微服务基础设施. ...

  9. 热度3年猛增20倍,Serverless&云开发的技术架构全解析

    『 作为一个不断发展的新兴技术, Serverless 热度的制高点已然到来.』 或许,Google Trends 所显示的 3 年猛增 20 倍的" Serverless " 搜 ...

  10. 转: 环信联合创始人:App主流反垃圾服务难点和技术实现全解析

    转:http://science.china.com.cn/2016-03/24/content_8659834.htm 发布时间: 2016-03-24 13:15:02  |  来源: 全球财经网 ...

随机推荐

  1. K230学习记录

    K230学习记录 参考自: # 立创·庐山派-K230-CanMV开发板资料与相关扩展板软硬件资料官网全部开源 # 开发板官网:www.lckfb.com # 技术支持常驻论坛,任何技术问题欢迎随时交 ...

  2. springboot+easypoi模板导出Excel 动态表头+多表格(一个sheet)

    1.需求:将此页面的几个表格导出 其中表头中的仓库 集散地是是动态生成的. 首先制作Excel模板: 代码: @Resource private RedisService redisService; ...

  3. Task VS ValueTask

    在 C# 中,异步编程是构建响应式应用程序的基础.Task 是表示异步操作的首选类型.但是,在某些高性能场景中,与 Task 相关的开销可能会达到一个瓶颈.ValueTask 是 .NET Core ...

  4. 震惊!Manus邀请码炒到5万元一个!附免费获取Manus邀请码两种方式

    在AI技术蓬勃发展的当下,一款名为Manus的产品掀起了行业巨浪.本文将深入剖析这款全球首款通用AI智能体,从它的惊艳亮相.独特功能,到其性能突破.模式限制,以及在AI领域的深远意义,全方位带大家了解 ...

  5. MySQL超大表删除数据过程

    背景 笔者在公司负责公司的OpenAPI应用,估产生了调用审计的需求.对于存储这些AccessLog,虽然业界有很合适的架构和理论,奈何我司已成本优先,且作为toB的项目,调用量并不算特别大,每天也就 ...

  6. 基础命令:dd、tar、ln、find、逻辑符号、alisa别名、md5sun校验、lrzsz文件上传下载、wget

    目录 3.0 dd读取.转换并输出数据 3.1 压缩 (tar.zip).解压缩(tar xf.unzip) 3.2 ln软硬链接 3.2.1 软链接: 3.2.2 硬链接: 3.3 find文件查找 ...

  7. 公安部网防G01-网站安全卫士软件/linux防御

    公安部网防G01-该软件免费使用,安装在网站服务器上,利用操作系统内核加固和主机web流量过滤技术,有效检测并抵御网页篡改.SQL注入.漏洞攻击.暴力破解.木马控制.XSS跨站.CC拒绝服务.系统提权 ...

  8. AI与.NET技术实操系列(九):总结篇 ── 探讨.NET 开发 AI 生态:工具、库与未来趋势

    1. 引言 本文作为本系列的最后一篇,旨在全面探讨 .NET 生态中与 AI 相关的工具.库.框架和资源,帮助开发者了解如何在 .NET 环境中开发 AI 应用.我们将分析 Microsoft 的 A ...

  9. 数字先锋 | 打造专属AI大脑,天翼云助力重庆理工大学开启智慧校园新篇!

    从在线课程的蓬勃兴起,到远程教育的逐渐普及,再到智能教学工具的广泛应用--人工智能.大数据.云计算等前沿技术迅速发展的当下,科技正以前所未有的深度和广度重塑教育领域,促使教育各个层面发生深刻变革.师生 ...

  10. 二分查找--java进阶day06

    1.二分查找 https://kdocs.cn/l/ciMkwngvaWfz?linkname=150996908 二分查找:每一次查找都从中间的元素查起,根据比较的大小来折半,以此类推,直到最后找到 ...