菜鸟学Mybatis之——动态SQL、resultMap、association、collection

MyBatis

Mybatis 1.1、1.2

1.3 常见问题及解决

1.3.1 解决数据库字段名与JavaBean属性名不匹配问题:

resultMap – The most complicated and powerful element that describes how to load your objects from the database resultsets. (描述怎么把数据库中的内容加载到对象中去)

数据库的字段名与Java类的属性名名称不匹配了,并且双方都不能修改,这时怎么办?

问题:

Java类的属性:

在这里插入图片描述

数据库的字段名:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.home.java.AdminMapper"> 
    <select id="queryAllAdmin" resultType="com.home.java.Admin">
        select * from tb_admin
    </select>
</mapper>
public class Main {
    
    
    public static void main(String[] args) throws IOException {
    
    

        String res = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(res);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();

        AdminMapper mapper = session.getMapper(AdminMapper.class);
        List<Admin> admins = mapper.queryAllAdmin();
        System.out.println(admins.get(0).getAccount());//null 因为在数据库中找不到account的字段
    }
}

解决问题

在xml文件中添加resultMap

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.home.java.AdminMapper"> 
    <resultMap id="zaq" type="com.home.java.Admin">
        <id property="id" column="id"></id> <!--property表示对象的属性名,column表示数据库中的字段名-->
        <result property="account" column="username"></result>
        <result property="pwd" column="pwd"></result> 
        <!--javaType表示在Java中property的类型是什么,jdbcType同理-->
    </resultMap>
    
    <select id="queryAllAdmin" resultMap="zaq">
        select * from tb_admin
    </select>
</mapper>

这样不用修改名称就解决了不匹配的问题,resultMap就相当于是数据库与Java代码之间的桥梁!

<resultMap>:自定义映射,处理复杂的表的关系

  • <id>:设置主键的映射关系,column设置字段名,property设置属性名

  • <result>:设置非主键的映射关系,column设置字段名,property设置属性名

resultMap下的id标签就是专门用来设置主键的映射关系,且只能有一个


1.3.2 实现添加时获取自动生成的主键

<insert id="saveDepartment" parameterType="com.home.java.bean.Department" useGeneratedKeys="true" keyProperty="id" >/*为了能在插入过程中拿到id值,在此设置这两个属性*/
        insert into tb_dep(depname) values (#{name})
</insert>

keyProperty:表示将生成的主键赋值给传入参数的哪个属性!

比如上面的过程,解读:传过来了一个对象(department的对象),然后把insert执行完生成的主键赋值给这个对象的id属性!

总结

  • useGeneratedKeys:可以使用自动生成的主键
  • keyProperty:将自动生成的主键赋值给传递过来的参数的对应属性,即Mybatis获取主键后,将这个值封装给JavaBean的哪个属性

1.3.3 关联查询_association定义关联对象封装规则

一对一的级联关系

数据库:通过辅助表中保存主表中的ID来关联两个表

实体类:主类中保存一个辅助类的对象

首先看一下数据库中的表之间的关系与类的之间的关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pkzn5DE2-1588841777257)(C:\Users\张澳琪\AppData\Roaming\Typora\typora-user-images\image-20200424220229703.png)]

teacher表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipBOv7ib-1588841777260)(C:\Users\张澳琪\AppData\Roaming\Typora\typora-user-images\image-20200424220258143.png)]

teacherInfo表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7BInSyEu-1588841777261)(C:\Users\张澳琪\AppData\Roaming\Typora\typora-user-images\image-20200424220314759.png)]

TeacherMapper.java

public interface TeacherMapper {
    
    

    Teacher queryTeacherById(int id);

    Teacher queryWithoutInfoById(int id);

}

teachermapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.home.java.TeacherMapper">
    <!--注意Teacher里有一个TeacherInfo,现在怎么让这个TeacherInfo 和 数据库中的表对应呢 这里用到association-->
    <resultMap id="tea" type="com.home.java.Teacher">
        <id column="id" property="id"></id>
        <result column="teanum" property="teanum"></result>
        <result column="pass" property="pwd"></result>
        <result column="status" property="status"></result>
        <association property="teacherInfo"><!--配置另外一个对象-->
            <id column="ti_id" property="id"></id> <!--这里teacherInfo表里的id就不能在写成id了,这样就冲突了,所以给他起了别名ti_id-->
            <result column="t_id" property="t_id"></result>
            <result column="phone" property="phone"></result>
            <result column="email" property="email"></result>
            <result column="teaname" property="teaname"></result>
            <result column="address" property="address"></result>
            <result column="jointime" property="joinTime"></result>
            <result column="gender" property="gender"></result>
        </association>
    </resultMap>


    <select id="queryTeacherById" resultMap="tea">
        -- 不能写成select *
        select
            a.id as id,
            teanum,
            pass,
            status,
            b.id as ti_id,
            t_id,
            phone,
            email,
            teaname,
            address,
            jointime,
            gender
        from tb_teacher as a join tb_teainfo as b ON a.id = b.t_id where a.id=#{id}
    </select>

    <select id="queryWithoutInfoById" parameterType="int" resultType="com.home.java.Teacher">
        select * from tb_teacher  where id=#{id}
    </select>
</mapper>

<associate>:最好设置上javaType,Mybatis可以通过associate创建一个javaType类型的对象(底层用了大量的反射。无反射,无框架)

测试:

public class Main {
    
    
    public static void main(String[] args) throws IOException {
    
    

        String res = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(res);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        SqlSession session = sqlSessionFactory.openSession();

        TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
        Teacher teacher = teacherMapper.queryTeacherById(29);
        System.out.println(teacher.getTeacherInfo().getAddress());
    }
}

1.3.4 关联查询_association分步查询

比如上面查询老师会将老师信息也查询出来,现在分为两步:

  • 先按照老师id查询出老师列表
  • 然后通过老师表id与老师信息表的t_id查出每个老师对应的信息

代码实现:

TeacherInfoMapper.java

public interface TeacherInfoMapper {
    
    
    TeacherInfo queryTeacherInfoByid(int id);//通过id查询老师信息
}

teacherinfomapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.home.java.mapper.TeacherInfoMapper">

    <select id="queryTeacherInfoByid" resultType="com.home.java.bean.TeacherInfo">
        select * from tb_teainfo where t_id = #{id}
    </select>

</mapper>

TeacherMapper.java

public interface TeacherMapper {
    
    
    Teacher queryTeacherByIdStep(int id);
}

teachermapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.TeacherMapper">
    <resultMap id="stepquery" type="com.home.java.bean.Teacher">
        <id column="id" property="id"></id>
        <result column="teanum" property="teanum"></result>
        <result column="pass" property="pwd"></result>
        <result column="status" property="status"></result>
        <association property="teacherInfo" select="com.home.java.mapper.TeacherInfoMapper.queryTeacherInfoByid" column="id"></association> <!--column="id":将查询出的id列的值当作参数传给queryTeacherInfoByid方法-->
    </resultMap>

    <select id="queryTeacherByIdStep" resultMap="stepquery">
        select * from tb_teacher where id = #{id} <!--先通过id查询老师列表,然后通过上面association调用查询老师信息的方法-->
    </select>

</mapper>

column属性:指定将查询的哪一列的值传给select的方法。

如果要将多列的值传递过去,则将多列的值封装到map中传递。即column="{key1=column1,key2=column2…}"

1.3.5 关联查询_collection定义关联集合封装规则

如果一个类中保存了一个容器比如department类中有一个链表(员工列表),这时resultMap中就不能用association匹配了,因为association匹配的是一个单一的类。需要使用collection

部门与员工的映射关系:

部门

public class Department {
    
    
    private int id;
    private String name;
    private List<Employ> employs;//一个部门有多个员工
	//省略getter、setter方法
}

员工:

public class Employ {
    
    
    private int id;
    private String employName;
    private Department department; //一个员工只有一个部门
	//省略getter、setter方法
}

DepartmentMapper.java

public interface DepartmentMapper {
    
    
    Department queryDepartmentById(int id);
}

departmentmapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.DepartmentMapper">

    <resultMap id="department" type="com.home.java.bean.Department">
        <id property="id" column="id"></id>
        <result property="name" column="depname"></result>
        <collection property="employs" ofType="com.home.java.bean.Employ"> <!--现在如果查询到的内容能和Employ匹配上,他就会将查询结果放入Employ中-->
            <id column="e_id" property="id"></id>
            <result column="employName" property="employName"></result>
            <!--这时不再需要反向把Department查出了-->
        </collection>
    </resultMap>

    <select id="queryDepartmentById" parameterType="int" resultMap="department">
        select
         a.id as id,
         depname,
         b.id as e_id,
         employName
        from tb_dep as a join tb_employ as b on a.id=b.d_id and a.id=#{id}
    </select>
</mapper>

测试,核心代码:

DepartmentMapper departmentMapper = session.getMapper(DepartmentMapper.class);
Department department = departmentMapper.queryDepartmentById(1);
System.out.println(department.getEmploys().get(1).getEmployName());

ofType属性:指集合中的类型,不需要指定javaType

collection分布查询(与association分布查询相同)

总结:

  • 当属性是一个对象时使用association

  • 当属性是一个集合时使用collection

  • 查询返回list,只需要将resultType的参数类型设置为list的泛型参数即可。

  • 查出一条数据返回map,将resultType的值设置为map(mabatis为Map起了别名,如果没起别名则要写全类名)

  • 查询多条数据返回map(Map<Integer,Employee>):则resultType的值设置为Employee类的全类名,且在方法上加注解@MapKey(“id”),告诉mybatis封装这个map的时候使用Employee的哪个属性作为map的key


1.4 动态SQL

1.<if test="eid != null "></if>:通过test表达式,拼接SQL(注意这个表达式只能判断数字,不能判断字符)

2.<where>:添加where关键字,同时会去掉多余的and(但是他只能去掉前面多出来的and或者or)

<where>
	<if test="id != null">
    	id = #{id}
    </if>
    <if test="eamil != null">
    	and email = #{email}
    </if>
    <if test="gender != null">
    	and gender = #{gender}
    </if>
</where>
<!--上面这种写法,如果id为空,mybatis会将where标签中email前的and关键字去掉。-->
<!--但是下面这种写法,如果gender为空,where标签中email后面的and关键字就不能被去掉-->
<where>
	<if test="id != null">
    	id = #{id} and
    </if>
    <if test="eamil != null">
    	email = #{email} and
    </if>
    <if test="gender != null">
    	gender = #{gender}
    </if>
</where>
<!--所以如果用where标签就最好把and写在前面。或者用trim标签可以解决这种问题-->

3.<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">:截取并拼接

  • prefix:在操作的SQL语句前加入某些内容

  • suffix:在操作的SQL语句后加入某些内容

  • prefixOverrides:把操作的SQL语句前的某些内容去掉

  • suffixOverrides:把操作的SQL语句后的某些内容去掉

    eg:设置<trim prefix="where" suffixOverrides="and | or">(如果产生多余的and或者or,则去掉)

4.<choose>:选择某一个when或者otherwise执行

  • <when test="">:通过test表达式拼接SQL

  • <otherwise>:当when都不符合条件,就会选择otherwise拼接SQL

5.<foreach collection="" item="" close="" open="" separator="" index="">:对一个数组或集合进行遍历

  • collection:指定要遍历的集合或数组
  • item:设置别名(将当前遍历出的元素赋值给指定的变量)
  • close:设置循环体的结束内容
  • open:设置循环体的内容
  • separator:设置每一次循环之间的分隔符
  • index:若遍历的是list,index代表下标(item就是当前值);若遍历的是map,index代表键(item就是map的值)

eg:批量删除(传入的参数是字符串:“1,2,3”)

<delete id="deleteMpreEmp">
        delete from emp where eid in (${value})
    	/*注意不能用#{},因为#{}用的是通配符?赋值,会给参数默认加单引号,这时只会删除第一个参数,即eid为1的值*/
</delete>

eg:foreach实现批量删除(传入的参数是链表)

<delete id="deleteMoreByList" parameterType="java.util.List">
        delete from emp where eid in 
        <foreach collection="list" open="(" item="eid" separator="," close=")">
            #{eid}
        </foreach>
</delete>

实现批量修改:

<update id="updateMoreByArray">
        <foreach collection="emps" item="emp">
            update emp set ename = #{emp.ename}, age = #{emp.age}, sex = #{emp.sex} where eid = #{emp.eid}; /*这样一次会执行多条sql语句*/
        </foreach>
</update>

把每条数据修改为对应内容,注意:必须在连接地址(url)后添加参数allowMultiQueries=true(允许一次执行多条sql语句)

6.<sql id="">:设置一段SQL片段,即公共SQL,可以被当前映射文件中所有的SQL语句所访问,使用时用include标签

  • <include refid="">:访问某个SQL片段

    include还可以定义一些property,sql标签内部就能使用自定义的属性。取值方式为${prop-name}

eg:

	<sql id="empColumns">
        eid,ename,age,sex,did,${testColumn}
    </sql>

    <select id="getEmpByEid" resultType="com.home.java.Emp">
        select
        <include refid="empColumns">
            <property name="testColumn" value="abc"/>
        </include>
        from emp
        where eid = #{eid}
    </select>

Mybatis学习内容持续更新中…

猜你喜欢

转载自blog.csdn.net/ysf15609260848/article/details/105976362