笔记输出来源:拉勾教育Java就业急训营
如有侵权,私信立删
修改时间:2020年2月18日
作者:pp_x
邮箱:[email protected]
文章目录
Mybatis高级查询
ResultMap
- resultType:如果实体的属性名与表中字段名一致,将查询结果自动封装到实体类中
- resultMap:如果实体的属性名与表中字段名不一致,可以使用ResutlMap实现手动封装到实体类中
- 属性:
- id:唯一标识,调用的时候填写
- type:封装后的实体类型
<!-- id: 标签的唯一标识
type:封装后的实体类型
-->
<resultMap id="userResultMap" type="com.lagou.domain.User">
<!-- 手动配置映射关系-->
<!-- id:用来配置主键-->
<id property="id" column="id"></id>
<!-- result:用来配置普通字段-->
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</resultMap>
多条件查询
需求:根据id和username查询user表
方式一
- 使用
#{arg0}-#{argn}
或者#{param1}-#{paramn}
获取参数
<!-- 多条件查询方式一-->
<select id="findByIdAndUsername" resultMap="userResultMap">
<!-- select * from user where id = #{arg0} and username = #{arg1} -->
select * from user where id = #{param1} and username = #{param2}
</select>
方式二
- 使用注解,引入
@Param()
注解获取参数 - 接口类
/**
* 多条件查询方式二
* @param id
* @param username
* @return
*/
public List<User> findByIdAndUsername2(@Param("id") int id, @Param("username") String username);
- 配置文件
<!-- 多条件查询方式二 -->
<select id="findByIdAndUsername2" resultMap="userResultMap">
<!-- 对应接口中@param注解中的值-->
select * from user where id = #{id} and username = #{username}
</select>
方式三
- 使用pojo对象传递参数
- 此方式
#{}
中的变量必须对应属性名 - 接口类
/**
* 多条件查询方式三
* @return
*/
public List<User> findByIdAndUsername3(User user);
- 配置文件
<!-- 多条件查询方式三 -->
<select id="findByIdAndUsername3" resultMap="userResultMap" parameterType="user">
select * from user where id = #{id} and username = #{username}
</select>
模糊查询
需求:根据username模糊查询user表
方式一
- xml配置文件
<!-- 模糊查询方式一-->
<select id="findByUsername1" resultMap="userResultMap" parameterType="String">
<!--
parameterType是基本数据类型或者String的时候,#{}里面的值可以随便写
#{}在mybatis中是占位符,引用参数值时会自动添加单引号
-->
select * from user where username like #{username}
</select>
- 测试类
/**
* 模糊查询方式一
*/
@Test
public void test5() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理 实际类型 proxy
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findByUsername1("%liuxiao%");
for (User user:users
) {
System.out.println(user);
}
}
方式二
- xml配置
<!-- 模糊查询方式二-->
<select id="findByUsername2" resultMap="userResultMap" parameterType="String">
<!--
parameterType是基本数据类型或者String的时候,${}里面的值只能写value
${}时sql原样拼接,不会自动添加单引号
-->
select * from user where username like '${value}'
</select>
- 测试
/**
* 模糊查询方式二
*/
@Test
public void test6() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理 实际类型 proxy
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findByUsername2("%liuxiao%");
for (User user:users
) {
System.out.println(user);
}
}
Mybatis映射文件
返回主键
- 应用场景
向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。
useGeneratedKeys
useGeneratedKeys
:声明返回主键keyProperty
:把返回主键的值封装到实体中的哪个属性上- 只能在支持主键自增的数据库中使用Oracle不行
<!-- 添加用户以及获取返回主键方式一-->
<!--
useGeneratedKeys:声明返回主键
keyProperty:把返回主键的值封装到实体中的哪个属性上
只能在支持主键自增的数据库中使用 Oracle不行
-->
<insert id="saveUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
selectKey
selectKey
:适用范围更广,支持所有类型的数据库order="AFTER"
:在sql语句执行后 执行此语句keyColumn
:指定主键对应的列名keyProperty
:指定将主键封装到实体的哪个属性resultType
:指定返回的主键类型
<!-- 添加用户以及获取返回主键方式二-->
<!--
selectKey:适用范围更广,支持所有类型的数据库
order="AFTER" 在sql语句执行后 执行此语句
keyColumn:指定主键对应的列名
keyProperty:指定将主键封装到实体的哪个属性
resultType:指定返回的主键类型
-->
<insert id="saveUser2" parameterType="user">
<selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
select last_insert_id()
</selectKey>
insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})
</insert>
动态sql
- 应用场景
当我们要根据不同的条件,来执行不同的sql语句的时候,需要用到动态sql。
if 标签
- test里写表达式
- where标签可以在合适的时候删除和添加and连接词
<!-- 动态sql之if:多条件查询-->
<select id="findByIdAndUsernameIf" parameterType="user" resultType="user">
select * from user
<!--
test里写表达式
where标签相当于 where 1=1
-->
<where>
<if test="id!=null">
and id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
</where>
</select>
set标签
<set>
:在更新的时候自动添加set关键字 还会去掉最后一个条件的逗号
<!-- 动态sql之set:动态跟新-->
<update id="updateIf" parameterType="user">
update user
<!--
<set>:在更新的时候自动添加set关键字 还会去掉最后一个条件的逗号
-->
<set>
<if test="username!=null">
username=#{username},
</if>
<if test="birthday!=null">
birthday=#{birthday},
</if>
<if test="sex!=null">
sex=#{sex},
</if>
<if test="address!=null">
address=#{address},
</if>
</set>
where id=#{id}
</update>
foreach标签
collection
:代表要遍历的集合元素 通常写collection或者listopen
: 代表语句的开始部分close
:代表语句的结束部分item
:代表遍历集合中的每个元素生成的变量名 和#{}
中的值保持一致
<!-- 动态sql之foreach:多值查询-->
<select id="findByList" parameterType="list" resultType="user">
<include refid="selectUser"></include>
<where>
<!-- foreach的属性:
collection:代表要遍历的集合元素 通常写collection或者list
open:代表语句的开始部分
close:代表语句的结束部分
item:代表遍历集合中的每个元素生成的变量名 和#{}中的值保持一致
separator:分隔符
-->
<foreach collection="collection" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
<!-- 动态sql之foreach:多值查询 数组-->
<select id="findByArray" parameterType="int" resultType="user">
<include refid="selectUser"></include>
<where>
<foreach collection="array" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
sql片段
<!-- 重复sql片段抽取-->
<sql id="selectUser">
select * from user
</sql>
<include refid="selectUser"></include>
Mybatis核心配置文件
plugins标签
以分页助手插件为例
- 导入插件入通用PageHelper坐标
- 在mybatis核心配置文件中配置PageHelper插件
<!-- 分页助手插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- dialect:指定方言-->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
- 分页相关的参数
/**
* 核心配置文件 plugin标签:pageHelper
*
*/
@Test
public void test13() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
//当前返回的是基于UserMapper所产生的代理对象 底层:JDK动态代理 实际类型 proxy
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//设置分页参数 必须写在调用mapper方法的上面
//参数一:当前页
//参数二:每页显示的条数
PageHelper.startPage(1,2);
List<User> list = mapper.findAllResultMap();
for (User user:list
) {
System.out.println(user);
}
//获取分页的其他参数
PageInfo<User> userPageInfo = new PageInfo<User>(list);
System.out.println("总条数:"+userPageInfo.getTotal());
System.out.println("总页数:"+userPageInfo.getPages());
System.out.println("是否是第一页:"+userPageInfo.isIsFirstPage());
sqlSession.close();
}
}
Mybatis多表查询
一对一
- 查询语句
SELECT * FROM orders o LEFT JOIN USER u ON o.uid=u.id;
代码实现
- 实体类:需要增加一个用户对象字段
public class Orders {
private Integer id;
private String ordertime;
private Double total;
private Integer uid;
// 表示当前订单属于那个用户 association
private User user;
@Override
public String toString() {
return "Orders{" +
"id=" + id +
", ordertime='" + ordertime + '\'' +
", total=" + total +
", uid=" + uid +
", user=" + user +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getOrdertime() {
return ordertime;
}
public void setOrdertime(String ordertime) {
this.ordertime = ordertime;
}
public Double getTotal() {
return total;
}
public void setTotal(Double total) {
this.total = total;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
}
- xml配置
association
: 在进行一对一关联查询配置时,使用association标签进行关联property="user"
:要封装实体的属性名javaType="com.lagou.domain.User"
:要封装的实体的属性类型
<!--一对一关联查询:查询所有订单,与此同时还要查询出每个订单所属的用户信息-->
<resultMap id="orderMap" type="com.lagou.domain.Orders">
<id property="id" column="id"/>
<result property="ordertime" column="ordertime"/>
<result property="total" column="total"/>
<result property="uid" column="uid"/>
<!--
association : 在进行一对一关联查询配置时,使用association标签进行关联
property="user" :要封装实体的属性名
javaType="com.lagou.domain.User" 要封装的实体的属性类型
-->
<association property="user" javaType="com.lagou.domain.User">
<id property="id" column="uid"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
<select id="findAllWithUser" resultMap="orderMap">
SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id
</select>
一对多
- 查询语句:要起别名,因为oid对应的uid有空值
SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id
代码实现
- 实体类需要增加一个List属性对应订单列表
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 代表当前用户具备的订单列表
private List<Order> orderList;
}
- xml映射文件
<!--一对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->
<resultMap id="userMap" type="com.lagou.domain.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--
collection : 一对多使用collection标签进行关联
-->
<collection property="ordersList" ofType="com.lagou.domain.Orders">
<id property="id" column="oid"></id>
<result property="ordertime" column="ordertime"/>
<result property="total" column="total"/>
<result property="uid" column="uid"/>
</collection>
</resultMap>
<select id="findAllWithOrder" resultMap="userMap">
SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM orders o RIGHT JOIN USER u ON o.uid = u.id
</select>
多对多
- 多对多即两个一对多,除了sql查询语句有区别,xml映射即使两个角度的一对多
- 查询语句
SELECT *
FROM
USER u -- 用户表
LEFT JOIN user_role ur -- 左外连接中间表
ON u.`id` = ur.`uid`
LEFT JOIN role r -- 左外连接角色表
ON ur.`rid` = r.`id` ;
代码实现
- 实体类:增加一个List字段存储对应的角色列表
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// 代表当前用户关联的角色列表
private List<Role> roleList;
}
- xml 映射
<!--多对多关联查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->
<resultMap id="userRoleMap" type="user">
<id property="id" column="id"/>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roleList" ofType="role">
<id column="rid" property="id"></id>
<result column="rolename" property="rolename"></result>
<result column="roleDesc" property="roleDesc"></result>
</collection>
</resultMap>
<select id="findAllWithRole" resultMap="userRoleMap">
SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON ur.userid = u.id
LEFT JOIN sys_role r ON ur.roleid = r.id
</select>
小结
- 多对一(一对一)配置:使用
<resultMap>
+<association>
做配置 - 一对多配置:使用
<resultMap>
+<collection>
做配置 - 多对多配置:使用
<resultMap>
+<collection>
做配置
Mybatis嵌套查询
一对一
- 查询语句
-- 先查询订单
SELECT * FROM orders;
-- 再根据订单uid外键,查询用户
SELECT * FROM `user` WHERE id = #{订单的uid};
代码实现
association
标签中的select属性填命名空间.statment
- order映射
<!--一对一嵌套查询-->
<resultMap id="orderMap2" type="com.lagou.domain.Orders">
<id property="id" column="id"/>
<result property="ordertime" column="ordertime"/>
<result property="total" column="total"/>
<result property="uid" column="uid"/>
<!--问题:1.怎么去执行第二条sql , 2.如何执行第二条sql的时候,把uid作为参数进行传递-->
<association property="user" javaType="com.lagou.domain.User"
select="com.lagou.mapper.UserMapper.findById" column="uid"/>
</resultMap>
<select id="findAllWithUser2" resultMap="orderMap2">
SELECT * FROM orders
</select>
- user映射
<!--根据id查询用户
useCache="true" 代表当前这个statement是使用二级缓存
-->
<select id="findById" resultType="com.lagou.domain.User" parameterType="int" useCache="true">
SELECT * FROM user WHERE id = #{id}
</select>
一对多
- 查询语句
-- 先查询用户
SELECT * FROM `user`;
-- 再根据用户id主键,查询订单列表
SELECT * FROM orders where uid = #{用户id};
代码实现
- user映射
<!--一对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的订单信息-->
<resultMap id="userOrderMap" type="com.lagou.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<!--fetchType="lazy" : 延迟加载策略
fetchType="eager": 立即加载策略
-->
<collection property="ordersList" ofType="com.lagou.domain.Orders" column="id"
select="com.lagou.mapper.OrderMapper.findByUid" fetchType="lazy" ></collection>
</resultMap>
<select id="findAllWithOrder2" resultMap="userOrderMap">
SELECT * FROM USER
</select>
- order映射
<select id="findByUid" parameterType="int" resultType="com.lagou.domain.Orders">
SELECT * FROM orders WHERE uid = #{uid}
</select>
多对多
- 查询语句
-- 先查询用户 SELECT * FROM `user`;
-- 再根据用户id主键,查询角色列表
SELECT * FROM role r
INNER JOIN user_role ur ON r.`id` = ur.`rid`
WHERE ur.`uid` = #{用户id};
代码实现
- user映射
<resultMap id="userRoleMap2" type="com.lagou.domain.User">
<id property="id" column="id"/>
<result property="username" column="username"></result>
<result property="birthday" column="birthday"></result>
<result property="sex" column="sex"></result>
<result property="address" column="address"></result>
<collection property="roleList" ofType="com.lagou.domain.Role" column="id" select="com.lagou.mapper.RoleMapper.findByUid"></collection>
</resultMap>
<!--多对多嵌套查询:查询所有的用户,同时还要查询出每个用户所关联的角色信息-->
<select id="findAllWithRole2" resultMap="userRoleMap2">
SELECT * FROM USER
</select>
- role映射文件
<select id="findByUid" resultType="com.lagou.domain.Role" parameterType="int">
SELECT * FROM sys_role r INNER JOIN sys_user_role ur ON ur.roleid = r.id
WHERE ur.userid = #{uid}
</select>
小结
- 一对一配置:使用
<resultMap>
+<association>
做配置,通过column条件,执行select查询 - 一对多配置:使用
<resultMap>
+<collection>
做配置,通过column条件,执行select查询 - 多对多配置:使用
<resultMap>
+<collection>
做配置,通过column条件,执行select查询 - 优点:简化多表查询操作
- 缺点:执行多次sql语句,浪费数据库性能