joken-前端工程师

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: :: :: 管理 ::

MyBatis 是一个轻量级的持久化框架(ORM 工具),在 Spring Boot 中也可以很好地用来操作 MySQL 等数据库。相比 Spring Data JPA,MyBatis 的特点是更灵活,你可以手动控制 SQL 语句,而不是完全依赖自动生成的查询。它通过映射文件(XML)或注解将 Java 方法与 SQL 关联起来,非常适合需要精细控制 SQL 的场景。

下面我详细讲解 MyBatis 在 Spring Boot 中的使用方式。


如何在 Spring Boot 中使用 MyBatis?

1. 添加依赖

pom.xml 中加入 MyBatis 的 Spring Boot Starter 和 MySQL 驱动:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

2. 配置数据源

application.propertiesapplication.yml 中配置 MySQL 连接:

spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=yourpassword
# 可选:MyBatis 配置
mybatis.mapper-locations=classpath:mapper/*.xml  # XML 映射文件位置
mybatis.type-aliases-package=com.example.entity  # 实体类包路径

3. 定义实体类

创建一个普通的 Java 类(POJO),用来映射数据库表。不需要像 JPA 那样加注解:

package com.example.entity;

public class User {
    private Long id;
    private String name;
    private String email;

    // 无参构造函数
    public User() {}

    // 有参构造函数、getter 和 setter
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

4. 定义 Mapper 接口

创建一个接口,定义数据库操作方法。可以用注解直接写 SQL,也可以用 XML 映射文件。

方法一:使用注解
package com.example.mapper;

import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Delete;
import java.util.List;

@Mapper
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);

    @Select("SELECT * FROM user")
    List<User> findAll();

    @Insert("INSERT INTO user(name, email) VALUES(#{name}, #{email})")
    void insert(User user);

    @Update("UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}")
    void update(User user);

    @Delete("DELETE FROM user WHERE id = #{id}")
    void delete(Long id);
}
  • @Mapper 表示这是一个 MyBatis 的映射接口。
  • #{} 是参数占位符,MyBatis 会自动把方法参数映射到 SQL。
方法二:使用 XML 映射文件

如果 SQL 比较复杂,推荐用 XML 文件:

  1. 创建接口:
    package com.example.mapper;
    
    import com.example.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    import java.util.List;
    
    @Mapper
    public interface UserMapper {
        User findById(Long id);
        List<User> findAll();
        void insert(User user);
        void update(User user);
        void delete(Long id);
    }
    
  2. src/main/resources/mapper 下创建 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">
        <select id="findById" resultType="com.example.entity.User">
            SELECT * FROM user WHERE id = #{id}
        </select>
    
        <select id="findAll" resultType="com.example.entity.User">
            SELECT * FROM user
        </select>
    
        <insert id="insert" parameterType="com.example.entity.User">
            INSERT INTO user(name, email) VALUES(#{name}, #{email})
        </insert>
    
        <update id="update" parameterType="com.example.entity.User">
            UPDATE user SET name = #{name}, email = #{email} WHERE id = #{id}
        </update>
    
        <delete id="delete">
            DELETE FROM user WHERE id = #{id}
        </delete>
    </mapper>
    
    • namespace 对应 Mapper 接口的全限定名。
    • id 对应接口中的方法名。

5. 使用 Mapper

在服务层注入并调用:

package com.example.service;

import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserService {
    private final UserMapper userMapper;

    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public User getUser(Long id) {
        return userMapper.findById(id);
    }

    public List<User> getAllUsers() {
        return userMapper.findAll();
    }

    public void addUser(User user) {
        userMapper.insert(user);
    }

    public void updateUser(User user) {
        userMapper.update(user);
    }

    public void deleteUser(Long id) {
        userMapper.delete(id);
    }
}

6. 配置扫描(可选)

如果不用 @Mapper,可以在启动类上加 @MapperScan 扫描 Mapper 接口:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

7. 测试

可以用 Controller 调用 Service,或者直接测试:

@RestController
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUser(id);
    }

    @PostMapping("/users")
    public void addUser(@RequestBody User user) {
        userService.addUser(user);
    }
}

MyBatis 的核心功能

  1. SQL 灵活性

    • 你可以完全掌控 SQL,适合复杂查询。
    • 支持动态 SQL(在 XML 中用 <if><foreach> 等标签)。
  2. 映射

    • 参数和结果自动映射到 Java 对象。
    • 支持一对一、一对多关系(需配置)。
  3. 轻量级

    • 不像 JPA 那样依赖 ORM 的复杂特性,性能更可控。

优势

  • SQL 控制:开发者可以编写精确的 SQL,优化性能。
  • 简单:没有 JPA 的学习成本,直接上手。
  • 灵活:支持动态 SQL 和复杂映射。

注意事项

  • 手动 SQL:不像 Spring Data JPA 那样自动生成,需要自己写。
  • 主键回填:如果需要插入后返回自增 ID,需配置(如 <insert>useGeneratedKeys)。
  • 事务:在 Service 层加 @Transactional 管理事务。

总结

MyBatis 在 Spring Boot 中通过 Mapper 接口和 SQL 映射(注解或 XML)实现数据库操作。它适合需要精细控制 SQL 的场景,比如复杂的报表查询。

示例

在使用 MyBatis 实现权限管理功能时,我们需要设计一个合理的数据库结构,并通过 MyBatis 的 Mapper 接口和映射文件(或注解)来实现查询、修改等操作。权限管理通常涉及用户(User)、角色(Role)和权限(Permission)之间的关系,常见的设计是基于 RBAC(Role-Based Access Control,基于角色的访问控制) 模型。

下面我将一步步讲解如何用 MyBatis 在 Spring Boot 中实现权限管理的 CRUD 操作,包括数据库设计、实体类、Mapper 接口和具体功能的实现。


1. 数据库设计

假设我们基于 RBAC 模型,设计以下表结构:

  • users(用户表)

    id (bigint, 主键,自增)
    username (varchar, 用户名)
    password (varchar, 密码)
    
  • roles(角色表)

    id (bigint, 主键,自增)
    name (varchar, 角色名,如 "admin", "user")
    
  • permissions(权限表)

    id (bigint, 主键,自增)
    name (varchar, 权限名,如 "read", "write")
    
  • user_roles(用户-角色关联表,多对多)

    user_id (bigint, 外键,关联 users.id)
    role_id (bigint, 外键,关联 roles.id)
    
  • role_permissions(角色-权限关联表,多对多)

    role_id (bigint, 外键,关联 roles.id)
    permission_id (bigint, 外键,关联 permissions.id)
    

这个结构允许用户拥有多个角色,每个角色拥有多个权限,支持灵活的权限分配。


2. 添加依赖

确保 pom.xml 中有 MyBatis 和 MySQL 的依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

3. 配置数据源

application.properties 中:

spring.datasource.url=jdbc:mysql://localhost:3306/rbac_db
spring.datasource.username=root
spring.datasource.password=yourpassword
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.entity

4. 定义实体类

基于表结构,创建对应的 Java 类:

User.java

package com.example.entity;

import java.util.List;

public class User {
    private Long id;
    private String username;
    private String password;
    private List<Role> roles; // 用户的角色列表

    // 构造函数、getter 和 setter
    public User() {}
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    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 List<Role> getRoles() { return roles; }
    public void setRoles(List<Role> roles) { this.roles = roles; }
}

Role.java

package com.example.entity;

import java.util.List;

public class Role {
    private Long id;
    private String name;
    private List<Permission> permissions; // 角色的权限列表

    // 构造函数、getter 和 setter
    public Role() {}
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public List<Permission> getPermissions() { return permissions; }
    public void setPermissions(List<Permission> permissions) { this.permissions = permissions; }
}

Permission.java

package com.example.entity;

public class Permission {
    private Long id;
    private String name;

    // 构造函数、getter 和 setter
    public Permission() {}
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

5. 定义 Mapper 接口和 XML

UserMapper.java

package com.example.mapper;

import com.example.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User findById(Long id);          // 查询用户及其角色
    void insert(User user);          // 添加用户
    void update(User user);          // 更新用户信息
    void delete(Long id);            // 删除用户
    void addUserRole(Long userId, Long roleId); // 为用户分配角色
    void removeUserRole(Long userId, Long roleId); // 删除用户角色
}

UserMapper.xml

src/main/resources/mapper/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="UserWithRoles" type="com.example.entity.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <collection property="roles" ofType="com.example.entity.Role">
            <id property="id" column="role_id"/>
            <result property="name" column="role_name"/>
            <collection property="permissions" ofType="com.example.entity.Permission">
                <id property="id" column="permission_id"/>
                <result property="name" column="permission_name"/>
            </collection>
        </collection>
    </resultMap>

    <!-- 查询用户及其角色和权限 -->
    <select id="findById" resultMap="UserWithRoles">
        SELECT 
            u.id, u.username, u.password,
            r.id AS role_id, r.name AS role_name,
            p.id AS permission_id, p.name AS permission_name
        FROM users u
        LEFT JOIN user_roles ur ON u.id = ur.user_id
        LEFT JOIN roles r ON ur.role_id = r.id
        LEFT JOIN role_permissions rp ON r.id = rp.role_id
        LEFT JOIN permissions p ON rp.permission_id = p.id
        WHERE u.id = #{id}
    </select>

    <!-- 插入用户 -->
    <insert id="insert" parameterType="com.example.entity.User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO users(username, password) VALUES(#{username}, #{password})
    </insert>

    <!-- 更新用户 -->
    <update id="update" parameterType="com.example.entity.User">
        UPDATE users SET username = #{username}, password = #{password} WHERE id = #{id}
    </update>

    <!-- 删除用户 -->
    <delete id="delete">
        DELETE FROM users WHERE id = #{id}
    </delete>

    <!-- 添加用户角色 -->
    <insert id="addUserRole">
        INSERT INTO user_roles(user_id, role_id) VALUES(#{userId}, #{roleId})
    </insert>

    <!-- 删除用户角色 -->
    <delete id="removeUserRole">
        DELETE FROM user_roles WHERE user_id = #{userId} AND role_id = #{roleId}
    </delete>
</mapper>

RoleMapper.java

package com.example.mapper;

import com.example.entity.Role;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface RoleMapper {
    Role findById(Long id);          // 查询角色及其权限
    void insert(Role role);          // 添加角色
    void update(Role role);          // 更新角色
    void delete(Long id);            // 删除角色
    void addRolePermission(Long roleId, Long permissionId); // 为角色分配权限
    void removeRolePermission(Long roleId, Long permissionId); // 删除角色权限
}

RoleMapper.xml

src/main/resources/mapper/RoleMapper.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.RoleMapper">
    <!-- 结果映射:角色及其权限 -->
    <resultMap id="RoleWithPermissions" type="com.example.entity.Role">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <collection property="permissions" ofType="com.example.entity.Permission">
            <id property="id" column="permission_id"/>
            <result property="name" column="permission_name"/>
        </collection>
    </resultMap>

    <!-- 查询角色及其权限 -->
    <select id="findById" resultMap="RoleWithPermissions">
        SELECT 
            r.id, r.name,
            p.id AS permission_id, p.name AS permission_name
        FROM roles r
        LEFT JOIN role_permissions rp ON r.id = rp.role_id
        LEFT JOIN permissions p ON rp.permission_id = p.id
        WHERE r.id = #{id}
    </select>

    <!-- 插入角色 -->
    <insert id="insert" parameterType="com.example.entity.Role" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO roles(name) VALUES(#{name})
    </insert>

    <!-- 更新角色 -->
    <update id="update" parameterType="com.example.entity.Role">
        UPDATE roles SET name = #{name} WHERE id = #{id}
    </update>

    <!-- 删除角色 -->
    <delete id="delete">
        DELETE FROM roles WHERE id = #{id}
    </delete>

    <!-- 添加角色权限 -->
    <insert id="addRolePermission">
        INSERT INTO role_permissions(role_id, permission_id) VALUES(#{roleId}, #{permissionId})
    </insert>

    <!-- 删除角色权限 -->
    <delete id="removeRolePermission">
        DELETE FROM role_permissions WHERE role_id = #{roleId} AND permission_id = #{permissionId}
    </delete>
</mapper>

PermissionMapper.java

package com.example.mapper;

import com.example.entity.Permission;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PermissionMapper {
    Permission findById(Long id);
    void insert(Permission permission);
    void update(Permission permission);
    void delete(Long id);
}

PermissionMapper.xml

src/main/resources/mapper/PermissionMapper.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.PermissionMapper">
    <select id="findById" resultType="com.example.entity.Permission">
        SELECT id, name FROM permissions WHERE id = #{id}
    </select>

    <insert id="insert" parameterType="com.example.entity.Permission" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO permissions(name) VALUES(#{name})
    </insert>

    <update id="update" parameterType="com.example.entity.Permission">
        UPDATE permissions SET name = #{name} WHERE id = #{id}
    </update>

    <delete id="delete">
        DELETE FROM permissions WHERE id = #{id}
    </delete>
</mapper>

6. 服务层实现

创建一个服务层来封装权限管理的业务逻辑:

UserService.java

package com.example.service;

import com.example.entity.User;
import com.example.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {
    private final UserMapper userMapper;

    @Autowired
    public UserService(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public User getUser(Long id) {
        return userMapper.findById(id);
    }

    @Transactional
    public void addUser(User user) {
        userMapper.insert(user);
    }

    @Transactional
    public void updateUser(User user) {
        userMapper.update(user);
    }

    @Transactional
    public void deleteUser(Long id) {
        userMapper.delete(id);
    }

    @Transactional
    public void assignRole(Long userId, Long roleId) {
        userMapper.addUserRole(userId, roleId);
    }

    @Transactional
    public void removeRole(Long userId, Long roleId) {
        userMapper.removeUserRole(userId, roleId);
    }
}

RoleService.java

package com.example.service;

import com.example.entity.Role;
import com.example.mapper.RoleMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class RoleService {
    private final RoleMapper roleMapper;

    @Autowired
    public RoleService(RoleMapper roleMapper) {
        this.roleMapper = roleMapper;
    }

    public Role getRole(Long id) {
        return roleMapper.findById(id);
    }

    @Transactional
    public void addRole(Role role) {
        roleMapper.insert(role);
    }

    @Transactional
    public void updateRole(Role role) {
        roleMapper.update(role);
    }

    @Transactional
    public void deleteRole(Long id) {
        roleMapper.delete(id);
    }

    @Transactional
    public void assignPermission(Long roleId, Long permissionId) {
        roleMapper.addRolePermission(roleId, permissionId);
    }

    @Transactional
    public void removePermission(Long roleId, Long permissionId) {
        roleMapper.removeRolePermission(roleId, permissionId);
    }
}

PermissionService.java

package com.example.service;

import com.example.entity.Permission;
import com.example.mapper.PermissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PermissionService {
    private final PermissionMapper permissionMapper;

    @Autowired
    public PermissionService(PermissionMapper permissionMapper) {
        this.permissionMapper = permissionMapper;
    }

    public Permission getPermission(Long id) {
        return permissionMapper.findById(id);
    }

    @Transactional
    public void addPermission(Permission permission) {
        permissionMapper.insert(permission);
    }

    @Transactional
    public void updatePermission(Permission permission) {
        permissionMapper.update(permission);
    }

    @Transactional
    public void deletePermission(Long id) {
        permissionMapper.delete(id);
    }
}

7. 测试功能

可以用 Controller 调用服务层,或者直接写测试代码:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CommandLineRunner run(UserService userService, RoleService roleService, PermissionService permissionService) {
        return args -> {
            // 添加权限
            Permission read = new Permission();
            read.setName("read");
            permissionService.addPermission(read);

            // 添加角色
            Role admin = new Role();
            admin.setName("admin");
            roleService.addRole(admin);

            // 为角色分配权限
            roleService.assignPermission(admin.getId(), read.getId());

            // 添加用户
            User user = new User();
            user.setUsername("alice");
            user.setPassword("123");
            userService.addUser(user);

            // 为用户分配角色
            userService.assignRole(user.getId(), admin.getId());

            // 查询用户及其权限
            User foundUser = userService.getUser(user.getId());
            System.out.println("User: " + foundUser.getUsername());
            foundUser.getRoles().forEach(role -> {
                System.out.println("Role: " + role.getName());
                role.getPermissions().forEach(perm -> System.out.println("Permission: " + perm.getName()));
            });
        };
    }
}

8. 关键点说明

  1. 多表查询:通过 <resultMap><collection> 实现用户-角色-权限的嵌套映射。
  2. 事务管理:用 @Transactional 确保分配角色或权限时数据一致性。
  3. 动态 SQL:如果需要更复杂的条件查询,可以在 XML 中用 <if><where> 等标签。

总结

通过 MyBatis,我们可以用灵活的 SQL 和映射机制实现权限管理的 CRUD 操作。

posted on 2025-02-23 13:58  joken1310  阅读(195)  评论(0)    收藏  举报