【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-

JPA、MyBatis、JDBC、MyBatis-Plus 的差异与操作说明

在 Java 后端开发中,数据访问层的技术选型直接影响开发效率与维护成本。下面从 JDBC、MyBatis、MyBatis-Plus、JPA 四种方案出发,对比它们的定位、差异,并给出各自的代码示例。

1. JDBC(Java Database Connectivity)

JDBC 是 Java 访问数据库的最底层 API,所有 ORM 框架底层最终都基于 JDBC 完成数据库交互。它的特点是灵活、可控,但需要开发者手动处理连接、预编译、结果集映射和异常关闭等大量样板代码。

java 复制代码
// 原生 JDBC 示例
String url = "jdbc:mysql://localhost:3306/test";
String user = "root";
String password = "123456";

try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement ps = conn.prepareStatement("SELECT * FROM user WHERE id = ?")) {
    ps.setLong(1, 1L);
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
}

特点:性能最高、最灵活,但代码冗余、易出错,适合对 SQL 有极致控制需求的场景。

2. MyBatis

MyBatis 是半自动 ORM 框架,SQL 由开发者编写,框架负责参数映射与结果集映射。它保留了 SQL 的灵活性,同时大幅减少了 JDBC 样板代码。

java 复制代码
// Mapper 接口
public interface UserMapper {
    User selectById(@Param("id") Long id);
}
xml 复制代码
<!-- UserMapper.xml -->
<select id="selectById" resultType="com.example.User">
    SELECT * FROM user WHERE id = #{id}
</select>
java 复制代码
// 调用
User user = userMapper.selectById(1L);

特点:SQL 完全可控,适合复杂查询、多表关联、动态 SQL 场景;但需要手写 XML 或注解 SQL,开发量相对较大。

3. MyBatis-Plus

MyBatis-Plus 是 MyBatis 的增强工具,在保留 MyBatis 全部能力的基础上,提供了通用 CRUD、条件构造器、分页插件等开箱即用的能力,大幅减少重复 SQL 编写。

java 复制代码
// 实体类
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
}
java 复制代码
// Mapper 继承 BaseMapper,直接获得通用 CRUD
public interface UserMapper extends BaseMapper<User> {
}
java 复制代码
// 使用条件构造器查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getName, "张三").gt(User::getAge, 18);
List<User> users = userMapper.selectList(wrapper);

// 分页查询
Page<User> page = userMapper.selectPage(new Page<>(1, 10), wrapper);

特点:单表 CRUD 几乎零 SQL,开发效率极高;复杂 SQL 仍可手写,兼顾效率与灵活性,是目前国内最流行的 MyBatis 增强方案。

4. JPA(Java Persistence API)

JPA 是 Java 官方规范,Hibernate 是其最主流的实现。JPA 是全自动 ORM,开发者通过实体注解和接口方法命名约定即可完成数据操作,无需编写 SQL。

java 复制代码
// 实体类
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private Integer age;
}
java 复制代码
// Repository 接口,继承 JpaRepository 获得通用 CRUD
public interface UserRepository extends JpaRepository<User, Long> {
    // 方法命名约定自动生成查询
    List<User> findByNameAndAgeGreaterThan(String name, Integer age);
}
java 复制代码
// 调用
List<User> users = userRepository.findByNameAndAgeGreaterThan("张三", 18);

特点:全自动映射,开发效率高,跨数据库移植性好;但复杂 SQL 和动态查询控制力较弱,性能调优相对困难,学习曲线较陡。

5. 四种方案对比总结

维度 JDBC MyBatis MyBatis-Plus JPA
自动化程度 无 半自动 半自动(增强) 全自动
SQL 控制力 完全控制 完全控制 完全控制 较弱
开发效率 低 中 高 高
学习成本 低 中 中 较高
复杂查询 手写 SQL 手写 SQL 手写 SQL + 条件构造器 较难
典型场景 底层框架/极致性能 复杂业务 SQL 快速 CRUD + 复杂 SQL 标准规范/快速开发

选型建议:追求极致 SQL 控制选 JDBC;复杂查询多选 MyBatis;国内快速开发首选 MyBatis-Plus;追求规范统一、跨库移植选 JPA。

6. 常见查询操作示例

下面补充几种最常见的用户查询场景,分别用 JDBC、MyBatis、MyBatis-Plus、JPA 四种方式实现,方便对比它们的写法差异。

6.1 根据 id 查询用户

JDBC

java 复制代码
// 根据 id 查询用户
String sql = "SELECT * FROM user WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setLong(1, 1L);
    try (ResultSet rs = ps.executeQuery()) {
        if (rs.next()) {
            User u = new User();
            u.setId(rs.getLong("id"));
            u.setName(rs.getString("name"));
            u.setAge(rs.getInt("age"));
            System.out.println(u);
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
}

MyBatis

java 复制代码
// Mapper 接口
public interface UserMapper {
    User selectById(@Param("id") Long id);
}
xml 复制代码
<!-- UserMapper.xml -->
<select id="selectById" resultType="com.example.User">
    SELECT * FROM user WHERE id = #{id}
</select>
java 复制代码
// 调用
User user = userMapper.selectById(1L);

MyBatis-Plus

java 复制代码
// 直接使用 BaseMapper 内置方法
User user = userMapper.selectById(1L);

JPA

java 复制代码
// Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
}

// 调用
User user = userRepository.findById(1L).orElse(null);

6.2 根据用户名模糊查询用户

JDBC

java 复制代码
// 根据用户名模糊查询
String sql = "SELECT * FROM user WHERE name LIKE ?";
try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setString(1, "%张%");
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
}

MyBatis

java 复制代码
// Mapper 接口
public interface UserMapper {
    List<User> selectByNameLike(@Param("name") String name);
}
xml 复制代码
<!-- UserMapper.xml -->
<select id="selectByNameLike" resultType="com.example.User">
    SELECT * FROM user WHERE name LIKE CONCAT('%', #{name}, '%')
</select>
java 复制代码
// 调用
List<User> users = userMapper.selectByNameLike("张");

MyBatis-Plus

java 复制代码
// 使用条件构造器模糊查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张");
List<User> users = userMapper.selectList(wrapper);

JPA

java 复制代码
// Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByNameContaining(String name);
}

// 调用
List<User> users = userRepository.findByNameContaining("张");

6.3 根据 id、用户名、密码三个条件一起查询(用户名和密码支持模糊查询)

JDBC

java 复制代码
// 多条件组合查询,用户名和密码模糊匹配
String sql = "SELECT * FROM user WHERE id = ? AND name LIKE ? AND password LIKE ?";
try (Connection conn = DriverManager.getConnection(url, user, password);
     PreparedStatement ps = conn.prepareStatement(sql)) {
    ps.setLong(1, 1L);
    ps.setString(2, "%张%");
    ps.setString(3, "%123%");
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
    }
} catch (SQLException e) {
    e.printStackTrace();
}

MyBatis

java 复制代码
// Mapper 接口
public interface UserMapper {
    List<User> selectByCondition(@Param("id") Long id,
                                 @Param("name") String name,
                                 @Param("password") String password);
}
xml 复制代码
<!-- UserMapper.xml -->
<select id="selectByCondition" resultType="com.example.User">
    SELECT * FROM user
    WHERE id = #{id}
      AND name LIKE CONCAT('%', #{name}, '%')
      AND password LIKE CONCAT('%', #{password}, '%')
</select>
java 复制代码
// 调用
List<User> users = userMapper.selectByCondition(1L, "张", "123");

MyBatis-Plus

java 复制代码
// 使用条件构造器组合查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getId, 1L)
       .like(User::getName, "张")
       .like(User::getPassword, "123");
List<User> users = userMapper.selectList(wrapper);

JPA

java 复制代码
// Repository 接口
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByIdAndNameContainingAndPasswordContaining(
            Long id, String name, String password);
}

// 调用
List<User> users = userRepository.findByIdAndNameContainingAndPasswordContaining(1L, "张", "123");

小结:从上面三个查询场景可以看出,JDBC 需要手写全部 SQL 和结果集映射;MyBatis 需要手写 SQL 但映射交给框架;MyBatis-Plus 通过条件构造器几乎零 SQL;JPA 则完全靠方法命名约定自动生成查询。实际项目中可根据对 SQL 控制力的需求灵活选型。

7. 用户表设计与登录注册实现

下面实现一个最简单的用户表,包含 Id、Username、Password、Create_time、Update_time 五个字段,并给出登录与注册的完整代码。

7.1 表结构设计

字段 类型 说明
Id 字符串(主键,uid 生成) 主键,使用 UUID 生成
Username 字符串(Unique 唯一索引) 用户名,唯一
Password 字符串 密码
Create_time Date 类型 创建时自动填写
Update_time Date 类型 创建时自动填写,更新时更新
sql 复制代码
-- 建表语句
CREATE TABLE user (
    Id          VARCHAR(32) PRIMARY KEY COMMENT '主键,uid 生成',
    Username    VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名,唯一索引',
    Password    VARCHAR(100) NOT NULL COMMENT '密码',
    Create_time DATETIME NOT NULL COMMENT '创建时间,自动填写',
    Update_time DATETIME NOT NULL COMMENT '更新时间,创建时填写,更新时更新'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

7.2 实体类(MyBatis-Plus 示例)

java 复制代码
// 实体类
@Data
@TableName("user")
public class User {
    @TableId(type = IdType.ASSIGN_UUID)   // uid 生成主键
    private String id;

    private String username;

    private String password;

    @TableField(fill = FieldFill.INSERT)          // 创建时自动填写
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)   // 创建时填写,更新时更新
    private Date updateTime;
}
java 复制代码
// 自动填充处理器
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
        this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
    }
}
java 复制代码
// Mapper 接口
public interface UserMapper extends BaseMapper<User> {
}

7.3 注册代码

java 复制代码
// 注册:用户名唯一校验 + 密码加密 + 插入用户
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    public boolean register(String username, String password) {
        // 1. 校验用户名是否已存在
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        if (userMapper.selectCount(wrapper) > 0) {
            return false; // 用户名已存在
        }

        // 2. 创建用户,密码加密存储
        User user = new User();
        user.setUsername(username);
        user.setPassword(passwordEncoder.encode(password));

        // 3. 插入数据库(Id 由 ASSIGN_UUID 自动生成,时间字段自动填充)
        return userMapper.insert(user) > 0;
    }
}

7.4 登录代码

java 复制代码
// 登录:根据用户名查询,校验密码
public User login(String username, String password) {
    // 1. 根据用户名查询用户
    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getUsername, username);
    User user = userMapper.selectOne(wrapper);

    // 2. 用户不存在或密码不匹配则登录失败
    if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
        return null;
    }

    // 3. 登录成功,返回用户信息(实际项目中可在此生成 Token)
    return user;
}

说明 :主键 Id 使用 IdType.ASSIGN_UUID 自动生成 uid;Username 在数据库层面加了 UNIQUE 唯一索引,注册时再配合代码校验保证唯一;Create_time 和 Update_time 通过 MetaObjectHandler 自动填充,创建时都写入当前时间,更新时只刷新 Update_time。

8. 修改密码与注销账号

下面补充两个最常见的账号操作:修改密码和注销账号,均基于 MyBatis-Plus 实现。

8.1 修改密码

java 复制代码
// 修改密码:校验原密码 + 更新为新密码
public boolean changePassword(String userId, String oldPassword, String newPassword) {
    // 1. 根据 id 查询用户
    User user = userMapper.selectById(userId);
    if (user == null) {
        return false; // 用户不存在
    }

    // 2. 校验原密码是否正确
    if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
        return false; // 原密码错误
    }

    // 3. 更新为新密码(加密存储,Update_time 自动刷新)
    User update = new User();
    update.setId(userId);
    update.setPassword(passwordEncoder.encode(newPassword));
    return userMapper.updateById(update) > 0;
}

8.2 注销账号

java 复制代码
// 注销账号:根据 id 删除用户
public boolean deleteUser(String userId) {
    // 直接调用 BaseMapper 内置删除方法
    return userMapper.deleteById(userId) > 0;
}

说明 :修改密码时先校验原密码,避免被他人恶意篡改;新密码同样使用 PasswordEncoder 加密后存储,Update_time 由 MetaObjectHandler 自动刷新。注销账号直接按主键 Id 删除即可,实际项目中可改为逻辑删除(@TableLogic)以保留历史数据。

相关推荐
y1su1 小时前
Leetcode 二分模板
java·数据结构·算法·leetcode·排序算法
leisoo80971 小时前
融资融券数据怎么查两融指标含义与杠杆观察方法 IG50免费开源股票数据API接口
开发语言·jvm·数据库·python·json
霸道流氓气质1 小时前
多Agent通信机制与协议设计完全指南:从FIPA-ACL到A2A/MCP的Java生产级实战
java·开发语言
Aric_Jones2 小时前
pgsql内置publication/subscription功能,实现数据同步
数据库
007张三丰2 小时前
C/C++ 内存管理详解:从内存分布到 new/delete 底层原理
java·c语言·c++·内存管理
骇客野人2 小时前
Java BIO / NIO / AIO 完整详解 + 编程技巧
java·开发语言·nio
有想法的py工程师2 小时前
PostgreSQL 字符集双重转义击穿 磁盘 I/O
数据库·postgresql