前言:在建立对象关系映射时,如果实体类中的属性名和数据库表中的字段名一致,使用resultType属性可以将查询结果自动封装到实体类中。
那如果实体类中的属性名和数据库表中的字段名不一致,就需要使用restltMap实现将查询结果手动封装到实体类中。
package cn.xuguowen.mapper;
import cn.xuguowen.pojo.User;
import java.util.List;
public interface UserMapper {
List findAll();
}
(2)编写UserMapper.xml文件
(3)测试select * from user
@Test
public void testFindAll() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
sqlSession.close();
}
2.多条件查询(三种)
需求:根据id和username查询user表
(1)方式一:使用 #{arg0}-#{argn} 或者 #{param1}-#{paramn} 获取参数 ①:编写UserMapper接口
User findByIdAndUserName1(int id, String username);
②:UserMapper.xml
③:测试
@Test
public void testFindByIdAndUserName1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findByIdAndUserName1(1, "王莹");
System.out.println(user);
sqlSession.close();
}
(2)方式二:使用注解,引入 @Param() 注解获取参数 ①:编写UserMapper接口
User findByIdAndUserName2(@Param("id") int id, @Param("username") String username)
②:UserMapper.xml
③:测试
@Test
public void testFindByIdAndUserName2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.findByIdAndUserName1(1, "王莹");
System.out.println(user);
sqlSession.close();
}
(3)方式三:使用pojo对象传递参数 ①:编写UserMapper接口
User findByIdAndUserName3(User user);
②:UserMapper.xml
③:测试
@Test
public void testFindByIdAndUserName3() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user1 = new User();
user1.setId(1);
user1.setUsernameabc("王莹");
User user = mapper.findByIdAndUserName3(user1);
System.out.println(user);
sqlSession.close();
}
3.模糊查询(两种)
需求:根据username模糊查询user表
(1)方式一:#{} ①:编写UserMapper接口
List findByUserName1(String username);
②:UserMapper.xml
③:测试
@Test
public void testFindByUserName1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List users = mapper.findByUserName1("%王%");
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
(2)方式二:${}
①:编写UserMapper接口
List findByUserName2(String username);
②:UserMapper.xml
③:测试
@Test
public void testFindByUserName2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List users = mapper.findByUserName2("%王%");
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
#{}和${}区别
-
#{}:表示一个占位符
- 通过#{}可以实现preparedStatement向占位符中设置值,自动进行java类型和jdbc类型转换(实体类中属性的类型和表中字段类型的转换),还可以有效防止sql注入问题。
- #{}可以接收简单数据类型的值和pojo属性值
- 如果parameterType是基本数据类型或者String类型的,并且只有一个参数时,#{}括号中的名称随便写
-
${}:表示拼接sql串
- 通过${}可以将parameterType传入的内容拼接在sql语句上,不进行jdbc类型转换,会出现sql注入问题。
- ${}可以接收简单数据类型的值和pojo属性值
- 如果parameterType中的值是基本数据类型或者是String类型的,并且只有一个参数时,${}括号里面只能是value
二、MyBatis映射文件深入 1.返回主键
应用场景:我们很多时候有这种需求,向数据库插入一条记录后,希望能立即拿到这条记录在数据库中的主键值。
(1)方式一:useGeneratedKeys属性 ①:编写UserMapper接口
void saveUser1(User user);
②:UserMapper.xml
insert into user (username,birthday,sex,address)
values (#{usernameabc},#{birthdayabc},#{sexabc},#{addressabc})
③:测试
@Test
public void testSaveUser1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsernameabc("尼古拉斯·赵四");
user.setBirthdayabc(new Date());
user.setSexabc("男");
user.setAddressabc("象牙山庄");
System.out.println(user); // id null
mapper.saveUser1(user);
System.out.println(user); // id 7
// 记住手动提交事务
sqlSession.commit();
sqlSession.close();
注意 只适用于主键自增的数据库,mysql和sqlserver支持,oracle不行。
(2)方式二:selectKey标签 ①:编写UserMapper接口
void saveUser2(User user);
②:UserMapper.xml
select last_insert_ID()
insert into user (username,birthday,sex,address)
values (#{usernameabc},#{birthdayabc},#{sexabc},#{addressabc})
③:测试
@Test
public void testSaveUser2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsernameabc("詹姆斯·马双");
user.setBirthdayabc(new Date());
user.setSexabc("男");
user.setAddressabc("向阳山庄");
System.out.println(user); // id null
mapper.saveUser2(user);
System.out.println(user); // id 8
// 记住手动提交事务
sqlSession.commit();
sqlSession.close();
}
2.动态SQL
应用场景:当我们要根据不同的条件,来执行不同的sql语句的时候,需要用到动态sql。
(1)动态SQL之 if需求:根据id和username查询,但是不确定两个都有值
List findByIdAndUsername(User user);
②:UserMapper.xml
③:测试
@Test
public void testFindByIdUsername1() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setUsernameabc("王莹");
List users = mapper.findByIdAndUsername(user);
for (User user1 : users) {
System.out.println(user1);
}
}
(2)动态SQL之 set
需求:动态更行user表中的数据,如果该属性有值就更新,没有值就不处理。
①:编写UserMapper接口
void updateSet(User user);
②:UserMapper.xml
③:测试update user where id = #{id} username = #{usernameabc}, birthday = #{birthdayabc}, sex = #{sexabc}, address = #{addressabc},
@Test
public void testUpdateSet() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(5);
user.setUsernameabc("刘诗诗");
user.setSexabc("女");
mapper.updateSet(user);
sqlSession.commit();
sqlSession.close();
}
(3)动态SQL之 foreach
foreach主要是用来做数据的循环遍历
例如:select * from user where id in (1,2,3)这样的sql语句中,传入的参数部分必须依靠foreach遍历才能实现。
传入的参数是集合 ①:编写UserMapper接口标签用于遍历集合,它的属性: • collection:代表要遍历的集合 • open:代表语句的开始部分 • close:代表语句的结束部分 • item:代表遍历集合的每个元素,生成的变量名 • sperator:代表分隔符
List findByList(List ids);
②:UserMapper.xml
select * from user
#{id}
③:测试
@Test
public void testFindByList() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
ArrayList ids = new ArrayList<>();
ids.add(1);
ids.add(5);
ids.add(7);
List users = mapper.findByList(ids);
for (User user : users) {
System.out.println(user);
}
}
传入的参数是数组 ①:编写UserMapper接口
List findByArray(Integer[] ids);
②:UserMapper.xml
③:测试
@Test
public void testFindByArray() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer[] ids = new Integer[]{1,2,5};
List users = mapper.findByArray(ids);
for (User user : users) {
System.out.println(user);
}
}
3.SQL片段
应用场景:在映射文件中,可以将重复的sql语句提取出来,使用时使用include标签引用即可,最终达到sql重用的目得。
select * from user
#{id}
三、MyBatis核心配置文件深入 1.plugins标签
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。
开发步骤 ①:导入通用PageHelper依赖坐标
com.github.pagehelper
pagehelper
3.7.5
com.github.jsqlparser
jsqlparser
0.9.1
②:在mybatis核心配置文件中配置PageHelper插件
③:测试分页代码实现
@Test
public void testFindAllPageHelper() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 在调用查询之前,一定要进行分页参数的设置
// 参数一pageNum:表示当前是第几页
// 参数二pageSize:表示每页显示的条数
PageHelper.startPage(1, 2);
List all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
// 获取其他的分页信息
PageInfo pageInfo = new PageInfo<>(all); // 泛型和传入list集合的泛型一致
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("每页显示的长度:" + pageInfo.getPageSize());
System.out.println("是否是第一页:" + pageInfo.isIsFirstPage());
System.out.println("是否是最后一页:" + pageInfo.isIsLastPage());
}
四、MyBatis多表查询 1.数据库表关系介绍
关系型数据库表关系分为
- 一对一 人和身份证号就是一对一
- 一对多 用户和订单就是一对多(一个用户可以下多个订单),订单和用户就是多对一(多个订单属于同一个用户)
- 多对多 学生和课程就是多对多
- 特例:从数据层面看,一个订单只从属于一个用户,所以MyBatis将多对一看成了一对一
案例环境准备:
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`ordertime` VARCHAR(255) DEFAULT NULL,
`total` DOUBLE DEFAULT NULL,
`uid` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `uid` (`uid`),
CONSTRAINT `orders_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of orders
-- ----------------------------
INSERT INTO `orders` VALUES ('1', '2020-12-12', '3000', '1');
INSERT INTO `orders` VALUES ('2', '2020-12-12', '4000', '1');
INSERT INTO `orders` VALUES ('3', '2020-12-12', '5000', '2');
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`rolename` VARCHAR(255) DEFAULT NULL,
`roleDesc` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', 'CTO', 'CTO');
INSERT INTO `sys_role` VALUES ('2', 'CEO', 'CEO');
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`userid` INT(11) NOT NULL,
`roleid` INT(11) NOT NULL,
PRIMARY KEY (`userid`,`roleid`),
KEY `roleid` (`roleid`),
CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `sys_role`
(`id`),
CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `user`
(`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');
INSERT INTO `sys_user_role` VALUES ('2', '1');
INSERT INTO `sys_user_role` VALUES ('1', '2');
INSERT INTO `sys_user_role` VALUES ('2', '2');
2.一对一(多对一)
①:介绍
一对一查询模型 用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求 查询所有订单,与此同时查询出每个订单所属的用户
一对一查询语句
SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id②:代码实现 a):Orders实体和User实体
public class Orders {
private Integer id;
private String ordertime;
private double total; // 订单价格
private Integer uid; // 关联user表
// 因为mybatis中把多对一的关系看做是一对一的关系
private User user; // 表示当前订单属于哪个用户
}
public class User {
private Integer id;
private String username;
private Date birthday;
private Character sex;
private String address;
}
b):OrdersMapper接口
public interface OrdersMapper {
List findAllWithUser();
}
c):OrdersMapper.xml
d):测试SELECT * FROM orders o LEFT JOIN USER u ON o.uid = u.id
@Test
public void testFindAllWithUser() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List orders = mapper.findAllWithUser();
for (Orders order : orders) {
System.out.println(order);
}
sqlSession.close();
}
2.一对多 ①:介绍
一对多查询模型 用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求 查询所有用户,与此同时查询出每个用户具有的订单
一对一查询语句
-- 查询所有用户,与此同时查询出该用户具有的订单 SELECT u.*,o.id oid,o.ordertime,o.total,o.uid FROM USER u LEFT JOIN orders o ON u.id = o.uid;②:代码实现 a):Orders实体和User实体
public class User {
private Integer id;
private String username;
private Date birthday;
private Character sex;
private String address;
// 表示多的关系:当前用户具备的订单列表
private List ordersList;
}
public class Orders {
private Integer id;
private String ordertime;
private double total; // 订单价格
private Integer uid; // 关联user表
// 因为mybatis中把多对一的关系看做是一对一的关系
private User user; // 表示当前订单属于哪个用户
}
b):UserMapper接口
public interface UserMapper {
List findAllWithOrders();
}
c):UserMapper.xml
d):测试SELECt u.*,o.id oid,o.ordertime,o.total,o.uid FROM USER u LEFT JOIN orders o ON u.id = o.uid;
@Test
public void testFindAllWithOrders() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List allWithOrders = mapper.findAllWithOrders();
for (User users : allWithOrders) {
System.out.println(users);
}
sqlSession.close();
}
3.多对多 ①:介绍
多对多查询模型 用户表和订单表的关系为:一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求 查询所有用户,与此同时查询出每个用户的所有角色
多对一查询语句
-- 查询所有用户,同时查询出该用户的所有角色 SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u LEFT JOIN sys_user_role ur ON u.id = ur.userid LEFT JOIN sys_role r ON ur.roleid = r.id②:代码实现 a):User实体和Role实体
public class User {
private Integer id;
private String username;
private Date birthday;
private Character sex;
private String address;
// 表示多的关系:当前用户具备的订单列表
private List ordersList;
// 表示多的关系:当前用户具备的角色列表
private List roleList;
}
public class Role {
private Integer id;
private String rolename;
private String roleDesc;
}
b):UserMapper接口
List findAllWithRole();
c):UserMapper.xml
SELECT u.*,r.id rid,r.rolename,r.roleDesc FROM USER u
LEFT JOIN sys_user_role ur ON u.id = ur.userid
LEFT JOIN sys_role r ON ur.roleid = r.id
d):测试
@Test
public void testFindAllWithRole() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List allWithRole = mapper.findAllWithRole();
for (User user : allWithRole) {
System.out.println(user);
}
sqlSession.close();
}
4.总结
* 多对一(一对一)配置:使用+做配置 * 一对多配置:使用 + 做配置 * 多对多配置:使用 + 做配置 * 多对多的配置跟一对多很相似,难度在于SQL语句的编写。
五、MyBatis嵌套查询 1.什么是嵌套查询
嵌套查询就是将原来多表查询中的联合查询语句拆成单个表的查询,再使mybatis的语法嵌套在一起。
例如:查询一个订单,与此同时查询出该订单所属的用户信息
需求:查询一个订单,与此同时查询出该订单所属的用户信息
一对一查询语句
-- 1.先查询订单信息
SELECT * FROM orders;
-- 2.再根据订单表中的uid查询用户信息
SELECt * FROM USER WHERe id = #{uid}
②:代码实现
a):OrdersMapper接口
List findAllWithUser2();
b):OrdersMapper.xml
c):在编写OrdersMapper.xml时需要依赖UserMapper接口和UserMapper.xml文件,因为第二条sql语句单独操作user表
UserMapper接口
User findById(Integer id);
UserMapper.xml
d):测试
@Test
public void testFindAllWithUser2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
List allWithUser2 = mapper.findAllWithUser2();
for (Orders orders : allWithUser2) {
System.out.println(orders);
}
sqlSession.close();
}
3.一对多嵌套查询
①:介绍
需求:查询所有用户,与此同时查询出该用户具有的订单
一对多查询语句
-- 1.先查询出所用用户
SELECT * FROM USER;
-- 2.根据id查询出每个用户所具有的订单信息
SELECt * FROM orders WHERe uid = #{id}
②:代码实现
a):UserMapper接口
List findAllWithOrders2();
b):UserMapper.xml
c):在编写UserMapper.xml时需要依赖OrdersMapper接口和OrdersMapper.xml文件,因为第二条sql语句单独操作orders表
OrdersMapper接口
List findById(Integer id);
OrdersMapper.xml
d):测试
@Test
public void testFindAllWithOrders2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List allWithOrders2 = mapper.findAllWithOrders2();
for (User user : allWithOrders2) {
System.out.println(user);
}
sqlSession.close();
}
4.多对多嵌套查询 ①:介绍
需求:查询所有用户,同时查询出该用户的所有角色
多对多查询语句
-- 1.先查询出所用用户
SELECT * FROM USER;
-- 2.根据id查询出每个用户所具有的订单信息
SELECt * FROM orders WHERe uid = #{id}
②:代码实现
a):UserMapper接口
List findAllWithRole2();
b):UserMapper.xml
c):在编写UserMapper.xml时需要依赖RoleMapper接口和RoleMapper.xml文件
List findByUid(Integer uid);
d):测试
@Test
public void testFindAllWithRole2() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List allWithRole2 = mapper.findAllWithRole2();
for (User user : allWithRole2) {
System.out.println(user);
}
sqlSession.close();
}
③:总结
一对一配置:使用+做配置,通过column条件,执行select查询 一对多配置:使用 + 做配置,通过column条件,执行select查询 多对多配置:使用 + 做配置,通过column条件,执行select查询 优点:简化多表查询操作 缺点:执行多次sql语句,浪费数据库性能



