今日内容
0 复习昨日
一、分页插件
二、ORM映射【重点】
三、多表联查 【重点】
四、动态SQL 【重点】
五、$和#
零、复习昨日
mybatis orm框架,作用于持久层,高效开发,只关注sql,其他不用关心
思考MyBatis到底帮你省了哪些事情?
jdbc第四步sql自己编写之外,其他mybatis都做了…
接口文件和映射文件如何关联?
namespace
接口文件中方法又是如何和映射文件中的语句关联?
接口的方法名与映射文件标签的id一致
语句执行时入参都可以有哪些?有什么注意事项?
基本类型,String,Map,List,POJO(javabean/对象)
语句执行后返回的有哪些类型?(出参)
基本类型,字符串,对象
BUG:
1 idea中resuorces和test文件不识别
手动设置标记
2 编码格式
控制台错误提示:
MalformedByteSequenceException: 1 字节的 UTF-8 序列的字节 1 无效。
解决方案,在pom文件中加入配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
补充: mapper文件位置
mapper映射文件放置位置有两个
- resources(推荐)
- java
- 如果使用这种,还需再pom文件加入build设置,让idea加载java下的xml文件
myabtis-config.xml文件加载映射文件时也要两种方案
使用
<mapper resource="com/qf/mapper/UserMapper.xml"/>
但是这种写法,会随着项目模块的增多,这个地方也会随之配置增多使用
<package name="com.qf.mapper"/>
这种写法可以一次加载一个包下的所有映射文件,但是包结构要与接口文件包结构一致
总结,以后就按照以下写法配置:
- 映射文件全部放在resources
- resources下放映射文件包结构要与java放接口文件包结构一致
- 文件名要一致
一、分页插件
现在我们要学习使用一个常用的mybatis的插件 --> 分页插件-PageHelper
最早: findAll() ---> 查全部
后来要分页: findAll(pageNo,pageSize) ---> 改动SQL 加上 limit x,y
还行count(*)来计数
使用分页插件之后,只编写正常的查询SQL即可,关于分页的操作插件会自动完成.
引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.0</version>
</dependency>
全局配置文件使用插件
<!-- 插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
在查询时使用分页功能
public interface UserMapper {
List<User> findAll();
}
<select id="findAll" resultType="User">
select * from tb_user
</select>
@Test
public void findAll() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 【在执行查询前设置】
// 参数1: 当前页
// 参数2: 每页大小
PageHelper.startPage(2,2);
List<User> all = mapper.findAll( );
for (User user : all) {
System.out.println(user );
}
}
mybatis插件是对运行时某一点进行拦截
pagehelper插件是拦截运行时发出的SQL,自动在SQL后面拼接关键词
后续还可以获得更新消息的分页数据,比如共多少条数据?共多少页?当前页?下一页?目前是不是第一页?
@Test
public void findAll() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 【在执行查询前设置】
// 参数1: 当前页
// 参数2: 每页大小
PageHelper.startPage(2,2);
// 执行查询全部
List<User> userList = mapper.findAll( );
// 后续可以获得更详细的信息
PageInfo<User> pageInfo = new PageInfo<>(userList);
System.out.println(pageInfo );
// 获得总条数
System.out.println(pageInfo.getTotal() );
// 获得总页数
System.out.println(pageInfo.getPages() );
// 获得总数据(当前页中的总数据)
System.out.println(pageInfo.getList() );
}
ps: 可以看源码,中国人开发,注释非常好理解
二、ORM映射
2.1 MyBatis自动ORM失效
MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系,二者不同时,无法自动ORM。
自动ORM失效 |
---|
![]() |
2.2 方案一:列的别名
在SQL中使用 as 为查询字段添加列别名,以匹配属性名。
<mapper namespace="com.qf.mapper.UserMapper">
<select id="findUserById" resultType="User">
select id as idd,username,password,phone,create_time,sex,money from tb_user where id = #{id}
</select>
</mapper>
2.3 方案二:结果映射(ResultMap - 查询结果的封装规则)
通过< resultMap id=“” type=“” >映射,匹配列名与属性名。
<mapper namespace="com.qf.mapper.UserMapper">
<!--定义resultMap标签-->
<resultMap id="findUserByIdResultMap" type="user">
<!--关联主键与列名-->
<id property="idd" column="id" />
</resultMap>
<!--使用resultMap作为ORM映射依据-->
<select id="findUserById" resultMap="findUserByIdResultMap">
select id,username,password,phone,create_time,sex,money from tb_user where id = #{id}
</select>
</mapper>
三、 多表联查 【重点】
表关系: 一对一,一对多,多对多
多表联查的SQL
3.1 OneToOne
需求: 实现一对一查询,查询订单以及对应的用户信息
数据: tb_user表, tb_order表
关系:
用户 —> 订单 (1 VS N) 一个用户有多个订单
订单 —> 用户 (1 VS 1) 一个订单只会属于一个人
tb_user表
CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',
`username` varchar(10) DEFAULT NULL COMMENT '用户名',
`password` varchar(10) DEFAULT NULL COMMENT '密码',
`phone` varchar(11) DEFAULT NULL COMMENT '手机号',
`create_time` date DEFAULT NULL COMMENT '注册时间',
`money` double(10,2) DEFAULT NULL COMMENT '账户余额'
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;
tb_order表
CREATE TABLE `tb_order` (
`oid` int(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',
`order_time` datetime DEFAULT NULL COMMENT '订单时间',
`order_desc` varchar(255) DEFAULT NULL COMMENT '订单详情',
`uid` int(11) DEFAULT NULL COMMENT '关联用户id',
PRIMARY KEY (`oid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `tb_order` VALUES (1, '2022-11-17 15:06:29', '笔记本电脑', 1);
INSERT INTO `tb_order` VALUES (2, '2022-12-16 11:00:41', 'Cherry键盘', 1);
INSERT INTO `tb_order` VALUES (3, '2022-12-16 11:01:23', 'Logi鼠标', 2);
实体类
public class Order {
private int oid;
private Date orderTime;
private String orderDesc;
private int uid;
// set get...
}
但是上面的实体类,只有订单信息,我们要查询的是订单和用户! 上面的类就无法展现全部数据,所以需要扩展类
public class OrderVO extends Order {
private User user;
// set get
}
OrderMapper.java接口文件
public interface OrderMapper {
OrderVO findOrderWithUserById(int oid);
}
OrderMapper.xml映射文件
<resultMap id="orderWithUserResultMap" type="OrderVO">
<!-- 封装查询主体Order: -->
<id column="oid" property="oid"/>
<result column="order_time" property="orderTime"/>
<result column="order_desc" property="orderDesc"/>
<result column="uid" property="uid"/>
<!-- 一对一映射,需要封装关联的User对象 -->
<!-- 一对一映射,需要特殊标签 association-->
<!-- property="user" 是OrderVO类中的属性,javaType是user属性的类型 -->
<association property="user" javaType="com.qf.model.User">
<!-- 下面正常的列和属性 一一映射 -->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="phone" property="phone"/>
<result column="create_time" property="createTime"/>
<result column="money" property="money"/>
</association>
</resultMap>
<!-- 多表联查,直接返回resultType无法封装关联的那个对象,就使用使用resultMap手动映射 -->
<select id="findOrderWithUserById" resultMap="orderWithUserResultMap">
SELECT
o.*,
u.*
FROM
tb_order o,
tb_user u
WHERE
o.uid = u.id
AND o.oid = 1
</select>
测试
@Test
public void findOrderWithUserById() {
OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
OrderVO orderVO = mapper.findOrderWithUserById(1);
// 获得订单信息
int oid = orderVO.getOid( );
System.out.println("oid = " + oid);
Date orderTime = orderVO.getOrderTime( );
System.out.println("orderTime = " + orderTime);
String orderDesc = orderVO.getOrderDesc( );
System.out.println("orderDesc = " + orderDesc);
// 获得订单一家关联的用户信息
User user = orderVO.getUser( );
System.out.println(user );
}
3.2 OneToMore
需求: 一对多,查询用户关联查询出所有的订单
SELECT
*
FROM
tb_user u
LEFT JOIN tb_order o ON u.id = o.uid
WHERE
u.id = 3
目的查询用户,以及关联多个订单,User类不够展现全部数据,那么就创建扩展类UserVO,UserVO类继承User就可以存储用户信息,还需要再UserVO类中添加Order类来存储信息,但是!!不是一个Order类,因为是一对多,一个用户关联多个订单,所有要设置List<Order>
User扩展实体类
public class UserVO extends User{
private List<Order> orderList;
@Override
public String toString() {
String s = super.toString( );
return s +" \r\n UserVO{" +
"orderList=" + orderList +
'}';
}
public List<Order> getOrderList() {
return orderList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
}
UserMapper.java接口
public interface UserMapper {
UserVO findUserWithOrdersById(int id);
}
UserMapper.xml映射文件
<!-- 一对多 -->
<resultMap id="userWithOrdersResultMap" type="UserVO">
<!-- 封装User对象 -->
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="phone" property="phone"/>
<result column="create_time" property="createTime"/>
<result column="money" property="money"/>
<!-- 一对多关联映射使用collection标签 -->
<!-- property是UserVO类中关联的属性 -->
<!-- 不是javaType,是ofType,是指定集合中存储的数据类型 -->
<collection property="orderList" ofType="com.qf.model.Order">
<id column="oid" property="oid"/>
<result column="order_time" property="orderTime"/>
<result column="order_desc" property="orderDesc"/>
<result column="uid" property="uid"/>
</collection>
</resultMap>
<!-- 多表联查,另外的属性不会自动封装,需要使用resultMap -->
<select id="findUserWithOrdersById" resultMap="userWithOrdersResultMap">
SELECT
*
FROM
tb_user u
LEFT JOIN tb_order o ON u.id = o.uid
WHERE
u.id = #{id}
</select>
3.3 关联查询总结
正常封装使用resultMap
一对一封装使用association
一对多封装使用collection
四、动态SQL【重点】
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
自己话理解: 帮助我们拼接SQL
常见的动态SQL语法
- SQL片段(官方不是在动态SQL章节)
- where , if
- set
- trim
- foreach
4.1 SQL片段
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。
自己的话: 减少代码重复,主要用于抽取字段,表名等
<!-- 将重复的SQL代码抽取成SQL片段,以供复用 -->
<sql id="userField">
id,
username,
password,
phone,
create_time,
money,
sex
</sql>
<select id="findAll" resultType="User">
select
<!-- 引入片段 -->
<include refid="userField"/>
from
tb_user
</select>
4.2 if
if就是用来判断,主要用于判断要不要拼接对应的条件语句
-- 需求:查询用户,条件是money=1000,如果密码不为空,也根据密码查 select * from tb_user where money = 1000 select * from tb_user where money = 1000 and password= '123456'
UserMapper.java接口方法
public interface UserMapper {
/**
* 演示if动态sql
*/
List<User> findByMap(HashMap<String,Object> map);
}
UserMapper.xml
<select id="findByMap" resultMap="userResultMap">
select
<include refid="userField"/>
from
tb_user
where
money = #{money}
<if test="password != null and password != ''">
and password = #{password}
</if>
</select>
测试
/**
* if动态sql
*/
@Test
public void showIf() {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>( );
map.put("money",1000);
List<User> list = userMapper.findByMap(map);
for (User user : list) {
System.out.println(user );
}
}
4.3 where
如果说只有if,可能会出现这么一种情况
SELECT * FROM tb_user WHERE
多出一个where关键词!!
所以我们需要一个智能的,有条件时帮我们拼接where关键词,没有条件查询时,不拼接where
<!-- 测试if的缺点 -->
<select id="findUserBySex2" resultType="User">
select
<include refid="userField"/>
from
tb_user
<where>
<if test="sex != null">
sex = #{sex}
</if>
</where>
</select>
所以一般会where和if一起用
4.4 set
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
UserMapper.java接口方法
public interface UserMapper {
int updateUser(User user);
}
UserMapper.xml
<!-- set完成动态更新 -->
<update id="updateUser">
update tb_user
<!-- set标签自动拼接SET关键词 -->
<set>
<!-- 会自动过滤最后一个, -->
<!-- 特别注意,因为判断条件是!=null,基本不可能为null,所以将基本类型变为包装类 -->
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="money != null">money = #{money},</if>
<if test="sex != null">sex = #{sex},</if>
</set>
where id = #{id}
</update>
测试
@Test
public void testUpdate(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User( );
user.setId(1);
// 只更新这个2字段,其他字段不动
user.setUsername("QF");
user.setPassword("qf666");
int i = mapper.updateUser(user);
System.out.println(i > 0?"OK":"ERR" );
// 增删改要提交
sqlSession.commit();
}
4.5 foreach
场景: 批量删除
delete from tb_user where id in (1,2,3,...);
String sql = "delete from tb_user where id in ("; int iMax = idsArr.length - 1;// 最大下标 for (int i = 0; i < idsArr.length; i++) { int id = idsArr[i]; sql += id; if (i != iMax) { sql += ","; } else { sql += ")"; } }
UserMapper.java
public interface UserMapper {
// 为了演示动态sql foreach
int deleteBatch(List<Integer> ids);
}
UserMapper.xml
<!-- 动态sql foreach -->
<delete id="deleteBatch">
delete from tb_user
where id in
<!--
<foreach>开始循环,取出集合中的数据
collection,要遍历的集合,此处必须写list (或者可以写collection,arg0,不能写别的)
item , 遍历得到结果,命名任意,但是下面#{}内的名字要和这里一致
-->
<foreach collection="list" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
测试
/**
* 测试foreach
*/
@Test
public void testForeach(){
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ArrayList<Integer> list = new ArrayList<>( );
list.add(31);
list.add(32);
list.add(33);
int i = mapper.deleteBatch(list);
System.out.println("i = " + i);
System.out.println(i > 0?"OK":"ERR" );
// 增删改要提交
sqlSession.commit();
}
任务
使用项目中的表,重复1遍
合同加房屋实现多表联查
做笔记,写注释,画图标记
使用项目中的表,重复1遍
合同加房屋实现多表联查
做笔记,写注释,画图标记