MyBatis注解

@Mapper

   @Mapper 注解通常用于Java项目中,特别是在使用MyBatis或类似的ORM(对象关系映射)框架时。这个注解的主要目的是简化Mapper接口的实现过程,使得开发者不需要手动编写实现类,框架会自动处理SQL语句的执行和结果集的映射。

使用场景
   在MyBatis中,@Mapper注解可以应用于接口上,标记这个接口为一个Mapper接口,即这个接口中定义的方法将会与数据库中的表进行交互。MyBatis会扫描这些带有@Mapper注解的接口,并动态地生成实现类,实现类中包含了执行SQL语句和映射结果集到Java对象的逻辑。

优点
简化开发:开发者只需要关注SQL语句的编写和接口方法的定义,无需关心SQL的执行和结果集的映射。
提高可维护性:SQL语句和Java代码分离,便于管理和维护。
灵活性:支持动态SQL,可以根据不同的条件构建不同的SQL语句。

示例
   假设有一个用户表user,我们想要通过MyBatis来操作这个表,可以定义一个Mapper接口如下:

@Mapper  
public interface UserMapper {  
    // 根据ID查询用户  
    User selectUserById(Integer id);  
  
    // 插入用户  
    int insertUser(User user);  
  
    // 更新用户  
    int updateUser(User user);  
  
    // 删除用户  
    int deleteUser(Integer id);  
}

   在这个例子中,UserMapper接口被@Mapper注解标记,MyBatis会识别这个接口,并为其生成实现类。然后,你就可以在Service层或其他地方注入UserMapper接口,直接调用其方法来操作数据库了。

注意事项

  1. 确保MyBatis的配置文件(如mybatis-config.xml)中配置了Mapper接口的位置,以便MyBatis能够扫描到这些接口。
  2. 在Spring Boot项目中,通常不需要在每个Mapper接口上都加上@Mapper注解,而是可以在启动类上使用@MapperScan注解来指定Mapper接口所在的包路径,这样Spring Boot会自动扫描并注册这些Mapper接口。
  3. SQL语句通常写在XML文件中,或者通过注解的方式直接写在Mapper接口的方法上。MyBatis会根据这些信息来执行SQL语句并映射结果集。

注解的方式
@Select: 用于标注在 Mapper 接口的方法上,指定 SQL 查询语句。
@Insert: 用于标注在 Mapper 接口的方法上,指定 SQL 插入语句。
@Update: 用于标注在 Mapper 接口的方法上,指定 SQL 更新语句。
@Delete: 用于标注在 Mapper 接口的方法上,指定 SQL 删除语句。
@Param: 用于方法的参数上,当 Mapper 接口方法有多个参数时,可以使用 @Param 注解来命名参数,以便在 SQL 语句中引用。
@Results: 用于映射 SQL 查询结果到 Java 对象。它可以包含多个 @Result 注解,用于指定列名和 Java 对象属性的映射关系。
@Result: 用于 @Results 注解内部,指定列名和 Java 对象属性的映射关系。
@One: 用于一对一关联映射。
@Many: 用于一对多关联映射。

@Select

  1. 查询操作(Select)
    单个对象查询
import org.apache.ibatis.annotations.Param;  
import org.apache.ibatis.annotations.Select;  
  
public interface UserMapper {  
  
    @Select("SELECT * FROM user WHERE id = #{id}")  
    User selectUserById(@Param("id") Integer id);  
}

列表查询

import org.apache.ibatis.annotations.Select;  
  
public interface UserMapper {  
  
    @Select("SELECT * FROM user WHERE age > #{minAge}")  
    List<User> selectUsersByAge(@Param("minAge") int minAge);  
}

@Insert

  1. 插入操作(Insert)
import org.apache.ibatis.annotations.Insert;  
  
public interface UserMapper {  
  
    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")  
    int insertUser(User user);  
}

@Update

  1. 更新操作(Update)
import org.apache.ibatis.annotations.Update;  
  
public interface UserMapper {  
  
    @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")  
    int updateUser(User user);  
}

@Delete

  1. 删除操作(Delete)
import org.apache.ibatis.annotations.Delete;  
  
public interface UserMapper {  
  
    @Delete("DELETE FROM user WHERE id = #{id}")  
    int deleteUser(@Param("id") Integer id);  
}

@Param

   @Param 是 MyBatis 中一个非常有用的注解,它用于命名 Mapper 接口方法的参数。当 Mapper 接口方法中的参数不止一个,或者需要在 XML 映射文件中引用方法参数时,@Param 注解就显得尤为重要。

   在 MyBatis 中,如果 Mapper 接口方法的参数只有一个,那么 MyBatis 可以默认使用这个参数来填充 SQL 语句中的占位符(比如 #{})。但是,如果方法有多个参数,或者虽然只有一个参数但是需要在 SQL 语句中引用它的属性名(比如,传递了一个对象而不是基本数据类型),那么就需要使用 @Param 注解来明确指定参数名。

使用场景

  1. 多个参数的情况:当 Mapper 接口方法接受多个参数时,可以使用 @Param 注解为每个参数命名,以便在 SQL 语句中通过指定的名称来引用它们。
  2. 对象参数的属性访问:虽然通常对象参数的属性可以通过点符号(.)直接访问(假设开启了 useColumnLabel 为 true 或使用 MyBatis 3.4.0 及以上版本并且没有复杂的嵌套结果映射),但在某些情况下,明确指定属性名可以使 SQL 语句更加清晰。
  3. 与 XML 映射文件结合使用:在 XML 映射文件中,当需要引用 Mapper 接口方法的参数时,可以使用 @Param 注解提供的名称。

示例
多个参数

public interface UserMapper {  
    // 使用 @Param 注解为参数命名  
    User selectUserByIdAndName(@Param("id") Integer id, @Param("name") String name);  
}  
  
<!-- 对应的 XML 映射文件中,可以通过 #{id} 和 #{name} 来引用参数 -->  
<select id="selectUserByIdAndName" resultType="User">  
  SELECT * FROM user WHERE id = #{id} AND name = #{name}  
</select>

对象参数的属性访问(虽然不常用,但可以展示如何使用)
   虽然通常不需要为对象参数的属性使用 @Param(因为可以直接通过点符号访问),但假设有特殊需求:

// 实际上,对于对象参数的属性,通常不需要使用 @Param  
// 这里只是为了展示如果硬要使用的话(通常不推荐)  
public interface UserMapper {  
    // 假设这里为了某种特殊需求,我们还是想通过 @Param 指定属性名(但通常不这么做)  
    // 注意:实际上这并不会改变 SQL 语句中如何引用对象属性的方式  
    User selectUserBySomeCriteria(@Param("user") User user);  
}  
  
<!--XML 映射文件中仍然通过点符号访问对象的属性 -->  
<select id="selectUserBySomeCriteria" resultType="User">  
  SELECT * FROM user WHERE name = #{user.name} AND age = #{user.age}  
</select>

   然而,需要注意的是,上面的 User selectUserBySomeCriteria(@Param(“user”) User user); 示例中,@Param(“user”) 实际上并没有改变 SQL 语句中引用对象属性的方式。它主要的作用是,如果这个方法在 XML 映射文件中被 、、 或 标签的某个属性(如 parameterMap,尽管在 MyBatis 3 中已经不常用了)中引用时,提供了一个明确的参数名。但在大多数情况下,对于对象参数的属性访问,我们直接使用点符号即可。

@Results和@Result

   使用@Results和@Result进行复杂结果映射
当查询结果需要映射到对象的复杂属性(如关联对象或集合)时,可以使用@Results和@Result注解。

import org.apache.ibatis.annotations.Result;  
import org.apache.ibatis.annotations.Results;  
import org.apache.ibatis.annotations.Select;  
  
public interface UserMapper {  
  
    @Select("SELECT u.*, a.address AS address FROM user u LEFT JOIN address a ON u.id = a.user_id WHERE u.id = #{id}")  
    @Results({  
        @Result(property = "id", column = "id"),  
        @Result(property = "name", column = "name"),  
        @Result(property = "address", column = "address", javaType = String.class)  
    })  
    User selectUserWithAddressById(@Param("id") Integer id);  
}

   注意:上面的selectUserWithAddressById方法是一个简化的例子,实际上如果address是一个复杂的对象而不是简单的字符串,你可能需要使用@One或@Many注解来进行关联映射。

@One

   @One 是 MyBatis 在处理关联对象时的一个注解,它用于一对一(One-to-One)关系映射。当你想在查询一个对象时,同时关联查询出另一个与之对应的一个对象时,可以使用 @One 注解。

   @One 注解通常与 标签的功能相对应,但它是用于注解方式的 MyBatis 映射。它告诉 MyBatis 在执行查询时,如何将查询结果中的一个嵌套结果集映射到 Java 对象的关联属性上。

使用场景
   假设有两个表:user 和 user_detail,它们之间是一对一的关系(例如,每个用户都有一个与之对应的用户详情记录)。在查询用户信息时,你可能希望同时获取到该用户的详情信息。

示例
   首先,定义 Java 对象模型:

public class User {  
    private Integer id;  
    private String name;  
    private UserDetail userDetail; // 一对一关联的对象  
  
    // 省略getter和setter方法  
}  
  
public class UserDetail {  
    private Integer id;  
    private Integer userId; // 外键,关联user表的id  
    private String address;  
  
    // 省略getter和setter方法  
}

   然后,在 Mapper 接口中使用 @One 注解来映射关联对象:

import org.apache.ibatis.annotations.One;  
import org.apache.ibatis.annotations.Result;  
import org.apache.ibatis.annotations.Results;  
import org.apache.ibatis.annotations.Select;  
  
public interface UserMapper {  
  
    @Select("SELECT u.*, ud.address FROM user u LEFT JOIN user_detail ud ON u.id = ud.user_id WHERE u.id = #{id}")  
    @Results({  
        @Result(property = "id", column = "id"),  
        @Result(property = "name", column = "name"),  
        @Result(property = "userDetail", javaType = UserDetail.class, one = @One(select = "selectUserDetailById"))  
    })  
    User selectUserWithDetailById(Integer id);  
  
    // 这是一个嵌套查询,用于获取用户详情  
    @Select("SELECT id, user_id, address FROM user_detail WHERE user_id = #{userId}")  
    UserDetail selectUserDetailById(Integer userId);  
}
  1. 在上面的例子中,@One 注解被用于 userDetail 属性的映射上,它指定了一个嵌套查询 selectUserDetailById 来获取用户详情。当执行 selectUserWithDetailById 方法时,MyBatis 会首先执行主查询来获取用户信息,然后对于每个用户,它会执行 selectUserDetailById 方法来获取对应的用户详情,并将结果设置到 User 对象的 userDetail 属性中。

  2. 注意:虽然 @One 注解提供了便利的关联映射方式,但在处理大量数据时,嵌套查询可能会导致性能问题,因为对于主查询中的每一行,都会执行一次嵌套查询。在这种情况下,考虑使用 标签在 XML 映射文件中进行映射,并可能采用连接查询(JOIN)来优化性能。

@Many

   @Many 注解在 MyBatis 的官方注解中并不存在。相反,MyBatis 提供了 @One 和 @Many 的功能对应注解,但 @Many 的功能是通过 @Results 注解中的 @Result 标记为 many 类型的 映射来实现的。在注解方式中,这通常是通过在 @Result 注解内嵌套 @Many 的替代注解(实际上是通过 many 属性或 @Many 类似的自定义注解,但后者不是 MyBatis 原生提供的)来实现的,但更常见的是直接通过 many 属性指定一个查询方法来映射一对多(Many-to-One 或 Many-to-Many)的关系。

   不过,为了说明如何在使用注解时映射一对多关系,我们可以看一个使用 many 属性的例子,而不是 @Many 注解(因为它不存在)。

示例
   假设我们有两个实体类:User 和 Post,其中 User 可以有多个 Post。

public class User {  
    private Integer id;  
    private String name;  
    private List<Post> posts; // 一对多关联  
  
    // 省略getter和setter方法  
}  
  
public class Post {  
    private Integer id;  
    private Integer userId; // 外键  
    private String title;  
    private String content;  
  
    // 省略getter和setter方法  
}

   在 Mapper 接口中,我们可以使用 @Results 和 @Result(带有 many 属性)来映射这种一对多关系:

import org.apache.ibatis.annotations.Many;  
import org.apache.ibatis.annotations.Result;  
import org.apache.ibatis.annotations.Results;  
import org.apache.ibatis.annotations.Select;  
  
// 注意:@Many 不是一个有效的 MyBatis 注解,这里仅用于说明  
// 实际上,我们会使用 many 属性在 @Result 注解中  
public interface UserMapper {  
  
    @Select("SELECT u.*, p.id as 'post.id', p.title as 'post.title', p.content as 'post.content' FROM user u LEFT JOIN post p ON u.id = p.user_id WHERE u.id = #{id}")  
    @Results({  
        @Result(property = "id", column = "id"),  
        @Result(property = "name", column = "name"),  
        @Result(property = "posts", javaType = List.class, column = "id",  
               many = @Many(select = "selectPostsByUserId")) // 注意:这里使用的是 many 属性,而不是 @Many 注解  
    })  
    User selectUserWithPostsById(Integer id);  
  
    @Select("SELECT id, user_id, title, content FROM post WHERE user_id = #{userId}")  
    List<Post> selectPostsByUserId(Integer userId);  
}

   但是,上面的代码中有一点需要注意:MyBatis 原生注解中并没有 @Many。实际上,我们应该在 @Result 注解中使用 many 属性,并指定一个方法来执行嵌套查询,以获取关联的多个对象。然而,由于 MyBatis 的注解支持可能不如 XML 映射文件那么灵活,因此在处理复杂关系时,XML 映射文件通常是更好的选择。

   在上面的代码中,我已经将 many = @Many(select = “selectPostsByUserId”) 修改为了一个假设的语法,以说明意图。实际上,你应该这样写:

@Result(property = "posts", javaType = List.class, column = "id",  
       many = @org.apache.ibatis.annotations.ResultMap(value = "postResultMap"))

   然后,在 Mapper XML 文件中定义一个 resultMap 来映射 Post 列表:

<resultMap id="postResultMap" type="Post">  
  <id column="post_id" property="id"/>  
  <result column="title" property="title"/>  
  <result column="content" property="content"/>  
</resultMap>

   但是,如果你坚持要使用注解并且不想在 XML 文件中定义 resultMap,你可能需要寻找或创建一个自定义的注解处理器来模拟 @Many 的行为,因为 MyBatis 原生并不支持这样的注解。然而,这通常是不必要的,因为 MyBatis 提供的注解和 XML 映射功能已经足够强大,可以满足大多数需求。

动态SQL

   虽然注解方式不如XML方式在动态SQL方面灵活,但MyBatis还是提供了一些基本的动态SQL支持,如@SelectProvider、@InsertProvider等,这些注解允许你编写一个类来动态构建SQL语句。然而,在实际项目中,对于复杂的动态SQL,通常推荐使用XML映射文件。

   在MyBatis中,动态字段通常指的是在构建SQL查询时,根据某些条件动态地包含或排除表中的某些字段(列),或者根据条件动态地构建WHERE子句、ORDER BY子句等。MyBatis提供了强大的动态SQL功能,允许你根据不同的条件来构建不同的SQL语句。

动态SQL主要通过MyBatis的XML Mapper文件中的<if>、<choose>(<when>、<otherwise>)、<where>、<set>、<trim>、<foreach>等标签来实现。

以下是一个基于动态字段的MyBatis示例,展示了如何根据条件动态地选择查询的字段:

Mapper 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.itheima.mapper.ProductsMapper">

    <select id="selectProducts" resultType="com.itheima.pojo.Products">

        SELECT

        <trim suffixOverrides=",">
        <if test="prodIdField">
              prod_id,
        </if>

        <if test="prodNameField">
            prod_name,
        </if>

        <if test="vendIdField">
            vend_id,
        </if>

        <if test="prodPriceField">
            prod_price,
        </if>

        <if test="prodDescField">
            prod_desc,
        </if>
        </trim>

        FROM products

        <where>
            <trim prefixOverrides="AND">
            <if test="prodName != null">
                AND prod_name = #{prodName}
            </if>
            </trim>
        </where>
    </select>

</mapper>
   在这个例子中,selectProducts方法允许你根据传入的参数(prodNameOption、prodIdOption、prodName等)来动态地选择查询的字段,并且还可以根据prodName参数来过滤结果。

Mapper 接口

// UserMapper.java  
@Mapper
public interface ProductsMapper {

    List<Products> selectProducts(Boolean prodNameOption,
                                  Boolean prodIdOption,
                                  String prodName);

使用Mapper
在你的服务层中,你可以这样调用selectProducts方法:

@Service
public class ProductsService {

    @Autowired
    private ProductsMapper productsMapper;

    public List<Products> selectProducts(Boolean prodNameOption,
                                         Boolean prodIdOption,
                                         String prodName) {
                   //调用mapper查询数据                              
        return  productsMapper.selectProducts(prodNameOption,prodIdOption,prodName);
    }

在你的控制层中,你可以这样调用selectProducts方法:

@Slf4j
@RequestMapping("/products")
@RestController
public class ProductsController {

    @Autowired
    private ProductsService productsService;

    @GetMapping
    public Result selectProducts(@RequestParam(value = "prodNameOption", defaultValue = "true") Boolean prodNameOption,
                                 @RequestParam(value = "prodIdOption", defaultValue = "false") Boolean prodIdOption,
                                 String prodName){
//    public Result selectProducts( Boolean prodNameOption,
 //                                 Boolean prodIdOption,
//                                  String prodName){
        log.info("selectProducts数据{},{},{}",prodNameOption,prodIdOption,prodName);

        //调用service查询数据
        List<Products> productsList =  productsService.selectProducts(prodNameOption,prodIdOption,prodName);
        return Result.success(productsList);
    }

}

// 处理查询结果
   在这个例子中,我们设置了prodNameOption和prodIdOption为true,表示我们希望查询结果中包含prod_name和prod_id字段,同时,我们还设置了一个prodName参数来过滤结果。

   然而,需要注意的是,当使用动态字段时,你应该确保你的应用逻辑能够正确处理可能返回的不完整数据(即某些字段可能为null或未包含)。此外,如果数据库表结构发生变化,你也需要相应地更新你的Mapper XML文件和相关的Java代码。

结论
   注解方式在简单场景下非常方便快捷,但对于复杂的SQL语句和映射关系,XML映射文件提供了更多的灵活性和控制力。因此,在选择使用注解还是XML时,应根据项目的具体需求和团队的偏好来决定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值