MyBatis注解开发

目录

1、简单查询

2、动态条件查询

3、一对一、一对多查询

3.1 一对多查询

3.2 一对一查询


虽然现在mybatis通过注解方式绑定sql的方式已经很少用了,都提倡代码和sql语句分离,但是本章还是重点介绍一下MyBatis的注解开发。

mybatis常用的注解主要有以下几种:

  • @Insert
  • @Delete
  • @Update
  • @Select
  • @Results:相当于xml文件里的<resultMap></resultMap>
  • @Result:相当于xml文件里的<result />
  • @Many
  • @One

一般来说,比较少使用@Results注解,因为只需要配置数据库字段名和Java属性名不一样的字段的映射,只需要使用@Result即可。最好情况下,数据库所有的字段的名字和Java实体类属性名一样,这时候可以不配置字段映射关系。

例如:有一个用户表,只有三个简单的字段

CREATE TABLE `nacos`.`user`  (
  `id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;

INSERT INTO `nacos`.`user` (`id`, `username`, `password`) VALUES ('12345', 'heyunlin', 'mhxy1218');
INSERT INTO `nacos`.`user` (`id`, `username`, `password`) VALUES ('22222', 'Tom', '12345');
INSERT INTO `nacos`.`user` (`id`, `username`, `password`) VALUES ('34567', 'Marry', '13618');

1、简单查询

现在一个接口查询所有用户的信息,使用注解的方式查询,则写成这样就可以了,不需要配置结果集映射。

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 查询全部用户
     * @return List<User>
     */
    @Select("select id, username, password from user")
    List<User> selectAll();
}

如果要配置的话,可以使用@Result注解配置结果集的字段映射关系。

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 查询全部用户
     * @return List<User>
     */
    @Result(property = "id", column = "id")
    @Result(property = "username", column = "username")
    @Result(property = "password", column = "password")
    @Select("select id, username, password from user")
    List<User> selectAll();
}

也可以使用@Results + @Result配置,上面的代码等价于

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 查询全部用户
     * @return List<User>
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "password", column = "password")
    })
    @Select("select id, username, password from user")
    List<User> selectAll();
}

当然了,一般这种简单的SQL也不需要我们写,一般项目都会集成Mybatis-Plus或者Tk-Mybatis。

在不使用通用mapper的情况下,通过注解的方式绑定SQL语句还是很便捷的,省去了创建XxxMapper.xml的步骤,而且还要在mapper.xml里创建statement,写上对应的SQL。

2、动态条件查询

其实大多数情况下,MyBatis注解开发要优于xml配置文件开发。

但是,当SQL语句比较复杂的时候,比如说,当我们的查询条件中包含了<if></if>以及<where></where>等动态SQL标签时,sql语句要包含在<script></script>里,这会使得代码的可读性较差。

比如,我们现在有一个需求,要根据用户名查询用户的信息,而且只有传入的用户名不为空才添加查询条件。这时候需要用上<where>和<if>标签

<where>配合<if>可以实现设置动态条件,只有当<where>里面的<if>条件至少有一个成立时才会在我们的sql语句后面添加where子句。

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 通过用户名查询全部用户
     * @return List<User>
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "password", column = "password")
    })
    @Select(
            "select id, username, password from user " +
            "<where>" +
                    "<if test='username != null and username!=\"\"'>" +
                        "username = #{username}" +
                    "</if>" +
            "</where>"
    )
    List<User> selectByUsername(String username);
}

当然也可以不用<where>标签,改用where关键字,常用的做法是这样的:

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 通过用户名查询全部用户
     * @return List<User>
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "password", column = "password")
    })
    @Select(
            "select id, username, password from user " +
            "where 1 = 1" +
            "<if test='username != null and username!=\"\"'>" +
                    " and username = #{username}" +
            "</if>"
    )
    List<User> selectByUsername(String username);
}

然后我们写一个接口,测试一下,报错了

 控制台报错信息

需要在sql语句最外层加上<script></script>标签

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface UserMapper {
    /**
     * 通过用户名查询全部用户
     * @return List<User>
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "username", column = "username"),
            @Result(property = "password", column = "password")
    })
    @Select(
            "<script>" +
                    "select id, username, password from user " +
                    "<where>" +
                            "<if test='username != null and username!=\"\"'>" +
                                "username = #{username}" +
                            "</if>" +
                    "</where>" +
            "</script>"
    )
    List<User> selectByUsername(String username);
}

修改完成后重启项目,发现能正常查询出来

传用户名时查出指定的用户

不传用户名、传空字符串时查询出全部用户

通过上面的案例我们发现,当sql语句很复杂的时候,@Select注解里写的sql可读性不好,可能包含转义字符。这时候可以考虑把sql语句写到Mapper.xml文件中,xml文件中可以直接使用各种标签, 可以说是非常的方便。

<select>

<update>

<insert>

<sql>

<include>

<trim>

<set>

<where>

<if>

<choose>

<when>

<otherwise>

<resultMap>

<collection>

<association>

3、一对一、一对多查询

接下来介绍mybatis一对一和一对多的查询

3.1 一对多查询

如上图,现在有那么一个需求,需要查询用户的订单,而一个订单里可能包含多种商品,这个时候用了子订单来保存商品的信息。 

订单实体类

package cn.edu.sgu.www.fresheveryday.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 订单
 * @author heyunlin
 * @version 1.0
 */
@Data
@TableName("oms_order")
public class Order implements Serializable {
    private static final long serialVersionUID = 18L;

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 订单号
     */
    private String orderId;

    /**
     * 订单状态
     */
    private Integer state;

    /**
     * 订单创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime created;

    /**
     * 运费/配送费
     */
    private Double freight;

    /**
     * 支付方式
     */
    private Short payStyle;

    /**
     * 配送信息id
     */
    private String distId;

    /**
     * 订单总金额/实付金额
     */
    private Double total;

    /**
     * 用户id
     */
    private String userId;

    /**
     * 订单修改次数
     */
    private Integer updateTimes;
}

子订单实体类

package cn.edu.sgu.www.fresheveryday.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;

/**
 * 子订单
 * @author heyunlin
 * @version 1.0
 */
@Data
@TableName("oms_sub_order")
public class SubOrder implements Serializable {
    private static final long serialVersionUID = 18L;

    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 商品id
     */
    private Integer productId;

    /**
     * 商品数量
     */
    private Integer quantity;

    /**
     * 订单金额
     */
    private Double money;

    /**
     * 父订单号
     */
    private String orderId;
}

我们返回给页面的vo类:OrderListVO.java

package cn.edu.sgu.www.fresheveryday.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.time.LocalDateTime;
import java.util.List;

/**
 * 订单vo
 * @author heyunlin
 * @version 1.0
 */
@Data
public class OrderVO {
    /**
     * 订单id(编号)
     */
    private Integer id;

    /**
     * 订单号
     */
    private String orderId;

    /**
     * 订单状态
     */
    private Integer state;

    /**
     * 订单创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime created;

    /**
     * 运费
     */
    private Double freight;

    /**
     * 配送id
     */
    private String distId;

    /**
     * 订单总额
     */
    private Double total;

    /**
     * 子订单信息
     */
    private List<SubOrderVO> subOrders;
}

 SubOrderVO.java

package cn.edu.sgu.www.fresheveryday.vo;

import lombok.Data;

import java.io.Serializable;

/**
 * 子订单vo
 * @author heyunlin
 * @version 1.0
 */
@Data
public class SubOrderVO implements Serializable {
    /**
     * 订单金额
     */
    private Double money;

    /**
     * 父订单号
     */
    private String orderId;

    /**
     * 商品数量
     */
    private Integer quantity;

    /**
     * 商品信息
     */
    private ProductSubOrderDetailVO product;
}

查询用户订单的方法如下,mybatis注解方式查询一对多的时候,在@Result注解里通过many=@Many(select = "类的全限定名.方法名")属性指定一个mapper方法的全路径

* 注意:这里的@Result不能写javaType=SubOrderVO.class,否则运行时会报错

@Result(column = "order_id", property = "subOrders", many = @Many(select = "cn.edu.sgu.www.fresheveryday.mapper.SubOrderMapper.selectDetailsByOrderId"))

package cn.edu.sgu.www.fresheveryday.mapper;

import cn.edu.sgu.www.fresheveryday.entity.Order;
import cn.edu.sgu.www.fresheveryday.pager.OrderPager;
import cn.edu.sgu.www.fresheveryday.vo.OrderVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface OrderMapper extends BaseMapper<Order> {
    /**
     * 分页查询用户订单列表
     * @param pager 分页参数
     * @return List<OrderListVO>
     */
    @Select(
            "select id, order_id, state, created, freight, dist_id, total from oms_order " +
            "where state = #{state} and user_id = #{userId} " +
            "order by created desc limit #{offset}, #{size}"
    )
    @Result(column = "order_id", property = "orderId")
    @Result(
            column = "order_id", property = "subOrders",
            many = @Many(select = "cn.edu.sgu.www.fresheveryday.mapper.SubOrderMapper.selectDetailsByOrderId")
    )
    List<OrderVO> selectByPage(OrderPager pager);
}

3.2 一对一查询

可以看到上面的子订单信息SubOrderVO.java中包含了商品的信息,因为一个子订单保存一类商品的订单信息,所以是一对一的关系。

 一对一的查询我们可以使用one=@One(select = "类的全限定名.方法名")来实现

package cn.edu.sgu.www.fresheveryday.mapper;

import cn.edu.sgu.www.fresheveryday.entity.SubOrder;
import cn.edu.sgu.www.fresheveryday.vo.ProductSubOrderDetailVO;
import cn.edu.sgu.www.fresheveryday.vo.SubOrderVO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * @author heyunlin
 * @version 1.0
 */
@Repository
public interface SubOrderMapper extends BaseMapper<SubOrder> {
    /**
     * 通过父订单号查询子订单
     * called by: OrderMapper.selectByPage()
     * @param orderId 父订单号
     * @return List<SubOrderVO>
     */
    @Select("select product_id, quantity, money from oms_sub_order where order_id = #{orderId}")
    @Result(
            column = "product_id", property = "product", javaType = ProductSubOrderDetailVO.class,
            one = @One(
                    select = "cn.edu.sgu.www.fresheveryday.mapper.ProductMapper.selectSubOrderDetailById"
            )
    )
    List<SubOrderVO> selectDetailsByOrderId(String orderId);
}

好了,文章就分享到这里了,文章涉及的项目已开源,创作不易,看完不要忘了点赞+收藏哦~

天天生鲜超市icon-default.png?t=N7T8https://gitee.com/he-yunlin/fresheveryday.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值