Day08.mybatis注解开发
day08-mybatis高级查询、注解开发
第一章 mybatis高级查询【掌握】
1、准备工作
【1】包结构
创建java项目,导入jar包和log4j日志配置文件以及连接数据库的配置文件;

【2】导入SQL脚本
运行资料中的sql脚本:mybatis.sql


【3】创建实体来包,导入资料中的pojo

【4】UserMapper接口
package com.itheima.sh.dao;
import com.itheima.sh.pojo.User;
public interface UserMapper {
//完成根据id查询用户数据;
User selectById(Long id);
}
【5】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.heima.mybatis.mapper.UserMapper">
<!--根据id查询:statement-->
<select id="selectById" resultType="User">
SELECT * FROM tb_user WHERE id=#{id}
</select>
</mapper>
【6】测试
package com.itheima.sh.test;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MybatisTest01 {
private static UserMapper mapper = null;
@BeforeClass
public static void beforeClass() throws Exception {
//1.构建SessionFactory
String resouce = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resouce);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//2.获取session
SqlSession sqlSession = build.openSession(true);
//3.获取接口对象
mapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void selectById() {
User user = mapper.selectById(1L);
System.out.println(user);
}
}
2、表介绍和表关系说明
导入资料中mybatis.sql脚本。新建以下4张表
tb_user:用户表
tb_order:订单表
tb_item:商品表
tb_orderdetail:订单详情表

【表关系】
1.tb_user和 tb_order表关系
tb_user 《==》 tb_order:一对多, 一个人可以下多个订单
tb_order 《==》 tb_user:一对一,一个订单只能属于一个人
结论:tb_user和tb_order属于一对多的关系,需要将一方tb_user的主键作为多方tb_order的外键维护关系
2.tb_order 和 tb_item 表关系
tb_order 《==》 tb_item :一个订单可以有多个商品
tb_item 《==》 tb_order:一个商品可以在多个订单上
结论:tb_order和tb_item属于多对多的关系,需要创建中间表tb_orderdetail维护两个表的关系,并且将两张表 的主键作为中间表的外键
3、一对一查询
需求:通过订单编号20140921003查询出订单信息,并查询出下单人信息。
【实现:关联查询】
【目标】使用多表关联查询,完成根据订单号查询订单信息和下单人信息(订单号:20140921003)
【分析】
一个订单编号对应一个订单,一个订单只能属于一个人。所以上述需求实现是一对一的实现。
【步骤】
1、首先,编写接口方法。编写SQL语句;
2、第二步:分析SQL,封装数据(关联对象);
3、处理多表之间的数据封装(数据库字段名---》实体类的属性名之间的映射)
【实现】
第一步:需求分析
编写多表关联查询SQL,根据订单号查询订单信息及下单人信息;
查询语句以及查询结果:

#方式一:分步查询
#第一步:根据order_number查询订单信息;
SELECT * FROM tb_order WHERE order_number = '20140921003';
#第二步:根据订单信息中的user_id查询出下单人的信息;
SELECT * FROM tb_user WHERE id = 1;
#方式二:多表关联查询,内连接
SELECT * FROM tb_order tbo inner join tb_user tbu on tbo.user_id = tbu.id where
tbo.order_number='20140921003'
#多表数据封装问题:
#关联对象封装数据(在Order中引用User)
第二步:添加关联
修改Order:
在Order类中,添加关联对象User,并添加getter和setter方法;
package com.itheima.sh.pojo;
/**
* 订单表
*
*/
public class Order {
private Integer id;
private String orderNumber;
//关联User对象
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Order{" +
"id=" + id +
", orderNumber='" + orderNumber + '\'' +
", user=" + user +
'}';
}
}
第三步:添加方法
编写OrderMapper接口

public interface OrderMapper {
/**
* 根据订单号查询订单及下单人的信息:方式二
* @param orderNumber
* @return
*/
Order queryOrderAndUserByOrderNumber2(@Param("orderNumber")String orderNumber);
}
第四步:编写SQL
在OrderMapper.xml中编写对应的SQL,并将OrderMapper.xml加入到mybatis-config.xml全局配置中;

【OrderMapper.xml代码;】
说明:
association:配置关联对象(User)的映射关系
<association property="user" javaType="User" autoMapping="true">
</association>
属性:
property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象 成员变量名
javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型
<?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">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.sh.dao.OrderMapper">
<!--
1.autoMapping="true" 表示只需要给当前表的id然后自动映射当前表的其他列值到
对应实体类的属性中,这属于偷懒行为,开发中我们最好都书写出来
2.id标签表示id的映射关系
3.result标签表示其他列和pojo类的属性映射关系
4.一对一映射关系使用子标签association来表示引用的另一个pojo类的对象
-->
<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
<!--主表主键-->
<id column="id" property="id"/>
<!--关联关系-->
<!--
1.property="user" 表示在Order类中的引用的User类的对象成员变量名
2.javaType="User" 表示引用的user对象属于User类型
-->
<association property="user" javaType="User" autoMapping="true">
<!--从表主键-->
<id column="id" property="id"/>
<!--<result column="user_name" property="userName"/>-->
</association>
</resultMap>
<!--多表关联查询:一对一-->
<select id="queryOrderAndUserByOrderNumber2" resultMap="orderAndUserResultRelative">
SELECT
*
FROM
tb_order tbo
INNER JOIN tb_user tbu ON tbo.user_id = tbu.id
WHERE
tbo.order_number = #{orderNumber}
</select>
</mapper>
说明:
1、由于queryOrderAndUserByOrderNumber2查询的结果Order对象中需要封装User信息,所以返回值不能够再使用单纯的resultType来操作;
2、定义resultMap进行关联查询的配置,其中:
属性:
id:标识这个resultMap;
type:返回的结果类型
autoMapping="true": 表示只需要给当前表的id然后自动映射当前表的其他列值到对应实体类的属性中,这 属于偷懒行为,开发中我们最好都书写出来
子元素:
id:主表主键映射
result:主表普通字段的映射
association:关联对象的映射配置
3、association:配置关联对象(User)的映射关系
属性:
property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象 成员变量名
javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型
第五步:测试
package com.itheima.sh.test;
import com.itheima.sh.dao.OrderMapper;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.Order;
import com.itheima.sh.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.InputStream;
public class MybatisTest02 {
private static OrderMapper mapper = null;
@BeforeClass
public static void beforeClass() throws Exception {
//1.构建SessionFactory
String resouce = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resouce);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//2.获取session
SqlSession sqlSession = build.openSession(true);
//3.获取接口对象
mapper = sqlSession.getMapper(OrderMapper.class);
}
@Test
public void selectById() {
Order order = mapper.queryOrderAndUserByOrderNumber2("20140921003");
System.out.println("order = " + order);
}
}
【测试结果】

注意事项
通过上述测试结果,我们发现User的id是错误的,不是3,正确结果是1:

因为tb_user表的主键是id,tb_order的主键也是id。查询的结果中有两列相同的id字段。在将查询结果封装到实体类的过程中就会封装错误。
注意:user表查询的是id不是id1,由于SQLyog图形化界面显示的原因。可以在cmd窗口查看结果:

【解决方案】
1、建议将所要查询的所有字段显示地写出来;
2、将多表关联查询结果中,相同的字段名取不同的别名;


resultMap中应该如下配置:

【正确结果】

【小结】
一对一关联查询:
1、需要在Order实体类中关联User对象;最终将数据封装到Order中;
2、在OrderMapper.xml文件中书写关联语句并配置关系;
3、关联关系配置:
<resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
<!--主表主键-->
<id column="oid" property="id"/>
<!--关联关系-->
<association property="user" javaType="User" autoMapping="true">
<!--从表主键-->
<id column="uid" property="id"/>
</association>
</resultMap>
4、一对多查询
【目标】查询id为1的用户及其订单信息
【分析】
一个用户可以有多个订单。
一个订单只能属于一个用户。
用户(1)-----订单(n)
【步骤】
第一步:查询SQL分析;
第二步:添加关联关系;
第三步:编写接口方法;
第四步:编写映射文件;
第五步:测试
【实现】
第一步:需求分析
编写SQL实现查询id为1的用户及其订单信息
查询语句及查询结果:

#查询id为1的用户及其订单信息
select * from tb_user where id=1;
select * from tb_order where user_id=1;
#一对多 内连接查询
select * from tb_user tbu inner join tb_order tbo on tbu.id = tbo.user_id where tbu.id=1;
# 封装数据:关联对象,一个用户关联多个订单 User(List<Order> orderList)
说明:一个用户关联多个订单 User(List
第二步:添加映射关系
因为一个用户可以拥有多个订单,所以用户和订单是一对多的关系;需要在User类中添加一个List<Order> 属性;
package com.itheima.sh.pojo;
import java.io.Serializable;
import java.util.List;
public class User implements Serializable {
private Long id;
// 用户名
private String userName;
// 密码
private String password;
// 姓名
private String name;
// 年龄
private Integer age;
//0 女性 1 男性
private Integer sex;
//订单
List<Order> orders;
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", orders=" + orders +
'}';
}
}
第三步:编写接口方法
在UserMapper接口中,添加关联查询;
/**
* 根据用户id查询用户及其订单信息
* @param id
* @return
*/
User oneToManyQuery(@Param("id") Long id);
第四步:编写SQL
在UserMapper.xml文件中编写SQL语句完成一对多的关联查询;
说明:
1.一对多使用collection子标签进行关联多方Order
<collection property="类中引用多方的成员变量名" javaType="存放多方容器的类型" ofType="多方类型" autoMapping="true">
</collection>
2.属性:
1)property="orders" 这里的orders表示User类的成员变量orders
2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List 一般不书写
3) ofType="Order" 表示List集合中存储数据的类型 Order
3.一定要记住这里给user表的id起别名是uid,order表的id起别名是oid.在resultMap标签的id子标签中的column属性值书写对应的uid和oid.
<!--自定义结果集-->
<resultMap id="oneToManyResult" type="User" autoMapping="true">
<!--User的主键-->
<id column="uid" property="id"/>
<!--Order关联映射-->
<!--
1.一对多使用collection子标签进行关联多方Order
2.属性:
1)property="orders" 这里的orders表示User类的成员变量orders
2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List,可以不配置
3) ofType="Order" 表示List集合中存储数据的类型 Order
-->
<collection property="orders" javaType="List" ofType="Order" autoMapping="true">
<!--Order的主键-->
<id column="oid" property="id" />
</collection>
</resultMap>
<!--根据用户ID查询用户及其订单数据-->
<select id="oneToManyQuery" resultMap="oneToManyResult">
SELECT
tbo.id as oid,
tbo.order_number,
tbu.id as uid,
tbu.user_name,
tbu.password,
tbu.name,
tbu.age,
tbu.sex
FROM
tb_user tbu
INNER JOIN tb_order tbo ON tbu.id = tbo.user_id
WHERE
tbu.id = #{id}
</select>
第五步:测试
在用户的测试类中
public class MybatisTest01 {
private static UserMapper mapper = null;
@BeforeClass
public static void beforeClass() throws Exception {
//1.构建SessionFactory
String resouce = "mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resouce);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
//2.获取session
SqlSession sqlSession = build.openSession(true);
//3.获取接口对象
mapper = sqlSession.getMapper(UserMapper.class);
}
//根据用户ID查询用户及其订单数据
@Test
public void oneToManyQuery() {
User user = mapper.oneToManyQuery(1L);
System.out.println("user = " + user);
}
}

【小结】
一对多关系配置:
1、在对象中添加映射关系;
2、编写接口方法,编写SQL;
3、编写resultMap处理数据库字段和实体类之间数据的封装;
5、高级查询小结
resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
resultMap:
属性:
type 结果集对应的数据类型 Order
id 唯一标识,被引用的时候,进行指定
autoMapping 开启自动映射
extends 继承
子标签:
id:配置id属性
result:配置其他属性
association:配置一对一的映射
property 定义对象的属性名
javaType 属性的类型
autoMapping 开启自动映射
collection:配置一对多的映射
property 定义对象的属性名
javaType 集合的类型
ofType 集合中的元素类型 泛型
autoMapping 开启自动映射
第二章 mybatis注解开发【掌握】
1、概述
上述我们已经学习mybatis的SQL映射文件可以使用xml的方式配置,但是我们发现不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句也比较麻烦。所以Mybatis为用户提供了快速的开发方式,因为有时候大量的XML配置文件的编写时非常繁琐的,因此Mybatis也提供了更加简便的基于注解(Annnotation)的配置方式。
注解配置的方式在很多情况下能够取代mybatis的映射文件,提高开发效率。
2、注解实现CRUD
说明:在演示注解开发之前,为了避免和之前的映射文件混淆,所以可以将之前书写的代码放到一个新的工程中,删除映射文件即可。
2.0、CRUD相关注解
【注解】
@Insert:保存
Value:sql语句(和xml的配置方式一模一样)
@Update:更新
Value:sql语句
@Delete: 删除
Value:sql语句
@Select: 查询
Value:sql语句
@Options:可选配置(获取主键)
userGeneratedKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
keyProperty :对象属性
keyColumn : 列名
【使用方式】
【第一步】将mybatis全局配置文件mybatis-config.xml中的mapper路径改为包扫描或者class路径;
说明:因为没有了映射文件,所以我们这里采用加载接口方式,需要告知mybatis哪个接口的方法上的注解需要被执行。

【第二步】编写接口和注解;
【第三步】测试
2.1、新增
目标:使用注解@Insert的方式新增数据
步骤:
第一步:UserMapper接口中新增用户方法上面编写注解;
第二步:测试
实现:
第一步:在UserMapper接口中的saveUser()方法上面添加@Insert注解,并设置该注解的value属性值为具体的SQL语句;
public interface UserMapper {
//1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
@Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
void saveUser(User user);
}
第二步:测试
在UserMapperTest类下面对save方法进行测试:
public class MybatisTest01 {
private static UserMapper userMapper;
@BeforeClass
public static void berofeClass() throws Exception {
//1、从xml中构建SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//这里指定了环境为test
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test");
//build不方法不指定环境就使用默认的 <environments default="development">
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3、获取UserMapper接口的动态代理对象
userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void saveUser(){
User user = new User();
user.setUserName("锁哥");
user.setAge(18);
user.setName("黑旋风");
user.setPassword("1234");
user.setSex(1);
userMapper.saveUser(user);
}
}
小结:
新增数据的注解为:@Insert ,作用等同于映射文件中的<insert>具体使用时,需要给其value属性设置具体的SQL。
2.2、删除
目标:使用注解@Delete删除id值为1的数据
步骤:
第一步:在根据id删除数据的方法上面编写注解@Delete;
第二步:测试
实现:
第一步:在UserMapper接口中的deleteUserById方法上编写@Delete,并设置其value属性值为具体的删除SQL;
/*
2.根据id删除用户
*/
@Delete("delete from user where id=#{id}")
void deleteUserById(Long id);
第二步:测试
@Test
public void deleteUserById(){
userMapper.deleteUserById(1L);
}
小结:
删除数据的注解:@Delete,作用等同于映射文件中的<delete>,具体使用时,需要设置其value属性值为具体的删除SQL;
2.3、修改
目标:修改id为1的用户的数据
步骤:
第一步:在根据id修改用户数据方法上面添加注解@Update,然后在其value属性值中编写具体的SQL;
第二步:测试
实现:
第一步:在UserMapper接口的updateUser方法上添加注解:@Update,然后将其value属性值设置成update的SQL;
/**
* 3.修改用户数据
* @param user
*/
@Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
void updateUser(User user);
第二步:测试
@Test
public void updateUser(){
User user = new User();
user.setId(1L);
user.setUserName("柳岩");
user.setSex(0);
user.setPassword("3456");
user.setName("岩岩");
user.setAge(20);
userMapper.updateUser(user);
}
小结:修改数据的注解:@Update,作用等同于映射文件中的<update>。
2.4、查询
目标:使用注解查询所有的用户数据
步骤:
第一步:在接口中查询所有的用户数据的方法上面添加注解:@Select,然后设置其value属性值为具体的SQL查询语句;
第二步:测试
实现:
第一步:在UserMapper接口的queryAllUsers方法上添加注解:@Select,然后设置其value属性值为具体的查询SQL;
/*
* 4.查询所有用户数据
*/
@Select("SELECT * FROM tb_user")
List<User> queryAllUsers();
第二步:测试
@Test
public void queryAllUsers(){
List<User> list = userMapper.queryAllUsers();
System.out.println("list = " + list);
}
小结:
查询数据注解:@Select ,作用等同于映射文件中的<select>标签。
3、返回新增数据的id(自增主键回填)了解
问题:上面注解实现CRUD的测试中,数据新增成功,但是id值没有正常返回.

目标:使用注解完成数据新增,新增成功后返回数据的主键id值
步骤:
1、在新增数据注解 @Insert下面,添加@Options;
2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;
实现:
第一步:在新增数据注解 @Insert下面,添加@Options,设置useGeneratedKeys值为true,keyProperty为id,keyColumn 为id;
//1、新增数据 #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
@Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
@Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
void saveUser(User user);
第二步:测试:

小结:注解@Options:


4、注解实现别名映射
根据之前的学习,如果数据表的列名和pojo实体类的属性名不一致,会导致数据表的数据无法封装到实体类属性值中,对此我们又如下解决方案:
【1】查询的时候给列起别名,别名和实体类的属性名一致
select user_name as userName from tb_user where id =1;
【2】在mybatis的核心配置文件中按照如下配置:
<settings>
<!--开启驼峰自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
【3】在映射文件中,我们可以通过在ResultMap中,通过result标签中,给指定的column映射到实体类中指定的属性上。
<resultMap id="orderAndUserResultRelative" type="Order">
<result column="user_name" property="userName"/>
</resultMap>
而在注解中也有相应的解决方案:这里就必须使用注解:@Results
@Results注解相当于之前映射文件中的ResultMap,该注解如下:
public @interface Results {
Result[] value() default {};
}
我们发现value属于Result数组类型,而Result属于一个注解,注解的属性如下:
public @interface Result {
//对应数据表的列
String column() default "";
//对应pojo类的属性
String property() default "";
//javaType:返回的对象类型
Class<?> javaType() default void.class;
//one: 一对一配置
One one() default @One;
//many: 一对多配置
Many many() default @Many;
}
目标:使用注解的方式给取别名后的字段,映射到实体类中
步骤:
第一步:将之前核心配置文件中的开启驼峰自动映射设置为false
<settings>
<!--开启驼峰自动映射-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
第二步:在接口中查询的方法上面添加注解@Results;
第三步:测试;
实现:
第一步:将之前核心配置文件中的开启驼峰自动映射设置为false。这样才可以演示出@Results的效果。
<settings>
<!--开启驼峰自动映射-->
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
第二步:在接口中查询方法上面添加注解:@Results ,然后通过@Result完成字段的别名和实体类的属性名之间的映射配置;
说明:这里我们使用之前的查询所用用户方法即可,也可以在接口中在定义一个根据id查询用户的方法。
/*
根据id查询用户
*/
@Select("SELECT * FROM tb_user WHERE id = #{id}")
@Results(value={
@Result(column = "user_name",property = "userName")
})
User selectById(@Param("id") Long id);
第三步:测试
@Test
public void selectById(){
User user = userMapper.selectById(1L);
System.out.println("user = " + user);
}
【结果】

小结:给别名映射到实体类中可以通过添加注解:@Results 中的 @Result实现;
第三章 注解实现动态sql
1、 动态sql(理解)
【需求】:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户。
正常的sql语句:查询男性并且用户名中包含zhang

select * from tb_user where sex = "男" and user_name like '%zhang%'

select * from tb_user where sex = "男"
实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。
上述动态sql语句部分: and user_name like '%zhang%'
注解开发时,Mybatis给我们提供了常见的2种编写动态sql的方式:
方式1:@SelectProvider
使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用命名参数。
接口:
@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL")
List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);
动态sql生成类
package com.itheima.sh.utils;
import org.apache.ibatis.annotations.Param;
public class ProviderUtils {
public String queryUsersBySexOrUsernameSQL(@Param("sex") String sex, @Param("username") String username){
String sql = "select * from tb_user where sex = #{sex}";
if (username!=null && !"".equals(username)){
sql += " and user_name like concat('%',#{username},'%')";
}
return sql;
}
}
测试类:
@Test
public void queryUsersBySexOrUsername() {
List<User> list = mapper.queryUsersBySexOrUsername("男", "lisi");
System.out.println("list = " + list);
}
方式2:@SelectProvider和SQL类
接口:
@SelectProvider(type= ProviderUtils.class,method = "queryUsersBySexOrUsernameSQL2")
List<User> queryUsersBySexOrUsername(@Param("sex") String sex,@Param("username") String username);
动态sql生成类:
借助mybatis提供的一个对象:SQL。
创建SQL对象
SQL sql = new SQL();
链式编程,每个方法返回值都是SQL类对象
public class ProviderUtils {
public String queryUsersBySexOrUsernameSQL2(@Param("sex") String sex, @Param("username") String username) {
//借助mybatis提供的一个对象:SQL
//创建SQL对象
SQL sql = new SQL();
//链式编程,每个方法返回值都是SQL类对象
sql.SELECT("*").FROM("tb_user").WHERE("sex = #{sex}");
//判断用户是否为空,不为空就继续链式编程,即继续拼接
if(username != null && !"".equals(username)){
sql.WHERE("user_name like concat('%',#{username},'%')");
}
//SELECT * FROM user WHERE (sex=? AND user_name like concat('%',?,'%'))
//转换为字符串并返回
return sql.toString();
}
}
测试类:
@Test
public void queryUsersBySexOrUsername() {
List<User> list = mapper.queryUsersBySexOrUsername("男", "lisi");
System.out.println("list = " + list);
}

浙公网安备 33010602011771号