2024年9月23日---关于MyBatis框架(2)
4.7 不同返回值类型的查询
4.7.1 返回基本数据类型
/**查询student表中的记录个数 */ int selectCount();
<select id="selectCount" resultType="_int">select count(*) from student; </select>
4.7.2 返回引用类型(实体类)
/**返回值为实体类的*/ Student findById(Integer id);
<!-- 属性resultType:书写方法的返回值类型 代理对象通过sql语句进行查询,然后将查询到的结果自动封装成返回值类型的实例 --> <select id="findById" resultType="Student">select * from student where id = #{arg0}; </select>
4.7.3 返回List类型
List<Student> findByPage(@Param("offset") int offset, @Param("pageSize") int pageSize);
<!--返回值类型: 如果方法的返回值类型是List,那么在属性resultType上只需要指定集合的元素类型的名字即可--> <select id="findByPage" resultType="Student">select * from student limit #{offset},#{pageSize} </select>
4.7.4 返回Map类型
Map<String,Object> findByName(String name);
<!--应用场景: 就是将从数据库查询出来的数据,封装成Map对象字段名作为key字段值作为value --> <select id="findByName" resultType="map">select * from student where name = #{name} </select>
4.7.5 返回Map实例的集合
/**应用场景: 一条记录封装一个Map实例,多个Map实例封装到List集合中 * 条件: 通过性别,可以查询出来多条记录,封装到List集合中,泛型为Map */ List<Map<String,Object>> findByGender(@Param("gender")String gender);
<select id="findByGender" resultType="map">select * from student where gender = #{gender} </select>
4.7.6 返回Map的另一种情况
/*** 1001 => {address=江南, gender=m, name=刘备, age=40, sid=1001}* 1002 => {address=上海, gender=m, name=关羽, age=35, sid=1002}* 1003 => {address=长春, gender=m, name=赵云, age=27, sid=1004}*/ @MapKey("sid") Map<Integer,Object> findByGender2(@Param("gender")String gender);
<select id="findByGender2" resultType="map">select * from student where gender = #{gender} </select>
4.7.7 返回Set集合
/** 应用场景: 数据库里的记录可能有重复数据, 然后再进行查询时,想要进行去重操作,我们就可以设计方法的* 返回值为Set, 泛型为该表的实体类 注意: 实体类必须重写equals方法和hashCode方法。* */ Set<Student> findAllByDistinct();
<select id="findAllByDistinct" resultType="student">select * from student </select>
4.8 特殊SQL的查询
4.8.1 模糊查询
like '%${username}%' like concat('%',#{username},'%') like "%"#{username}"%"
4.8.2 批量删除
使用${},不能使用#{}
4.8.3 动态指定表名
4.8.4 主键返回
-
自增长类型主键
1)在接口StudentMapper里添加如下方法
int insertStudent(Student s);
2)在StudentMapper.xml里添加如下内容
<!-- int insertEmployee(Employee employee); -->
<!-- useGeneratedKeys属性字面意思就是“使用生成的主键” -->
<!-- keyProperty属性可以指定主键在实体类对象中对应的属性名,Mybatis会将拿到的主键值存入这个属性 -->
<insert id="insertStudent" useGeneratedKeys="true" keyProperty="id" parameterType="student">
insert into student
(name, age, gender, id_card, address)
values
(#{name},#{age},#{gender},#{idcard},#{address})
</insert>
3)测试
@Test public void test12() throws IOException {InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession = factory.openSession();StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Student student = new Student("张鲁一",40,"男","2222233333","北京");mapper.insertStudent(student);System.out.println(student.getId()); sqlSession.commit();sqlSession.close(); }
-
非自增长类型主键
use mybatis_db; drop table if exists computer; create table if not exists computer(id varchar(100) primary key,brand varchar(20),model varchar(20),price double ); insert into computer values (1, 'ThinkPad','X1',12000); insert into computer values (2, 'Mi','p1',6500); insert into computer values (3, 'lenove','s1',4000); commit; select * from computer;
而对于不支持自增型主键的数据库(例如 Oracle)或者字符串类型主键,则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用!
使用 selectKey 帮助插入UUID作为字符串类型主键示例:
<insert id="insertUser" parameterType="User">
<selectKey keyProperty="id" resultType="java.lang.String"
order="BEFORE">
SELECT UUID() as id
</selectKey>
INSERT INTO user (id, username, password)
VALUES (
#{id},
#{username},
#{password}
)
</insert>
在上例中,我们定义了一个 insertUser 的插入语句来将 User 对象插入到 user 表中。我们使用 selectKey 来查询 UUID 并设置到 id 字段中。
通过 keyProperty 属性来指定查询到的 UUID 赋值给对象中的 id 属性,而 resultType 属性指定了 UUID 的类型为 java.lang.String。
需要注意的是,我们将 selectKey 放在了插入语句的前面,这是因为 MySQL 在 insert 语句中只支持一个 select 子句,而 selectKey 中查询 UUID 的语句就是一个 select 子句,因此我们需要将其放在前面。
最后,在将 User 对象插入到 user 表中时,我们直接使用对象中的 id 属性来插入主键值。
使用这种方式,我们可以方便地插入 UUID 作为字符串类型主键。当然,还有其他插入方式可以使用,如使用Java代码生成UUID并在类中显式设置值等。需要根据具体应用场景和需求选择合适的插入方式。
4.9 级联查询
4.9.1 多对一查询
多对一,指的是表与表之间的记录关系,比如学生信息表(S_ID
,S_NAME
,……,T_ID
)与教师(班主任)信息表(T_ID
,T_NAME
,……)。多个学生是一个老师教的。通过学生信息表里的任意一条记录,都可以找到教师信息表里的对应的老师信息。
第一种写法:字段映射
第二种写法:association(关联,联合)
第三种写法:分步写法
//查询7369这个员工的员工信息和其所在部门信息 select * from emp e join dept d on e.deptno = d.deptno where empno = 7369; select * from emp where empno = 7379; select * from dept where deptno =
4.9.2 一对多查询
一对多,其实就是多对一的反向操作。教师信息表是主表,学生信息表是副表,通过教师信息表的任意一条记录,都可以在学生信息表里找到该教师的多个学生信息。
//查询10号部门的信息及其所有员工信息 select * from dept d left join emp e on d.deptno = e.deptno where d.deptno = 10;
第一种写法:collection
第二种写法:分步写法
第一步:查询accounting部门的信息select * from dept where dname = 10; 第二步:在员工表里查询,select * from emp where deptno = ?
4.9.3 多对多查询
一般多对多,都会涉及到第三张表。 学生信息表(每个学生的信息都是唯一的一条记录), 课程信息表(每个科目也都是唯一的一条记录),学生课程表(一个学生可以选择多个科目进行学习,一个科目可以被多个学生选择学习)。学生信息表和课程信息表通过学生课程表进行的对应关系,就是多对多的关系。
CREATE TABLE `course` (
`c_id` int(0) NOT NULL AUTO_INCREMENT COMMENT '课程ID',
`c_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '课程名称',
`t_id` int(0) NOT NULL COMMENT '授课教师ID',
`c_academy` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '所属学院',
`c_note` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '课程备注',
PRIMARY KEY (`c_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of course
-- ----------------------------
INSERT INTO `course` VALUES (1, '高数', 1, '信息学院', '高等数学,微积分');
INSERT INTO `course` VALUES (2, '英语', 2, '工程学院', '英语选修');
INSERT INTO `course` VALUES (3, 'JAVA', 3, '信息学院', '面向对象的编程语言');
INSERT INTO `course` VALUES (4, '食品安全', 1, '食品学院', '民以食为天');
INSERT INTO `course` VALUES (5, '土木建筑', 2, '工程学院', '桥梁,观景房');
INSERT INTO `course` VALUES (6, '体育', 2, '工程学院', '健身强体...');
CREATE TABLE `score` (
`s_id` int(0) NOT NULL COMMENT '学生ID',
`c_id` int(0) NOT NULL COMMENT '课程ID',
`score` int(0) NULL DEFAULT NULL COMMENT '课程分数',
PRIMARY KEY (`s_id`, `c_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of score
-- ----------------------------
INSERT INTO `score` VALUES (1001, 1, 80);
INSERT INTO `score` VALUES (1001, 2, 90);
INSERT INTO `score` VALUES (1001, 3, 99);
INSERT INTO `score` VALUES (1002, 1, 70);
INSERT INTO `score` VALUES (1002, 2, 60);
INSERT INTO `score` VALUES (1002, 3, 80);
INSERT INTO `score` VALUES (1003, 1, 80);
INSERT INTO `score` VALUES (1003, 2, 80);
INSERT INTO `score` VALUES (1003, 4, 80);
INSERT INTO `score` VALUES (1004, 3, 50);
INSERT INTO `score` VALUES (1004, 4, 30);
INSERT INTO `score` VALUES (1004, 5, 20);
INSERT INTO `score` VALUES (1005, 5, 76);
INSERT INTO `score` VALUES (1005, 6, 87);
INSERT INTO `score` VALUES (1006, 5, 31);
INSERT INTO `score` VALUES (1006, 6, 34);
INSERT INTO `score` VALUES (1007, 4, 89);
INSERT INTO `score` VALUES (1007, 6, 98);
需求: 查询每个学生的学号,姓名,年龄,所学科目名称,及其成绩
select s.id,s.name,s.age,c.c_name,sc.score from student s , course c, score sc where s.id = sc.s_id and c.c_id = sc.c_id select s.id,s.name,s.age,c.c_name,sc.score from student s left join score sc on s.id = sc.s_id left join course c on c.c_id = sc.c_id select s.id,s.name,s.age,c.c_name,sc.score from student s left join score sc left join course c on c.c_id = sc.c_id and s.id = sc.s_id
4.9.4 延迟加载
延迟加载,就是在使用数据时,进行查询操作,不使用时,不提前加载。可以节省内存,提高查询效率。
第一种方式: 局部配置(映射文件)
在 <association> 标记里 配置如下属性: fetchType="lazy" lazy: 延迟加载 eager: 不延迟加载 如下:<association property="dept" fetchType="eager" .....
第二种方法:全局配置(核心配置文件)
<settings><setting name="logImpl" value="STDOUT_LOGGING"/><!--全局的延迟加载开关, value设置为true,表示开启全局延迟加载, 不写value属性,默认就是false--><setting name="lazyLoadingEnabled" value="true"/> </settings>
4.10 分页查询
4.10.1 简介
在开发过程中,分页查询是一个常见的需求。为了简化分页查询的操作,我们可以使用 Mybatis 的分页插件,如 PageHelper
。
分页插件的概念
分页查询时,通常需要传入页数(page)*和*每页条数(pageSize)。返回的数据包括页面数据、总条数、总页数、当前页面、每页条数等。使用分页插件可以快速帮助我们获取这些数据。
分页插件的核心原理
分页查询的核心原理是通过 SQL 语句中的 LIMIT 关键字,根据传入的参数(当前页码、每页显示条数)来控制返回的当前页的内容范围。
4.10.2 步骤
1)添加依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.2.0</version> </dependency>
2)在MyBatis的核心配置文件( mybatis-config.xml)中配置插件
<plugins><!--设置分页插件--><plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> </plugins>
4.10.3 分页的应用
1)开启分页功能
在查询功能之前使用 PageHelper.startPage(int pageNum, int pageSize)
开启分页功能,传入当前页码和每页显示的条数:
-
pageNum
:当前页的页码 -
pageSize
:每页显示的条数
2)打印方法的返回值,查看
Page<Object> page = PageHelper.startPage(1, 3); System.out.println(page);
Page{ count=true, pageNum=2, pageSize=3, startRow=3, endRow=6, total=19, pages=7, reasonable=false, pageSizeZero=false}[Student{id=1002, name='关羽', gender='m', age=35, address='上海', scores=[Score{sid=1002, cid=1, score=70}, Score{sid=1002, cid=2, score=60}, Score{sid=1002, cid=3, score=80}]', courses=[Course{cid=1, cname='高数', tid=1, academy='信息学院', note='高等数学,微积分', students=null}, Course{cid=2, cname='英语', tid=2, academy='工程学院', note='英语选修', students=null}, Course{cid=3, cname='JAVA', tid=3, academy='信息学院', note='面向对象的编程语言', students=null}]}]
3)另外一个API:
PageInfo 这个类型封装的信息更多一些,包括了导航分页的信息。 用于在查询之后
new PageInfo(List list, int navegatePage); list: 分页查询的返回数据 navegatePage: 用来定义导航分页的页码显示数量
PageInfo{
pageNum=2,
pageSize=3,
size=1, startRow=4, endRow=4,
total=19,
pages=7,
prePage=1, nextPage=3,
isFirstPage=false,
isLastPage=false,
hasPreviousPage=true,
hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=5, navigatepageNums=[1, 2, 3, 4, 5]}
4.11 注解完成增删改查
使用注解开发会比配置文件开发更方便。
-
@Select 查询
-
@Insert 添加
-
@Update 修改
-
@Delete 删除
案例演示
1)Mapper接口里:
package com.sldl.mapper; import com.sldl.pojo.Student; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; public interface StudentMapper1 {@Select("select * from student ")List<Student> findAll(); @Select("select * from student where id = #{id}")Student findById(int id); @Insert("insert into student values (null,#{name},#{age},#{gender},#{idcard},#{address})")void addStudent(Student s); @Update("update student set name=#{name},age=#{age} where id=#{id}")void modStudent(Student s); @Delete("delete from student where id = #{id}")void delStudent(int id); }
2)测试:
package com.sldl.test;
import com.sldl.mapper.ComputerMapper;
import com.sldl.mapper.StudentMapper;
import com.sldl.mapper.StudentMapper1;
import com.sldl.pojo.Computer;
import com.sldl.pojo.Student;
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.After;
import org.junit.Before;
import org.junit.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;public class MyBatisTest3 {
SqlSession sqlSession = null;
@Before
public void testBefore() throws IOException {
String mybatis_config = "mybatis-config.xml";
InputStream stream = Resources.getResourceAsStream(mybatis_config);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
sqlSession = factory.openSession();
}
@After
public void testAfter(){
sqlSession.close();
}
@Test
public void test1(){
StudentMapper1 mapper = sqlSession.getMapper(StudentMapper1.class);
//测试查询方法
List<Student> all = mapper.findAll();
for (Student student : all) {
System.out.println(student);
}
}
@Test
public void test2(){
StudentMapper1 mapper = sqlSession.getMapper(StudentMapper1.class);
//测试查询方法
Student student = mapper.findById(3);
System.out.println(student);
}
@Test
public void test3(){
StudentMapper1 mapper = sqlSession.getMapper(StudentMapper1.class);Student s1 = new Student();
s1.setName("sunny");
s1.setAge(18);
s1.setGender("女");
s1.setIdcard("1234567890");
s1.setAddress("长春绿园");mapper.addStudent(s1);
sqlSession.commit();
}
@Test
public void test4(){
StudentMapper1 mapper = sqlSession.getMapper(StudentMapper1.class);
Student s1 = new Student();
s1.setId(19);
s1.setName("sun");
s1.setAge(28);
mapper.modStudent(s1);
sqlSession.commit();
}
@Test
public void test5(){
StudentMapper1 mapper = sqlSession.getMapper(StudentMapper1.class);
mapper.delStudent(19);
sqlSession.commit();
}
}