Mybatis(进阶部分)

四 Mybatis完成CURD(二)

4.5 多条件CRUD

        之前的案例中,接口里方法的形参个数都是1个;如果方法形参是两个或者两个以上时,MyBatis又该如何获取获取参数呢?

Mybatis提供了好几种方式,可以获取多个参数的值

第一种: 使用arg0,arg1…或者param1,param2…来按照参数顺序获取对应的值

 /*通过部门编号,领导编号,员工姓名,查询员工信息*/Employee findByDeptnoAndMgrAndEname(int deptno, int mgr, String ename);

        接口里的方法与Sql映射文件中的语句进行映射后,并且在调用方法期间,Mybatis会默认将所有传入到方法中的实际参数封装到一个Map对象中,实际参数作为value,按照从左到右的顺序,分别绑定到key名为arg0,arg1…或者param1,param2…上。

因此我们在获取参数时,可以这样写

    <!--Employee findByDeptnoAndMgrAndEname(int deptno, int mgr, String ename);-->
<!--    方法的参数有多个时,可以不用子标签里书写parameterType属性-->
<!--    没有指定参数类型时,会默认从自动封装的Map对象中,寻找具体的值给?赋上使用arg0,arg1,arg2...或者使用param1,param2,param3...
--><select id="findByDeptnoAndMgrAndEname" resultType="employee" resultMap="resultMap1" >
<!--        select * from emp where deptno = #{arg0} and mgr =#{param2} and ename=#{arg2}-->
<!--        select * from emp where deptno = #{arg0} and mgr =#{arg1} and ename=#{arg2}-->select * from emp where deptno = #{param1} and mgr =#{param2} and ename=#{param3}</select>

测试:

@Testpublic void test1(){//获取代理对象EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);/*当对应的Sql映射文件中,没有指定参数类型时,Mybatis会将实际参数封装到一个Map对象中arg0,arg1...作为key,实际参数从左到右依次作为value与key对应。还会再存储一组键值对:param1,param2....作为key实际参数从左到右依次作为value与key对应。*/Employee scott = mapper.findByDeptnoAndMgrAndEname(30, 7698, "TURNER");System.out.println(scott);}

第二种:Map作为参数

map 集合:只需要保证 SQL 中的参数名和 map 集合的键的名称对应上,即可设置成功

/*如果在设计方法时,需要使用多个形参。为了将值传入到Mybatis的映射文件中的sql语句中,我们可以换一个思路:就是在设计方法时,形参设计成Map类型,在调用时,将各种实际参数封装到Map对象中,然后只需要传入Map参数即可注意,映射文件中,要使用parameterType:来指定形参为Map类型,map/Map*/Employee findByMap(Map<String,Object> map);

映射文件:

<!--    Employee findByMap(Map<String,Object> map);-->
<!--    如果形参是Map类型,占位符中的名称必须是map的key,所以在封装成map对象时,尽量见名知意--><select id="findByMap" resultType="Employee" resultMap="resultMap1" parameterType="map">select * from emp where deptno = #{deptno} and mgr = #{mgr} and empno = #{empno} and job = #{job}</select>

测试:

 @Testpublic void test2(){//获取代理对象EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);// 自定义一个map类型,用于封装浏览器传过来的各种值。Map<String,Object> map = new HashMap<>();map.put("deptno",30);map.put("mgr",7698);map.put("empno",7499);map.put("job","SALESMAN");//map在存储键值对时,key必须和占位符中的名字一样。因为占位符是通过key来从map中获取具体的value值,给?赋值。Employee byMap = mapper.findByMap(map);System.out.println(byMap);}

第三种:实体类作为参数

实体类封装参数:只需要保证 SQL 中的参数名和实体类属性名对应上,即可设置成功

实体类:

package com.mybatis.pojo;public class ParamsType {private Integer empno;private String ename;private String job;private Integer mgr;public ParamsType() {}public ParamsType(Integer empno, String ename, String job, Integer mgr) {this.empno = empno;this.ename = ename;this.job = job;this.mgr = mgr;}@Overridepublic String toString() {return "ParamsType{" +"empno=" + empno +", ename='" + ename + '\'' +", job='" + job + '\'' +", mgr=" + mgr +'}';}
}

接口:

/*如果在设计方法时,需要使用多个形参。除了使用Map类型外,还可以将这多个形参设计到一个实体类对象上。比如: 如果这些形参都是表对应的那个实体类,那就可以直接使用表对应的实体类,比如Employee还可以单独封装到一个参数实体类,专门用于传参用的,比如定义一个类型ParamsType。select * from emp where empno = ? and ename = ? and job = ? and mgr = ?*/Employee findByEntity(ParamsType params);

 映射文件:

<!--     Employee findByEntity(ParamsType params);--><select id="findByEntity" resultType="Employee" resultMap="resultMap1" parameterType="paramsType">select * from emp where empno=#{empno} and ename=#{ename} and job=#{job} and mgr=#{mgr}</select>

 测试代码

    @Testpublic void test3(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);ParamsType paramsType = new ParamsType(7499,"ALLEN","SALESMAN",7698);Employee byEntity = mapper.findByEntity(paramsType);System.out.println(byEntity);}

第四种:使用@Param注解命名参数

散装参数:需要使用 @Param (" SQL 中的参数占位符名称")

    /*如果在设计方法时,需要使用多个形参。除了上述方法之外,还可以使用注解@Param("占位符的名字")。这种方式就可以使用对应的形参给对应的占位符赋值了。非常方便。*/Employee findByAnnotation(@Param("empno") int empno,@Param("ename") String ename,@Param("job") String jobb,@Param("mgr") int mgrno);

 映射文件:

<!--    Employee findByAnnotation(@Param("empno") int empno,@Param("ename") String ename,@Param("job") String jobb,@Param("mgr") int mgrno);--><select id="findByAnnotation" resultType="Employee" resultMap="resultMap1">select * from emp where empno=#{empno} and ename=#{ename} and job=#{job} and mgr=#{mgr}</select>

测试类:

    @Testpublic void test4(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Employee byEntity = mapper.findByAnnotation(7499,"ALLEN","SALESMAN",7698);System.out.println(byEntity);}

4.6 动态SQL

        SQL语句会随着用户的输入和外部条件的变化而变化,我们称之为动态SQL。MyBatis对动态SQL有很强大的支持。

4.6.1 where/if标签

        if标签,是根据Test属性中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段

应用场景如图所示: 

存在的问题:第一个条件不需要逻辑运算符。  

案例演示:

在接口EmployeeMapper.java里添加如下方法

 /* ---where条件可能会随着用户的输入而改变的我们称之为动态sql----mybatis支持动态sql的写法第一个动态SQL: 应用场景: 浏览器在查询操作时,从多个条件中进行任意个组合条件进行查询,条件个数未知* */Employee findByConditionNum(Map<String,Object> map);

在映射文件EmployeeMapper.xml里配置如下

第一种方案:使用恒等式让所有条件格式都一样

<!--    动态sql的练习Employee findByConditionNum(Map<String,Object> map);用户有的时候,按照 员工编号查询....有的时候按照 员工姓名查询....有的时候按照 员工编号 和职位查询select * from emp where empno = ?select * from emp where ename = ?select * from emp where empno = ? and job = ?
-->
<!--    第一种写法:使用1=1恒成立的写法,来解决 可能多一个and关键字的问题
--><select id="findByConditionNum" resultType="Employee" resultMap="resultMap1" parameterType="map">select * from empwhere 1=1<if test="empno != null and empno!='' ">and empno = #{empno}</if><if test="ename != null and ename!='' ">and ename = #{ename}</if><if test="job != null and ename!='' ">and job = #{job}</if></select>

 第二种方案:使用<where>标签替换where关键字。 注意:where标签会将第一个条件之前的连接符自动去掉

    
<-- 第二种写法:配合where标签, where标签的作用,用来连接条件,如果在第一个条件前有多余连接符号(and 、or)。会自动去掉 --><select id="findByConditionNum" resultType="Employee" resultMap="resultMap1" parameterType="map">  select * from emp<where><if test="empno!=null and empno!='' ">empno = #{empno}</if><if test="ename!=null and ename!='' ">and ename = #{ename}</if><if test="job!=null and job!='' ">and job = #{job}</if></where></select>

4.6.2 choose/when标签  

应用场景:

        choose(when,otherwise):类似于java的switch-case-default, 只要满足一个when,choose就结束了,如果都不满足,就会执行otherwise。

1)在接口EmployeeMapper里添加如下方法

/*第二个动态sql:  应用场景: 浏览器在查询操作时,只能从多个条件中选择一个条件进行查询。条件个数只有一个*/Employee findByConditionOne(Map<String,Object> map);

2)在sql映射文件EmployeeMapper.xml里添加如下内容  

<!--    Employee findByConditionOne(Map<String,Object> map);-->
<!--    对应的标签:  choose-when-otherwise  与java的switch-case(break)-default功能一样,只能执行其中一个分支choose是父标签when是子标签,可以有多个,如果执行了某一个when,则结束chooseotherwise是子标签,只能有一个,如果没有执行的when,则执行otherwise需求: 用户可能按照员工编号查询,也可能按照员工姓名查询,也可能按照奖金查询。
--><select id="findByConditionOne" resultType="Employee" resultMap="resultMap1" parameterType="map">select * from emp where<choose><when test="empno!=null and empno!='' ">empno = #{empno}</when><when test="ename!=null and ename!='' ">ename = #{ename}</when><when test="comm!=null and comm!='' ">comm = #{comm}</when><otherwise>1=1</otherwise></choose></select>

 3)测试

 /*模拟场景浏览器端只传过来一个条件进行查询*/@Testpublic void test6(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Map<String,Object> map = new HashMap<>();map.put("comm",1400);Employee byConditionOne = mapper.findByConditionOne(map);System.out.println(byConditionOne);}

4.6.3 set/if标签

        当进行修改时,有些字段可能有选择的进行修改,这时我们就可以使用<set>标签 配合<if>标签来完成操作。set标签会自动将最后一个条件的逗号去掉。

 1)在接口EmployeeMapper里添加如下方法

/*第三个动态sql: 应用场景: 浏览器在修改信息时,修改的文本框的个数不定*/void updateByCondition(Map<String,Object> map);

2)在sql映射文件EmployeeMapper.xml里添加如下内容  

<!--      void updateByCondition(Map<String,Object> map);标签:set/if两个标签的应用Set标签可以自动去掉最后一个字段后面的逗号。应用场景:  浏览器在修改信息时,可能修改了员工姓名,职位也可能修改了员工的工资和奖金,总之就是修改的内容不一定是什么。
--><update id="updateByCondition" parameterType="map">update emps<set><if test="ename != null and ename != '' ">ename = #{ename},</if><if test="job != null and job != '' ">job = #{job},</if><if test="mgr != null and mgr>0">mgr = #{mgr},</if><if test="hiredate != null and hiredate !='' ">hiredate = #{hiredate},</if><if test="sal != null and sal >0">sal = #{sal},</if><if test="comm != null and comm>=0">comm = #{comm},</if><if test="deptId != null and deptId >0">deptno = #{deptId},</if></set>where empno = #{empno}</update>

 3)测试

/*浏览器传过来的要修改的字段不一定是哪些*/@Testpublic void test7(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);Map<String,Object> map = new HashMap<>();map.put("empno",7499);map.put("ename","TOM");map.put("job","ClEAR");map.put("sal",1500);mapper.updateByCondition(map);sqlSession.commit();}

4.6.4 foreach标签

<foreach>标签的使用, 比如进行集合查询等操作

1)在接口EmployeeMapper里添加如下方法

两种写法:直接传入字符串(使用${})和封装到集合中(foreach方法)

/*第四个动态sql:应用场景: 浏览器端可能使用了复选框来勾选要查询的某一个字段的多个值的数据,底层sql应该是一个集合查询比如:select * from emp where job in ('SALESMAN','ANALYST')传过来的某一个字段值不一定有几个select * from emp where ename in('tom','lily','lucy')empnos: "'tom','lily','lucy'"*/List<Employee> findByOneColumnMuitlValue1(String empnos);/* foreach标签的应用:对一个集合进行遍历。*/List<Employee> findByOneColumnMuitlValue2(@Param("enames") List<String> enames);

2)在EmployeeMapper.xml里添加如下内容

<!--    List<Employee> findByOneColumnMuitlValue1(String empnos);一个字段的多个值的查询操作:  一种变相的写法。
--><select id="findByOneColumnMuitlValue1" resultType="Employee" parameterType="string" resultMap="resultMap1">select * from emp where empno in (${empnos})</select>
<!--    List<Employee> findByOneColumnMuitlValue2(List<String> enames);foreach标签: 用于遍历集合参数的。  通常用于(not) in 这种sql语句。属性:-collection: 用于指定方法的形参-open :  指定集合的开始符号,比如"("-close :  指定集合的结束符号,比如")"-item: 遍历期间的元素的存储位置,即变量   注意: foreach标签中要使用变量-separator: 分隔符,比如","
--><select id="findByOneColumnMuitlValue2" resultType="Employee" resultMap="resultMap1">select * from empwhere ename in<foreach collection="enames" open="(" close=")" item="ename" separator="," >#{ename}</foreach></select>

3)测试

    @Testpublic void test8(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);String empnos = "7499,7521,7566";
//        String enames = "'TOM','WARD'";List<Employee> emps = mapper.findByOneColumnMuitlValue1(empnos);emps.forEach(System.out::println);}@Testpublic void test9(){EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);List<String> names = new ArrayList<>();names.add("TOM");names.add("WARD");names.add("KING");List<Employee> list = mapper.findByOneColumnMuitlValue2(names);list.forEach(System.out::println);}

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);
<!--    Student findById(Integer id);属性resultType: 书写方法的返回值类型  代理对象通过sql语句进行查询,然后将查询到的结果自动封装成返回值类型的实例--><select id="findById" resultType="Student">select * from student where id=#{arg0022331kkkskk}</select>

4.7.3 返回List类型

 /*返回值为List类型*/List<Student> findByPage(@Param("offset") int offset,@Param("pageSize") int pageSize);
<!--    List<Student> findByPage(@Param("page") int page,@Param("pageSize") int pageSize);返回值类型: 如果方法的返回值类型时List, 那么在属性resultType上只需要指定集合当元素类型的名字即可--><select id="findByPage" resultType="Student">select * from student limit #{offset},#{pageSize}</select>

4.7.4 返回Map类型

/*返回值为Map类型应用场景: 将查询出来的信息封装成Map形式的样子。表的字段名作为key字段值作为value*/Map<String,Object> findByName(String name);
<select id="findByName" resultType="Map">select * from student where name=#{name}</select>

4.7.5 返回Map实例的集合

 /*应用场景: 将查询出来的多条记录,封装到Map集合中条件:通过性别,开头查询出来多条记录, 封装到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}*/
//    Map<String,Map<String,Object>>@MapKey("id")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 模糊查询

在sql映射文件中的三种写法:

like  '%${username}%'
like concat('%',#{username},'%')
like "%"#{username}"%"

案例演示:

/*模糊查询:select * from student where name like '%备%'*/List<Student> findByNameLike(@Param("shortName") String shortName);
<!--    select * from student where name like '%${shortName}%'-->
<!--    select * from student where name like concat('%',#{shortName},"%")--><!--    模糊匹配。--><select id="findByNameLike" resultType="Student">select * from student where name like "%"#{shortName}"%"</select>

4.8.2 批量删除

两种方法:

        批量删除: 一般都是通过唯一标识符来删除的,浏览器上应该使用的是复选框。

        传入服务端的应该是一个唯一标识符的集合。

第一种处理方式 String ids = "1,2,3,4,5" 占位符应该使用${} delete from student where id in (${})

   void deleteBatch1(String ids);
    <delete id="deleteBatch1">delete from student where id in (${i})</delete>

第二种处理方式: 使用foreach标签

void deleteBatch2(@Param("ids") List<Integer> ids);
<!--    foreach中的collection属性默认为list list就是你传入的集合的。 如果没有指定(用@param指定),就会是默认的list--><delete id="deleteBatch2" parameterType="list">delete from student where id in<foreach collection="ids" open="(" close=")" separator="," item="id" >#{id}</foreach></delete>

 4.8.3 动态指定表名

动态指定表名应该就是根据不同的表名实现不同的查询结果。

/*动态指定表名*/List<Student> findAllByTableName(String tableName);
<!--    动态指定表名:  站位符使用${}List<Student> findAllByTableName(String tableName);--><select id="findAllByTableName" resultType="com.mybatis.pojo.Student">select  * from ${tableName}</select>

4.8.4 主键返回

自增长类型主键

主键返回:

         之前的写法: 想要向数据库中新增一条记录 insert into student (id,name,gender,age,address)values(null,'安其拉','f',40,'北京')

         自增主键会传值null,想要获取主键值还需要再去表中查询。

mybatis为我们提供了可以在添加时获取主键的方式。(useGenerateKeys,keyproperty)

 void insertStudent(Student student);
<!--应用场景: 向数据库保存记录时,同时返回这条记录的主键值。应用于后续代码。useGeneratedKeys: 是否要使用数据库中生产的该条记录的主键值,true表示使用,false表示不使用keyproperty: 用于指定生成的主键值,存储的位置,一般指的是对应的实体类的属性值。
--><insert id="insertStudent" useGeneratedKeys="true" keyProperty="id">insert into student values(null,#{name},#{gender},#{age},#{address})</insert>

 测试:

@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(关联,联合)

        第三种写法:分步写法

/*通过员工id 查找员工信息和其所在部门信息* 两种写法在mapper文件中。* */Employee findById(Integer id);
 <!--    字段映射写法。column: 指定数据库表中的字段名,property: 指定实体类中的属性名,由于要给属性dept赋值,而dept是一个Dept类型,因此实际上是给dept的各个属性赋值。--><resultMap id="result1" type="employee"><id property="id" column="empno"/><result property="name" column="ename"/><result property="job" column="job"/><result property="mgr" column="mgr"/><result property="hireDate" column="hiredate"/><result property="sal" column="sal"/><result property="bonus" column="comm"/><result property="deptId" column="deptno"/><result property="dept.deptno" column="deptno"/><result property="dept.dname" column="dname"/><result property="dept.loc" column="loc"/></resultMap><select id="findById" resultMap="result1">select * from emp e join dept d on e.deptno = d.deptno where e.empno = #{id}</select>

 第二种写法 association

 <resultMap id="result2" type="employee"><!--        id子标签,专门用于主键映射column: 指定数据库表中的字段名property: 指定实体类中的属性名--><id property="id" column="empno"/><result property="name" column="ename"/><result property="job" column="job"/><result property="mgr" column="mgr"/><result property="hireDate" column="hiredate"/><result property="sal" column="sal"/><result property="bonus" column="comm"/><result property="deptId" column="deptno"/><!--       多对一的查询的第二种写法: 使用association(级联,关联,联合)标签。property: 用于指定给实体类的哪个属性做映射。javaType: 用于指定该属性的java类型。association的子标签: 用于将字段与实体类的属性的属性进行映射关系。
--><association property="dept" javaType="Dept"><result property="deptno" column="deptno"/><result property="dname" column="dname"/><result property="loc" column="loc"/></association></resultMap><!--    --><select id="findById" resultMap="result2">select * from emp e join dept d on e.deptno = d.deptno where e.empno = #{id}</select>

 第三种 分步查询

 // 多对一的查询的第三种写法: 分步查询// 通过id 查询员工信息,同时查询出员工所对应的部门信息Employee findByIdFirstEmpSecondDept(Integer id);
/*多对一的第三种写法的第二步*/Dept findByIdSecondStep(Integer deptno);

区别代码:

<association property="dept" select="com.mybatis.mapper.DeptMapper.findByIdSecondStep" column="deptno"/>
<!--多对一的分步写法的第二步
--><select id="findByIdSecondStep" resultType="Dept">select * from dept where deptno = #{id}</select>
<resultMap id="result3" type="employee"><id property="id" column="empno"/><result property="name" column="ename"/><result property="job" column="job"/><result property="mgr" column="mgr"/><result property="hireDate" column="hiredate"/><result property="sal" column="sal"/><result property="bonus" column="comm"/><result property="deptId" column="deptno"/><!--       多对一的查询的第三种写法: 也是使用association(级联,关联,联合)标签。不同再写association里面的子标签了只需配置下面几个属性即可property: 用于指定给实体类的关联属性(Dept dept)select: 对应的是第二步查询,语法结构:namespace.id(另一个映射的namespace地址值加上映射里的方法id值)column: 第二步的sql需要一个条件,column用于指定第一步查询中要作为第二个sql语句的字段名。--><association property="dept" select="com.mybatis.mapper.DeptMapper.findByIdSecondStep" column="deptno"/></resultMap><select id="findByIdFirstEmpSecondDept" resultMap="result3">select * from emp where empno = #{id}</select>

4.9.2 一对多查询

        一对多,其实就是多对一的反向操作。教师信息表是主表,学生信息表是副表,通过教师信息表的任意一条记录,都可以在学生信息表里找到该教师的多个学生信息。

第一种写法:collection

 // 案例演示: 查询某一个部门的信息,及其所有员工的信息。Dept findByIdCollection(Integer deptno);
<!--    一对多的写法 collection--><resultMap id="deptMap1" type="Dept"><result property="deptno" column="deptno"/><result property="dname" column="dname"/><result property="loc" column="loc"/><collection property="emps" ofType="Employee"><id property="id" column="empno"/><result property="name" column="ename"/><result property="job" column="job"/><result property="mgr" column="mgr"/><result property="hireDate" column="hiredate"/><result property="sal" column="sal"/><result property="bonus" column="comm"/><result property="deptId" column="deptno"/></collection></resultMap><select id="findByIdCollection" resultMap="deptMap1">select * from dept d join emp e on d.deptno = e.deptno where d.deptno=#{deptno}</select>

第二种写法:分步写法

 /*一对多的第二种写法: 分步查询*/Dept findByIdFirstStep(Integer deptno);
 // 一对多查询的第二种写法分步查询// 第二步:通过第一步查询的结果中的deptno字段进行查询员工信息List<Employee> findByIdSecondStep(Integer deptno);

 区别代码:

<collection property="emps" select="com.mybatis.mapper.EmployeeMapper.findByIdSecondStep" column="deptno"/>
    <resultMap id="deptMap2" type="Dept"><result property="deptno" column="deptno"/><result property="dname" column="dname"/><result property="loc" column="loc"/><collection property="emps" select="com.mybatis.mapper.EmployeeMapper.findByIdSecondStep" column="deptno"></collection></resultMap><!--    一对多查询的分步第一步--><select id="findByIdFirstStep" resultMap="deptMap2">select * from dept where deptno=#{deptno}</select>
<resultMap id="result4" type="employee"><id property="id" column="empno"/><result property="name" column="ename"/><result property="job" column="job"/><result property="mgr" column="mgr"/><result property="hireDate" column="hiredate"/><result property="sal" column="sal"/><result property="bonus" column="comm"/><result property="deptId" column="deptno"/></resultMap><select id="findByIdSecondStep" resultMap="result4">select * from emp where deptno = #{id}</select>

4.9.3 多对多查询

        一般多对多,都会涉及到第三张表。 学生信息表(每个学生的信息都是唯一的一条记录), 课程信息表(每个科目也都是唯一的一条记录),学生课程表(一个学生可以选择多个科目进行学习,一个科目可以被多个学生选择学习)。学生信息表和课程信息表通过学生课程表进行的对应关系,就是多对多的关系。

建表sql:

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);

 StudentMapper接口

public interface StudentMapper {/*查询每个学生的基本信息,及其所学科目信息*/List<Student> findAll();/*根据学生ID,查询学生的基本信息,及其所学科目信息(不包含成绩)*/Student findById(Integer id);/*查询所有学生的学号,姓名,性别,年龄,课程号,选修科目名称,选修成绩*/List<StudentInfo> findAllStudentInfo();
}

 mapper映射文件

<mapper namespace="com.mybatis.mapper.StudentMapper"><resultMap id="studentMap1" type="Student"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/><result property="gender" column="gender"/><result property="address" column="address"/><collection property="courses" ofType="course"><id property="cId" column="c_id"/><result property="cName" column="c_name"/><result property="tId" column="t_id"/><result property="academy" column="c_academy"/><result property="note" column="c_note"/></collection></resultMap><select id="findAll" resultMap="studentMap1">select * from student sleft join score sc on s.id = sc.s_idleft join course c on sc.c_id = c.c_id</select><select id="findById" resultMap="studentMap1">select * from student sleft join score sc on s.id = sc.s_idleft join course c on sc.c_id = c.c_idwhere s.id=#{id}</select><resultMap id="studentMap2" type="studentInfo"><id property="id" column="id"/><result property="name" column="name"/><result property="age" column="age"/><result property="gender" column="gender"/><result property="cid" column="c_id"/><result property="cName" column="c_name"/><result property="score" column="score"/></resultMap><select id="findAllStudentInfo" resultMap="studentMap2">select s.id,s.name,s.age,s.gender,c.c_id,c.c_name,scorefrom student sleft join score sc on s.id = sc.s_idleft join course c on sc.c_id = c.c_id</select>
</mapper>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/433977.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

erlang学习:Linux命令学习7

grep进阶&#xff0c;正则表达式初步学习 正则表达式简介 正则表达式是由一些具有特殊含义的字符组成的字符串&#xff0c;多用于查找、替换符合规则的字符串。在表单验证、Url映射等处都会经常用到,同样在linux中也能够用到。 元字符 元字符&#xff1a;即为有特定含义的字…

搭建基于H.265编码的RTSP推流云服务器

一、前言 网上能够找到的RTSP流地址&#xff0c;均是基于H.264编码的RTSP流地址&#xff0c;无法测试应用是否可以播放H265实时流为此&#xff0c;搭建本地的把H.264转码成H.265的RTSP服务器&#xff0c;不管是通过VLC搭建本地RTSP服务器&#xff0c;还是通过FFmpeg搭建本地RT…

自定义knife4j访问路径

文章目录 本文档只是为了留档方便以后工作运维&#xff0c;或者给同事分享文档内容比较简陋命令也不是特别全&#xff0c;不适合小白观看&#xff0c;如有不懂可以私信&#xff0c;上班期间都是在得 原由&#xff0c;嫌弃doc.html 太大众 直接重定向&#xff0c;直接上代码了 p…

数据结构:详解搜索二叉树

目录 一、搜索二叉树的概念 二、搜索二叉树的基本结构 三、搜索二叉树的插入 四、搜索二叉树的查找 五 、搜索二叉树的删除 一、搜索二叉树的概念 ⼆叉搜索树⼜称⼆叉排序树&#xff0c;它或者是⼀棵空树&#xff0c;或者是具有以下性质的⼆叉树&#xff1a; 若它的左子树…

smb文件夹共享设置

UOS统信三种不同场景的文件夹共享,分别是:1、UOS系统间的文件共享;2、Windows7系统访问UOS共享的文件;3、UOS系统访问Windows7共享的文件 文章目录 功能概述功能介绍第一种场景:UOS系统之间的文件共享设置步骤一:打开共享文件夹步骤二:共享管理步骤三:设置共享密码步骤…

【步联科技身份证】 身份证读取与解析———未来之窗行业应用跨平台架构

一、身份证解析代码 C# function 身份证数据解析_湖南步联科技(wzxx) {var result {};result[xm] wzxx.substr(0, 15);result[xbdm] wzxx.substr(15, 1);result[mzdm] wzxx.substr(16, 2);result[csrq] wzxx.substr(18, 8);result[dzmc] wzxx.substr(26, 35);result[gms…

【射频通信电子线路第六讲】射频信号与调制包括调幅和部分调频的内容

一、调制&#xff08;Modulation&#xff09;与解调&#xff08;Demodulation&#xff09; 1、相关概念 调制是指使一个信号&#xff08;如光信号、高频电磁振荡等&#xff09;的某些参数&#xff08;振幅、频率和相位&#xff09;按照另一个欲传输的信号的特点变化的过程。 …

普通二叉搜索树的模拟实现【C++】

二叉搜素树简单介绍 二叉搜索树又称二叉排序树&#xff0c;是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树 注意…

[ComfyUI]Flux 出图背景太模糊?一招解决!

小伙伴们在使用 Flux 出图的时候&#xff0c;应该也遇到过大多数情况下背景都是比较模糊的&#xff0c;虽然大多数时候没啥影响&#xff0c;毕竟我们很多时候都只是看主体嘛。 但是也有些场景&#xff0c;我们希望整体的构图中背景也可以高清一些&#xff0c;这样在看整张图片…

【第十六章:Sentosa_DSML社区版-机器学习之生存分析】

【第十六章&#xff1a;Sentosa_DSML社区版-机器学习之生存分析】 16.1 加速失效时间回归 1.算子介绍 加速失效时间回归模型Accelerated failure time (AFT)是一个监督型参数化的回归模型&#xff0c;它可以处理删失数据。它描述了一个生存时间的对数模型&#xff0c;所以它通…

【CSS】透明度 、过渡 、动画 、渐变

opacity 透明度transition 过渡animation 动画background 渐变 ( 线性渐变 \ 径向渐变 \ 锥形渐变 ) opacity 透明度 设置元素的透明度&#xff0c;会影响元素及其所有子元素的透明度&#xff0c;值范围&#xff1a;0&#xff08;完全透明&#xff09;到 1&#xff08;完全不透…

经济不好,但是遍地都是赚钱的机会

现在职场越来越内卷&#xff0c;裁员风波也是不断&#xff0c;前些天看到一个帖子&#xff0c;裁员都裁到应届生头上了。 都说00后整治职场&#xff0c;在如今环境下也要掂量一下了。 大家都在抱怨环境&#xff0c;可是你有没有想过&#xff0c;有些人在闷声发着小财。 下面…

Pygame中Sprite实现逃亡游戏5

在《Pygame中Sprite实现逃亡游戏4》中通过碰撞检测实现了玩家、飞龙与飞火之间的碰撞处理&#xff0c;基本上实现了逃亡功能。最后&#xff0c;实现这个逃亡游戏中文字提示的功能。 1 操作提示 当进入游戏后&#xff0c;会在玩家下方的位置给出操作提示&#xff0c;如图1所示…

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall

数据集-目标检测系列-鲨鱼检测数据集 shark >> DataBall 数据集-目标检测系列-鲨鱼检测数据集 shark 数据量&#xff1a;6k 数据样例项目地址&#xff1a; gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview github: https://github.com/Te…

3款照片人物开口说话AI工具,跟真人说话一样~免费!短视频带货必备!(附教程)

大家好&#xff0c;我是画画的小强 今天给大家分享一个AI图片口播数字人讲认知思维&#xff0c;单号佣金赚5W的AI带货信息差玩法&#xff0c;许多小伙伴表示对这类AI带货玩法感兴趣。 说实话&#xff0c;现在AI照片人物对口型工具&#xff0c;越来越逼真&#xff0c;很难辨识出…

JAVA开源项目 技术交流分享平台 计算机毕业设计

本文项目编号 T 053 &#xff0c;文末自助获取源码 \color{red}{T053&#xff0c;文末自助获取源码} T053&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 新…

数据集-目标检测系列-豹子 猎豹 检测数据集 leopard>> DataBall

数据集-目标检测系列-豹子 猎豹 检测数据集 leopard>> DataBall 数据集-目标检测系列-豹子 猎豹 检测数据集 leopard 数据量&#xff1a;5k 想要进一步了解&#xff0c;请联系。 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#x…

赋值运算符重载

背景&#xff1a; 在EHR模块进行调试时&#xff0c;发现QVector3D对象进行赋值时&#xff0c;出现变量未赋值成功问题。 问题描述&#xff1a; 在进行代码调试时&#xff0c;发现赋值操作未成功&#xff0c;导致代码逻辑异常&#xff0c;经过分析&#xff0c;发现QVector3D 赋…

前台项目启动/打包报错 Error: error:0308010C:digital envelope routines::unsupported

在package.json中修改启动/打包语句 如图&#xff0c;我这里是打包时候报错&#xff0c;就在build里前面加上 set NODE_OPTIONS--openssl-legacy-provider && 再次打包&#xff0c;成功。

828华为云征文|部署敏捷项目管理系统工具 ZenTao

828华为云征文&#xff5c;部署敏捷项目管理系统工具 ZenTao 一、Flexus云服务器X实例介绍二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置2.4 Docker 环境搭建 三、Flexus云服务器X实例部署 ZenTao3.1 ZenTao 介绍3.2 ZenTao 部署3.3 ZenTao 使用 四、总…