解决数据库字段名与属性名不一致的映射问题、resultMap详解(多对一关系映射问题)、一对多关系映射问题

解决数据库字段名与属性名不一致的映射问题

a>为字段起别名来保持和属性名的一致,在不一致的字段后加上 属性名:

select eid,emp_name eName,gender,age,dept from employeeinfo 

b>通过全局配置文件MybatisConfig.xml,配置驼峰映射法,这样字段名中的_下划线会自动映射为符合驼峰命名法的属性名,如emp_name--->empName:

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

c>通过resultMap标签设置自定义映射,<select>标签中resultMap的值需要与<resultMap>标签中id的值保持一致,<resultMap>中type用来设置映射关系的实体类类型

 <resultMap>标签中,子标签<id>用来设置主键映射关系,子标签<result>用来设置其他字段与属性名的映射关系,子标签中的property值为属性名,colum值为字段名

 

 

ResulMap详解:

有如下学生表和老师表,学生与老师是多对一的关系,,tid与teacher表的id关联

 

 对应的实体类信息:

 我们想查询一名同学和这位同学对应的老师信息,数据库执行结果如下:

那在mybatis中如果我们用普通的Student类型来接收会有什么结果呢:

public Student selectSandTById(@Param("sid") Integer id);
<select id="selectSandTById" resultType="pojo.Student">
        select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
</select>

查询结果:

Student(id=1, name=学生1, age=20, teacher=null)

可以看到,学生的信息可以正常查询出来,但是老师的信息为null,说明mybatis无法将后面查询的老师数据映射到Teacher类型中,但我们又不能在resultType中声明两个实体类,所以,如果我们想同时查询学生和老师的信息,就需要用到resultMap标签来自定义映射关系,有如下几种方式:

a>通过级联属性赋值解决多对一的映射关系

public Student selectSandTById(@Param("sid") Integer id);
    <select id="selectSandTById" resultMap="StudentAndTeacher">
        select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
    </select>
    <resultMap id="StudentAndTeacher" type="pojo.Student">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="teacher.id" column="id"></result>
        <result property="teacher.name" column="name"></result>
    </resultMap>
结果:Student(id=1, name=学生1, age=20, teacher=Teacher(id=1, name=学生1))    
这里Teacher的信息被Student的信息覆盖的原因时:teacher表与student的id,name字段重名

使用resultMap标签的注意点:

1.<select>标签中resultMap的值需要与<resultMap>标签中id的值保持一致,不然mybatis怎么知道它要去找哪一个resultMap

2.<resultMap>中type用来设置映射关系的实体类类型,我们需要Student类型数据,其中包含了Teacher类型数据,所以type中指定的是Student类型

3. <resultMap>标签中,子标签<id>用来设置主键映射关系(也就是student表的主键),子标签<result>用来设置其他字段与属性名的映射关系,子标签中的property值为属性名,colum值为字段名

4.级联属性的使用方法为:对象名.属性,例如我们要把teacher表中的数据映射到teacher对象中,所以property是teacher.id,teacher.name

虽然结果是成功的查询出来了,但是我发现了一个问题,也是我在设计表的时候的一个疏忽,student表和teacher表中他们的id和name字段名都一样:

所以在指定column值的时候,就出现了值一致情况,然后我尝试了使用表名.字段的方式来查询:

  <select id="selectSandTById" resultMap="StudentAndTeacher">
        select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
    </select>
    <resultMap id="StudentAndTeacher" type="pojo.Student">
        <id property="id" column="student.id"></id>
        <result property="name" column="student.name"></result>
        <result property="age" column="age"></result>
        <result property="teacher.id" column="teacher.id"></result>
        <result property="teacher.name" column="teacher.name"></result>
    </resultMap>
结果:Student(id=1, name=学生1, age=20, teacher=null)

个人猜测是如果出现两表字段名重复的情况,mybatis会按字段出现先后顺序赋值?

后续:我太粗心了,一开始没发现Teacher与Student查出来的id和name居然相同,然后再去网上查了Mybatis多表联查不同表字段名相同的解决方法,其实就是起别名,下面附上正确代码:

    <select id="selectSandTById" resultMap="StudentAndTeacher">
        select student.id id1,student.name name1,student.age,teacher.id id2,teacher.name name2
        from student
        left join teacher
        on student.tid = teacher.id
        where student.id = #{sid}
    </select>
    <resultMap id="StudentAndTeacher" type="pojo.Student">
        <id property="id" column="id1"></id>
        <result property="name" column="name1"></result>
        <result property="age" column="age"></result>
        <association property="teacher" javaType="pojo.Teacher">
            <id property="id" column="id2"></id>
            <result property="name" column="name2"></result>
        </association>
    </resultMap>
查询结果正确:Student(id=1, name=学生1, age=20, teacher=Teacher(id=1, name=老师1, studentList=null))

 

 

b>通过association标签解决

association标签是专门处理多对一关系的标签,和resultMap标签注意点一样,id设置主键,result设置其他值

public Student selectSandTById(@Param("sid") Integer id);
   <select id="selectSandTById" resultMap="StudentAndTeacher">
        select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
    </select>
    <resultMap id="StudentAndTeacher" type="pojo.Student">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <association property="teacher" javaType="pojo.Teacher">
            <id property="id" column="id"></id>
            <result property="name" column="name"></result>
        </association>
    </resultMap>

 

c>通过分布查询解决(常用)

我将这种方式理解为SQL语句的子查询:先执行一个SQL语句,该语句的查询结果作另一个SQL语句查询条件。

分布查询分为两个步骤:第一步是先查询出student的信息,第二部是通过student信息中的tid去查询teacher的信息,他们之间关系连接依旧是使用association标签

/**
     * 分布查询第一步,根据id查询出student的信息
     * @param id
     * @return
     */
    public Student selectStudentAndTeacherStepOne(@Param("sid") Integer id);

    /**
     * 分布查询第二步,根据student信息中的tid去查找teacher信息
     * @param id
     * @return
     */
    public Teacher selectStudentAndTeacherStepTwo(@Param("tid") Integer id);
   //第一步查询
   <select id="selectStudentAndTeacherStepOne" resultMap="StepOne"> select * from student where id = #{sid} </select>    //分布查询的桥梁 <resultMap id="StepOne" type="pojo.Student"> <id property="id" column="id"></id> <result property="name" column="name"></result> <result property="age" column="age"></result> <association property="teacher" select="mapper.StudentMapper.selectStudentAndTeacherStepTwo" column="tid"> </association> </resultMap>    //第二步查询 <select id="selectStudentAndTeacherStepTwo" resultType="pojo.Teacher"> select * from teacher where id = #{tid} </select>
   @Test
    public void selectSandTByTwoStep(){
        SqlSession session = this.getConn();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        System.out.println(mapper.selectStudentAndTeacherStepOne(2));
    }

association标签作为分布查询连接的桥梁,它的设置最为重要,注意点如下:

1.property值为需要映射表信息的对象名。我们第二布查询的目的是查询teacher表对应信息赋值给Student的teacher对象,所以值为teacher。

2.select:设置分布查询第二步的SQL的唯一标识,值为namespace.SQLId,也就是mapper接口全类名.方法名。设置这个的目的是告诉mybatis,分布查询的第二步要去执行哪条SQL语句,所以是第二次查询SQL的唯一标识id。

3.column:设置分布查询第二部的查询条件。我们需要根据student信息中的tid,作为第二步查询的入参,也就是说第二步查询的条件是id=#{tid},所以该值为tid。

 

 

 

分步查询的优点:可以实现延迟加载(懒加载)。

但是必须在核心配置文件中设置全局配置信息:

lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载

aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。

如果配置了全局的按需加载,又有某处特殊的需求需要延迟加载,此时可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"。

 

 

 

 

一对多关系映射问题

前面是学生对于老师是多对一关系,如果现在改成老师对应学生就变成了一对多关系。我们只需要明白映射关系中,对一用对象接收,对多用集合接收,所以在Teacher类中我们用List集合来存放Student:

处理一对多关系我们有两种方式:

a>collection标签

collection顾名思义就是集合了,所以用collection标签来处理一对多中集合的映射再合适不过了,需要注意的就是ofType:设置collection标签所处理的集合属性中存储数据的类型

    <select id="selectTeacherAndStudentsById" resultMap="TeacherAndStudentResultMap">
        SELECT t.id id1,t.name name1,s.id id2,s.name name2,s.age
        FROM teacher t
        LEFT JOIN student s 
        ON t.id = s.`tid` 
        WHERE t.`id` = #{tid};
    </select>

    <resultMap id="TeacherAndStudentResultMap" type="pojo.Teacher">
        <id column="id1" property="id"></id>
        <result column="name1" property="name"></result>
        <collection property="studentList" ofType="pojo.Student">
            <id column="id2" property="id"></id>
            <result column="name2" property="name"></result>
            <result column="age" property="age"></result>
        </collection>
    </resultMap>
结果:Teacher(id=1, name=老师1, studentList=[Student(id=1, name=学生1, age=20, teacher=null), Student(id=3, name=学生3, age=20, teacher=null)])

 

b>分布查询

    /**
     * 分布查询第一步,先查老师部分的信息
     * @param tid
     * @return
     */
    public Teacher selectTeacherAndStudentsStepOne(@Param("tid") Integer tid);

    /**
     * 分布查询第二部,根据老师id查询学生list信息
     * @param id
     * @return
     */
    public List<Student> selectTeacherAndStudentsStepTwo(@Param("id") Integer id);
    <select id="selectTeacherAndStudentsStepOne" resultMap="TeacherAndStudentsResultMapByStep">
        select * from teacher where id = #{tid}
    </select>
    
    <resultMap id="TeacherAndStudentsResultMapByStep" type="pojo.Teacher">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <collection property="studentList"
                    select="mapper.TeacherMapper.selectTeacherAndStudentsStepTwo"
                    column="id">
        </collection>
    </resultMap>

    <select id="selectTeacherAndStudentsStepTwo" resultType="pojo.Student">
        select * from student where tid = #{id}
    </select>
结果:Teacher(id=1, name=老师1, studentList=[Student(id=1, name=学生1, age=20, teacher=null), Student(id=3, name=学生3, age=20, teacher=null)])

这次分布查询的桥梁时collection标签,其规则和association标签一样

 

posted @ 2023-08-24 13:36  去公司搞点薯条  阅读(477)  评论(0)    收藏  举报