目录
虽然现在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);
}
好了,文章就分享到这里了,文章涉及的项目已开源,创作不易,看完不要忘了点赞+收藏哦~