Mybatis注解

Mybatis注解

开发流程

  1. 导入相关依赖

  2. 配置mybatis全局配置文件,创建实体类

  3. 创建mapper接口并使用注解在接口中定义sql操作

    import org.apache.ibatis.annotations.*;
    
    import java.util.List;
    
    public interface UserMapper {
    
        @Select("SELECT * FROM users WHERE id = #{id}")
        User getUserById(int id);
    
        @Select("SELECT * FROM users")
        List<User> getAllUsers();
    
        @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
        @Options(useGeneratedKeys = true, keyProperty = "id")
        void insertUser(User user);
    
        @Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
        void updateUser(User user);
    
        @Delete("DELETE FROM users WHERE id = #{id}")
        void deleteUser(int id);
    }
    
  4. 在全局配置文件中映射指定Mapper接口以及数据源信息

    <configuration>
        <mappers>
            <mapper class="com.example.mapper.UserMapper"/>
        </mappers>
    </configuration>
    
  5. 使用 SqlSessionFactory 获取 SqlSession,再获取 Mapper 接口实例

    • 可以纯注解,先不改了,流程不是重点
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    public class MyBatisExample {
        public static void main(String[] args) {
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(MyBatisExample.getClassLoader().getResourceAsStream("mybatis-config.xml"));
            try (SqlSession session = sqlSessionFactory.openSession()) {
                UserMapper mapper = session.getMapper(UserMapper.class);
    
                // 使用 Mapper 方法
                User user = mapper.getUserById(1);
                System.out.println(user.getName());
            }
        }
    }
    

SQL语句获取参数

  1. 使用 @Param 注解

    • @Param 注解用于给方法参数命名,SQL 语句中可以通过 #{参数名} 的方式引用参数值

      @Update("UPDATE user SET username=#{username} WHERE id=#{id}")
      int updateUsernameById(@Param("id") int id, @Param("username") String username);
      
  2. 单个参数

    • 如果方法只有一个参数,可以直接在 SQL 语句中通过 #{参数名} 引用参数值,无需使用 @Param 注解

      @Select("SELECT * FROM user WHERE id = #{id}")
      User getUserById(int id);
      
  3. 默认参数名

    • 如果方法参数没有使用 @Param 注解,MyBatis 会使用默认的参数名

      • 单个参数:_parameter

      • 多个参数:param1, param2, ...

        @Select("SELECT * FROM user WHERE id = #{param1} AND username = #{param2}")
        User getUserByIdAndUsername(int id, String username);
        
  4. 使用 Map 传递参数

    • 可以将参数封装到一个 Map 中,SQL 语句中通过 #{key} 引用 Map 中的值

      @Update("UPDATE user SET username=#{username} WHERE id=#{id}")
      int updateUsernameById(Map<String, Object> params);
      
  5. 使用对象传递参数

    • 可以将参数封装到一个 Java 对象中,SQL 语句中通过 #{属性名} 引用对象的属性值

      public class User {
          private int id;
          private String username;
          // Getters and Setters
      }
      
      @Update("UPDATE user SET username=#{username} WHERE id=#{id}")
      int updateUsernameById(User user);
      
  6. 多个对象传递参数

    • 通过 @Param 注解为每个对象命名,SQL 语句中通过 #{对象名.属性名} 引用对象的属性值

      public class User {
          private int id;
          private String username;
          // Getters and Setters
      }
      
      public class Address {
          private String city;
          private String street;
          // Getters and Setters
      }
      
      @Update("UPDATE user SET username=#{user.username}, city=#{address.city} WHERE id=#{user.id}")
      int updateUserAndAddress(@Param("user") User user, @Param("address") Address address);
      
  7. 使用 #{}${} 的区别

    • #{}:
      • 是预编译占位符,MyBatis 会将其替换为 ?,并使用 PreparedStatement 设置参数。
      • 可以防止 SQL 注入。
      • 适用于传递参数值。
    • ${}:
      • 是字符串替换,MyBatis 会直接将其替换为参数值。
      • 不能防止 SQL 注入。
      • 适用于动态拼接 SQL 片段(如表名、列名)。

SQL语句设置返回值

  1. 基本返回值类型

    • 基本返回值类型是指那些简单的、非结构化的数据类型,例如基本数据类型(intlong 等)、包装类型(IntegerLong 等)、字符串类型(String)等。这些类型通常用于简单的查询或操作,比如统计查询、单值查询等。

      @Select("SELECT COUNT(*) FROM user")
      int countUsers();
      
      @Select("SELECT age FROM user WHERE id = #{id}")
      int getAgeById(int id);
      
  2. 对象返回值类型

    • 对象返回值类型是指将查询结果映射到一个 Java 对象(通常是实体类)。这种方式适用于查询结果包含多个字段,并且需要将这些字段映射到一个对象的场景。MyBatis 提供了多种方式来实现对象返回值类型的映射,包括自动映射和手动映射

    • 自动映射

      • MyBatis 支持自动将查询结果的列映射到实体对象的属性中。只要查询结果的列名与实体对象的属性名一致,MyBatis 就会自动完成映射
      public class User {
          private int id;
          private String username;
          private String email;
          // Getters and Setters
      }
      
      @Select("SELECT id, username, email FROM user WHERE id = #{id}")
      User getUserById(int id);
      
    • 手动映射

      • 如果查询结果的列名与实体对象的属性名不一致,可以使用 @Results@Result 注解手动指定映射关系
      @Results({
          @Result(property = "id", column = "user_id"),
          @Result(property = "username", column = "user_name"),
          @Result(property = "email", column = "user_email")
      })
      @Select("SELECT user_id, user_name, user_email FROM user WHERE id = #{id}")
      User getUserByIdWithCustomMapping(int id);
      
    • 嵌套对象映射

      • 如果实体对象中包含嵌套对象(即对象属性是另一个对象),可以使用 @Result 注解的 columnPrefix 属性来映射嵌套对象
      public class Address {
          private String city;
          private String street;
          // Getters and Setters
      }
      
      public class User {
          private int id;
          private String username;
          private Address address;
          // Getters and Setters
      }
      
      @Results({
          @Result(property = "id", column = "id"),
          @Result(property = "username", column = "username"),
          @Result(property = "address.city", column = "city"),
          @Result(property = "address.street", column = "street")
      })
      @Select("SELECT id, username, city, street FROM user WHERE id = #{id}")
      User getUserWithAddressById(int id);
      
  3. 集合返回值类型

    • List集合

      • 如果查询结果返回多条记录,并且每条记录需要映射为一个对象,可以直接将返回值类型设置为 List<实体类>

      • 自动映射

        • 如果查询结果的列名与实体对象的属性名一致,MyBatis 会自动完成映射

        • public class User {
              private int id;
              private String username;
              private String email;
              // Getters and Setters
          }
          
          @Select("SELECT id, username, email FROM user")
          List<User> getAllUsers();
          
      • 手动映射

        • 如果查询结果的列名与实体对象的属性名不一致,可以使用 @Results@Result 注解手动指定映射关系

        • @Results({
              @Result(property = "id", column = "user_id"),
              @Result(property = "username", column = "user_name"),
              @Result(property = "email", column = "user_email")
          })
          @Select("SELECT user_id, user_name, user_email FROM user")
          List<User> getAllUsersWithCustomMapping();
          
    • Set集合

      • Set 是一种不允许重复元素的集合,适用于需要去重的场景

      • @Select("SELECT DISTINCT username FROM user")
        Set<String> getAllUsernames();
        
    • Map集合

      • Map 是一种键值对的集合,适用于将查询结果的某一列作为键,另一列作为值的场景

      • @Select("SELECT id, username FROM user")
        @MapKey("id")
        Map<Integer, User> getUserMap();
        
        • @MapKey("id") 注解指定将 id 列作为 Map 的键。
        • MyBatis 会将每条记录映射为 User 对象,并将 idUser 对象放入 Map 中返回。
        • 如果查询结果为空,返回一个空的 Map
  4. 嵌套集合

    • 如果查询结果包含嵌套集合(即一个对象的属性是集合),可以使用 @Results@Result 注解手动映射
  5. 返回 Map 对象

    • 如果查询结果需要以键值对的形式返回,可以将返回值类型设置为 Map

    • MyBatis 会将查询结果的列名作为键,列值作为值,放入 Map 中返回

      @Select("SELECT * FROM user WHERE id = #{id}")
      Map<String, Object> getUserByIdAsMap(int id);
      

嵌套查询

  1. one 的使用

    • one 属性用于处理 一对一 的关系,即一个对象中包含另一个对象

    • 假设有两个表:user 表和 address 表,一个用户对应一个地址,根据用户表中的相关信息可以查询地址表

    • public class User {
          private int id;
          private String username;
          private Address address; // 一对一关系
          // Getters and Setters
      }
      
      public class Address {
          private int id;
          private String city;
          private String street;
          // Getters and Setters
      }
      
      @Results({
          @Result(property = "id", column = "id"),
          @Result(property = "username", column = "username"),
          @Result(property = "address", column = "address_id", 
                  one = @One(select = "getAddressById"))
      })
      @Select("SELECT id, username, address_id FROM user WHERE id = #{id}")
      User getUserWithAddress(int id);
      
      @Select("SELECT id, city, street FROM address WHERE id = #{id}")
      Address getAddressById(int id);
      
    • @Result(property = "address", column = "address_id", one = @One(select = "getAddressById")):

      • property = "address": 指定 User 对象中的 address 属性。
      • column = "address_id": 指定查询结果中的 address_id 列作为参数传递给嵌套查询。
      • one = @One(select = "getAddressById"): 指定使用 getAddressById 方法查询 Address 对象。
    • getAddressById(int id): 根据 address_id 查询 Address 对象

  2. many 的使用

    • many 属性用于处理 一对多 的关系,即一个对象中包含一个集合

    • 假设有两个表:user 表和 order 表,一个用户对应多个订单

    • public class User {
          private int id;
          private String username;
          private List<Order> orders; // 一对多关系
          // Getters and Setters
      }
      
      public class Order {
          private int id;
          private String orderNo;
          // Getters and Setters
      }
      
      @Results({
          @Result(property = "id", column = "id"),
          @Result(property = "username", column = "username"),
          @Result(property = "orders", column = "id", 
                  many = @Many(select = "getOrdersByUserId"))
      })
      @Select("SELECT id, username FROM user WHERE id = #{id}")
      User getUserWithOrders(int id);
      
      @Select("SELECT id, order_no FROM orders WHERE user_id = #{userId}")
      List<Order> getOrdersByUserId(int userId);
      
    • @Result(property = "orders", column = "id", many = @Many(select = "getOrdersByUserId")):

      • property = "orders": 指定 User 对象中的 orders 属性。
      • column = "id": 指定查询结果中的 id 列作为参数传递给嵌套查询。
      • many = @Many(select = "getOrdersByUserId"): 指定使用 getOrdersByUserId 方法查询 Order 列表。
    • getOrdersByUserId(int userId): 根据 user_id 查询 Order 列表

动态SQL

  • MyBatis 注解本身不支持直接在注解中编写复杂的动态 SQL(如 <if><choose> 等 XML 标签),但可以通过 @SelectProvider@UpdateProvider@InsertProvider@DeleteProvider 等注解结合 Java 代码实现动态 SQL

  • MyBatis 注解中实现动态 SQL 的主要方式是使用 @*Provider 注解(如 @SelectProvider@UpdateProvider 等),并编写一个 Java 类来生成动态 SQL

  • 常用的 @*Provider 注解

    • @SelectProvider: 动态生成查询 SQL。
    • @UpdateProvider: 动态生成更新 SQL。
    • @InsertProvider: 动态生成插入 SQL。
    • @DeleteProvider: 动态生成删除 SQL。
  • MyBatis 提供了一个 SQL 类,用于以编程方式构建 SQL 语句。SQL 类的方法链式调用可以方便地生成动态 SQL

  • 参数传递: 当调用 Mapper 方法时,MyBatis 会将方法的参数封装到一个 Map 中,键是参数名,值是参数值。

  • 动态 SQL 生成: 在 Provider 方法中,可以通过 params.get("key") 获取参数值,并根据参数值动态生成 SQL

  • SQL 类的常用方法

    • SELECT(String): 添加 SELECT 子句。
    • FROM(String): 添加 FROM 子句。
    • WHERE(String): 添加 WHERE 子句。
    • SET(String): 添加 SET 子句。
    • INSERT_INTO(String): 添加 INSERT INTO 子句。
    • VALUES(String, String): 添加 VALUES 子句。
    • UPDATE(String): 添加 UPDATE 子句。
    • DELETE_FROM(String): 添加 DELETE FROM 子句。
    • AND(): 添加 AND 条件。
    • OR(): 添加 OR 条件

动态查询

根据条件动态生成查询 SQL。

  • Java类

    public class UserSqlProvider {
        public String findUserByCondition(Map<String, Object> params) {
            return new SQL() {{
                SELECT("*");
                FROM("user");
                if (params.get("username") != null) {
                    WHERE("username = #{username}");
                }
                if (params.get("age") != null) {
                    WHERE("age = #{age}");
                }
                if (params.get("city") != null) {
                    WHERE("city = #{city}");
                }
            }}.toString();
        }
    }
    
  • Mapper接口

    @SelectProvider(type = UserSqlProvider.class, method = "findUserByCondition")
    List<User> findUserByCondition(@Param("username") String username, @Param("age") Integer age, @Param("city") String city);
    

动态更新

根据条件动态生成更新 SQL

  • Java类

    public class UserSqlProvider {
        public String updateUser(Map<String, Object> params) {
            return new SQL() {{
                UPDATE("user");
                if (params.get("username") != null) {
                    SET("username = #{username}");
                }
                if (params.get("age") != null) {
                    SET("age = #{age}");
                }
                if (params.get("city") != null) {
                    SET("city = #{city}");
                }
                WHERE("id = #{id}");
            }}.toString();
        }
    }
    
  • Mapper 接口

    @UpdateProvider(type = UserSqlProvider.class, method = "updateUser")
    int updateUser(@Param("id") int id, @Param("username") String username, @Param("age") Integer age, @Param("city") String city);
    

动态插入

根据条件动态生成插入 SQL

  • Java类

    public class UserSqlProvider {
        public String insertUser(Map<String, Object> params) {
            return new SQL() {{
                INSERT_INTO("user");
                if (params.get("username") != null) {
                    VALUES("username", "#{username}");
                }
                if (params.get("age") != null) {
                    VALUES("age", "#{age}");
                }
                if (params.get("city") != null) {
                    VALUES("city", "#{city}");
                }
            }}.toString();
        }
    }
    
  • Mapper 接口

    @InsertProvider(type = UserSqlProvider.class, method = "insertUser")
    int insertUser(@Param("username") String username, @Param("age") Integer age, @Param("city") String city);
    

动态删除

根据条件动态生成删除 SQL

  • Java类

    public class UserSqlProvider {
        public String deleteUser(Map<String, Object> params) {
            return new SQL() {{
                DELETE_FROM("user");
                if (params.get("id") != null) {
                    WHERE("id = #{id}");
                }
                if (params.get("username") != null) {
                    WHERE("username = #{username}");
                }
            }}.toString();
        }
    }
    
  • Mapper接口

    @DeleteProvider(type = UserSqlProvider.class, method = "deleteUser")
    int deleteUser(@Param("id") Integer id, @Param("username") String username);
    

分页查询-PageHelper

  • PageHelper 是 MyBatis 的一个非常流行的开源分页插件,它可以极大地简化分页查询的实现。通过拦截 MyBatis 的 SQL 语句,PageHelper 能够自动为查询添加分页逻辑,并返回分页结果。

PageHelper 的核心功能

  • 自动分页:只需在查询前调用 PageHelper.startPage 方法,后续的查询会自动进行分页。
  • 支持多种数据库:PageHelper 支持 MySQL、Oracle、SQL Server 等多种数据库,并自动适配数据库的分页语法。
  • 返回分页信息:除了分页数据外,还可以返回总条数、总页数等分页信息。
  • 易于集成:只需简单的配置即可集成到 MyBatis 项目中。

PageHelper.startPage 的作用范围

  • 生效范围PageHelper.startPage 只会对紧随其后的第一条查询语句生效。
  • 如果需要同时对多条查询语句进行分页,需要为每条查询语句单独调用 PageHelper.startPage
  • 失效条件
    • 如果执行了第一条查询语句,分页参数会自动清除。
    • 如果调用了 PageHelper.clearPage(),分页参数也会被清除。

集成 PageHelper

  • 在 Maven 项目中,添加 PageHelper 的依赖:

    • <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>5.3.2</version> <!-- 以最新版本为准 -->
      </dependency>
      
  • 配置插件

    • mybatis-config.xml中配置 PageHelper 插件:

    • <plugins>
          <plugin interceptor="com.github.pagehelper.PageInterceptor">
              <!-- 指定数据库方言 -->
              <property name="helperDialect" value="mysql"/>
              <!-- 分页合理化:当页码超出范围时,自动调整到合理范围 -->
              <property name="reasonable" value="true"/>
              <!-- 支持通过 Mapper 接口参数传递分页参数 -->
              <property name="supportMethodsArguments" value="true"/>
              <!-- 自动检测数据库方言 -->
              <property name="autoDialect" value="true"/>
          </plugin>
      </plugins>
      
    • SpringBoot中配置

    • # PageHelper 配置
      pagehelper.helperDialect=mysql
      pagehelper.reasonable=true
      pagehelper.supportMethodsArguments=true
      pagehelper.autoDialect=true
      
  • 使用PageHelper 实现分页

    • 在查询方法前调用 PageHelper.startPage,后续的查询会自动分页。

      • import com.github.pagehelper.PageHelper;
        import com.github.pagehelper.PageInfo;
        
        public PageInfo<User> getUsersByPage(int pageNum, int pageSize) {
            // 开始分页,pageNum 是当前页码,pageSize 是每页条数
            PageHelper.startPage(pageNum, pageSize);
            
            // 正常查询,无需手动编写分页 SQL
            List<User> users = userMapper.selectAllUsers();
            
            // 返回分页信息
            return new PageInfo<>(users);
        }
        
    • 分页结果 PageInfo

      • PageInfo 是 PageHelper 提供的分页结果类,包含了分页信息和数据列表。常用属性如下:

        • list:当前页的数据列表。
        • pageNum:当前页码。
        • pageSize:每页条数。
        • total:总条数。
        • pages:总页数。
        • hasPreviousPage:是否有上一页。
        • hasNextPage:是否有下一页。
      • PageInfo<User> pageInfo = getUsersByPage(1, 10);
        System.out.println("当前页: " + pageInfo.getPageNum());
        System.out.println("每页条数: " + pageInfo.getPageSize());
        System.out.println("总条数: " + pageInfo.getTotal());
        System.out.println("总页数: " + pageInfo.getPages());
        System.out.println("数据列表: " + pageInfo.getList());
        
    • 方法的作用

      • PageHelper.startPage(pageNum, pageSize)
        • 调用 PageHelper 的 startPage 方法,开启分页功能。
        • 后续的查询会自动添加分页逻辑(如 MySQL 的 LIMITOFFSET)。
      • userMapper.selectAllUsers()
        • 执行查询操作,获取当前页的数据。
        • 由于开启了分页,查询结果只会返回当前页的数据(如第 1 页的 10 条数据)。
      • new PageInfo<>(users)
        • 将查询结果封装到 PageInfo 对象中。
        • PageInfo 会自动计算总条数、总页数等分页信息。

注意事项:

  • 线程安全问题
    • PageHelper.startPage 是基于 ThreadLocal 实现的,因此必须确保每次分页查询都在独立的线程中执行。
    • 如果使用了线程池,需要在每次查询后调用 PageHelper.clearPage() 清理分页参数。
  • SQL 优化
    • PageHelper 会自动生成分页 SQL,但对于复杂查询(如多表关联查询),可能需要手动优化 SQL。
  • 数据库方言
    • 如果项目需要支持多种数据库,建议将 autoDialect 设置为 true,PageHelper 会自动检测数据库类型。
posted @ 2025-03-17 22:08  QAQ001  阅读(35)  评论(0)    收藏  举报