喵星软件园提供热门手机游戏下载,最新手机游戏攻略!

《Super Auto Pets》四本食物一览,

时间:2023-10-03 22:45:44 来源: 浏览:

MyBatis的关联映射

实际的开发中,对数据库的操作常常会涉及到多张表,这在面向对象中就涉及到了对象与对象之间的关联关系。针对多表之间的操作,MyBatis提供了关联映射,通过关联映射就可以很好的处理对象与对象之间的关联关系。

1.关联关系概述

在关系型数据库中,多表之间存在着三种关系,分别是一对一,一对多,多对多。

  • 一对一:在任意一个表中引入另外一个表的主键作为外键。
  • 一对多:在多个表中都引入了某一个表的主键作为外键
  • 多对多:需要用一张中间表表示多对多的关系,这张中间表引入两张表的主键作为外键。

一般来说一个对象映射一张表,因此一对一的关系就是在A类中定义B类属性,一对多的关系就是在A类中定义List< B> 的属性,多对多就是分别在A、B类中定义对方的List 属性。

2.一对一

一对一关系是一个基本的映射关系,比如Person(人)--IDCard(身份证),我们可以通过如下两种方式实现:

  1. 通过配置XxxMapper.xml实现1对1 <配置方式>
  2. 通过注解的方式实现1对1 <注解方式>

2.1配置方式

2.1.1环境搭建

配置映射文件来实现一对一的映射关系,实现级联查询,要求通过person可以获取到对应的idencard信息

关于级联查询:若表A中有一个外键引用了表B的主键,A表就是子表,B表就是父表。当查询表A的数据时,通过表A的外键将表B的记录也查找出来,这就是级联查询。相应的还有级联删除,当删除B表的记录时,会先将A表中关联的记录删掉

(1)person表和 idencard表

-- 创建 idencard 表-- 记录身份证CREATE TABLE `idencard`(`id` INT PRIMARY KEY AUTO_INCREMENT,`card_sn` VARCHAR(32) NOT NULL DEFAULT '')CHARSET utf8;INSERT INTO `idencard` VALUES(1,'123456789098765');  -- 创建person表CREATE TABLE `person`(`id` INT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(32) NOT NULL DEFAULT '',`card_id` INT, -- 对应idencard表的主键-idFOREIGN KEY (`card_id`) REFERENCES idencard (`id`)-- card_id作为外键)CHARSET utf8;INSERT INTO `person` VALUES(1,'张三',1);

(2)实体类 IdenCard 和 Person

package com.li.entity; /** * @author 李 * @version 1.0 */public class IdenCard {    private Integer id;    private String card_sn;    //省略setter,getter,toString方法}
package com.li.entity; /** * @author 李 * @version 1.0 */public class Person {    private Integer id;    private String name;    private IdenCard card;    //省略setter,getter,toString方法}

(3)PersonMapper 接口

package com.li.mapper; import com.li.entity.Person; /** * @author 李 * @version 1.0 */public interface PersonMapper {    //通过Person的id获取到Person,包括这个Person关联的IdenCard对象(级联操作)    public Person getPersonById(Integer id);}

2.1.2方式1:多表联查

mybatis – MyBatis 3 | XML 映射器

PersonMapper.xml映射文件实现级联查询,实现方法是使用多表联查,返回的数据通过resultMap结果映射

<mapper namespace="com.li.mapper.PersonMapper">    <!--1.接口声明:public Person getPersonById(Integer id);        2.通过Person的id获取到Person,包括这个Person关联的IdenCard对象(级联操作)        3.返回类型如果配置成resultType="Person",不能实现级联查询,		在返回的person对象中IdenCard属性对象为 null        4.因此需要使用自定义resultMap,在resultMap中指定级联关系-->    <select id="getPersonById" parameterType="Integer" resultMap="PersonResultMap">        SELECT * FROM `person`,`idencard` WHERE `person`.`id`= #{id} AND        `person`.`card_id` = `idencard`.`id`;    </select>     <!--association – 一个复杂类型的关联;许多结果将包装成这种类型嵌套结果映射 – 关联可以是		resultMap 元素,或是对其它结果映射的引用    	1.property="card" 表示 Person对象的card属性    	2.javaType="IdenCard" 表示card属性的类型-->    <resultMap id="PersonResultMap" type="Person">        <!--id标签–一个ID结果(就是主键);标记出作为主键的结果可以帮助提高整体性能-->         <!--这里的property表示Person类的属性名,column表示对应表的字段-->        <id property="id" column="id"/>        <result property="name" column="name"/>        <association property="card" javaType="IdenCard">            <!--这里的property表示IdenCard类的属性名,column表示表的字段名-->            <result property="id" column="id"/>            <result property="card_sn" column="card_sn"/>        </association>    </resultMap></mapper>

测试:

@Testpublic void getPersonById() {    Person person = personMapper.getPersonById(1);    System.out.println("person=" + person);    if (sqlSession != null) {        sqlSession.close();    }}

2.2.3方式2:分解为多次单表操作(推荐使用)

第一种方式使用了多表联查的形式实现级联查询,但是如果涉及的表过多,sql语句可读性就会变差。第二种方式的核心思想是将多表联查分解成单表操作,这样更简洁,易于维护,而且可以复用你写好的方法,推荐使用

(1)创建IdenCardMapper接口

public interface IdenCardMapper {    //根据id获取到身份证序列号    public IdenCard getIdenCardById(Integer id);}

(2)在IdenCardMapper的映射文件中实现该方法

<mapper namespace="com.li.mapper.IdenCardMapper">    <!--配置实现public IdenCard getIdenCardById(Integer id);-->    <select id="getIdenCardById" parameterType="Integer" resultType="IdenCard">        SELECT * FROM `idencard` WHERE `id` = #{id}    </select></mapper>

(3)PersonMapper接口

//通过Person的id获取到Person,包括这个Person关联的IdenCard对象(方式2)public Person getPersonById2(Integer id);

(4)实现PersonMapper接口的映射文件

  1. 先通过 SELECT * FROM person WHERE id =#{id} 返回 person 信息  2. 以第一个操作返回的 card_id 字段数据,作为条件再次查询,得到对应的 IdenCard 数据

如果第一个操作使用了别名,那么返回的时候的字段也是别名,因此第二个操作也要使用别名才能匹配到

<!--通过Person的id获取到Person,包括这个Person关联的IdenCard对象(方式2)    接口方法:public Person getPersonById2(Integer id);--><resultMap id="PersonResultMap2" type="Person">    <id property="id" column="id"/>    <result property="name" column="name"/>    <!--第二种方式的核心思想是将多表联查操作分解成单表操作,    这样更简洁,易于维护,复用性更强,推荐使用-->    <!--1.property="card"表示Person对象的card属性        2.column="card_id"是SELECT * FROM person WHERE id = #{id}语句返回的card_id字段名/别名        3.返回的字段card_id信息/数据会作为getIdenCardById()的入参来执行方法-->    <association property="card" column="card_id"                 select="com.li.mapper.IdenCardMapper.getIdenCardById"/></resultMap><select id="getPersonById2" parameterType="Integer" resultMap="PersonResultMap2">    SELECT * FROM person WHERE id = #{id};</select>

测试结果:

可以看到底层执行了两次sql查询操作。首先对person表进行查询,查询结果(card_id)作为第二张表的查询条件(id),再对idencard表进行查询。

2.2注解方式

通过注解的方式来实现一对一的映射关系,实现级联查询,通过person可以获取到对应的idencard的信息。这里只进行方式二的演示。

在实际开发中还是推荐使用配置方式

(1)注解实现方法

IdenCardMapperAnnotation 接口:

package com.li.mapper; import com.li.entity.IdenCard;import org.apache.ibatis.annotations.Select; /** * @author 李 * @version 1.0 * 使用注解的方式实现一对一的映射 */public interface IdenCardMapperAnnotation {    //根据id获取到身份证序列号    @Select(value = "SELECT * FROM `idencard` WHERE `id` = #{id}")    public IdenCard getIdenCardById(Integer id);}

PersonMapperAnnotation 接口:

package com.li.mapper; import com.li.entity.Person;import org.apache.ibatis.annotations.One;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select; /** * @author 李 * @version 1.0 */public interface PersonMapperAnnotation {    //通过Person的id获取到Person,包括这个Person关联的IdenCard对象    //注解的形式就是对前面xml配置方式的体现    @Select(value = "SELECT * FROM person WHERE id = #{id}")//如果这里返回的字段使用了别名,则@result的card_id也要使用该别名    @Results({//配置返回数据的映射            @Result(id = true, property = "id", column = "id"),            @Result(property = "name", column = "name"),            @Result(property = "card", column = "card_id",                    one = @One(select = "com.li.mapper.IdenCardMapper.getIdenCardById"))    })    public Person getPersonById(Integer id);}

(2)测试

@Testpublic void getIdenCardById() {    Person person = personMapperAnnotation.getPersonById(1);    System.out.println("person=" + person);    if (sqlSession != null) {        sqlSession.close();    }}

2.3注意事项

一张表是否设置了外键,对MyBatis进行对象级联映射没有影响,外键只是对表本身数据的约束

2.4练习-1对1双向映射

前面我们讲解的是查询Person可以级联查询到IdenCard,如果要求通过查询IdenCard,也可以级联查询到Person,应该如何解决?


这实际上是Person和IdenCard的1对1的双向映射(1<=>1),解决办法除了传统的多表联查之外,仍然可以使用分解为多次单表操作的方式:

  1. 先通过 SELECT * FROM idencard WHERE id =#{id} 返回 IdenCard 信息  2. 以第一个操作返回的 id 字段数据,作为条件再次查询,得到对应的 person数据  3. 将结果进行映射,放在resultMap中

(1)修改IdenCard实体类,添加Person属性

package com.li.entity; public class IdenCard {    private Integer id;    private String card_sn;    private Person person;    //省略setter、getter、toString方法}

Person实体类不变。

(2)IdenCard接口

//练习-根据id获取到身份证序列号,同时返回对应的Person信息public IdenCard getIdenCardById2(Integer cardId);

(3)Person接口

//通过Person的card_id获取到Person信息public Person getPersonByCardId(Integer cardId);

(4)IdenCardMapper.xml

<resultMap id="IdenCardMap" type="IdenCard">    <id property="id" column="id"/>    <result property="card_sn" column="card_sn"/>    <!--1.property="person" 是指 IdenCard类的person属性名        2.column="id" 是指getIdenCardById2返回的id字段名作为第二次查询的card_id-->    <association property="person" column="id"                 select="com.li.mapper.PersonMapper.getPersonByCardId"/></resultMap> <!--练习-根据id获取到身份证序列号,同时返回对应的Person信息    public IdenCard getIdenCardById2(Integer id);--><select id="getIdenCardById2" parameterType="Integer" resultMap="IdenCardMap">    SELECT * FROM `idencard` WHERE `id` = #{id}</select>

(5)PersonMapper.xml

<!--练习-通过Person的card_id获取到Person--><select id="getPersonByCardId" parameterType="Integer" resultType="Person">    SELECT * FROM person WHERE card_id = #{cardId};</select>

(6)测试

@Testpublic void getIdenCardById2() {    IdenCard idenCard = idenCardMapper.getIdenCardById2(1);    System.out.println("idenCard=" + idenCard);}

3.一对多

3.1基本介绍

mybatis – MyBatis 3 | XML 映射器

多对一关系也是一个基本的映射关系,多对一,也可以理解为一对多。例如:

User--Pet:一个用户可以有多只宠物

Dep--Emp:一个部门有多个员工

双向的多对一关系:通过User可以查询到对应的所有Pet,反之,通过Pet也可以级联查询到对应的User信息。

多对多的关系就是在多对一的关系上拓展

3.2案例实现

映射方式:

方式1:通过配置映射文件实现多对一

方式2:通过注解的方式实现多对一

需求说明:实现级联查询,通过user的user_id可以查询到User信息和关联的所有pet信息,反之,通过pet的pet_id也可以查询到Pet信息和user的信息

先创建user表和pet表:

-- 创建user表CREATE TABLE `user`(`id` INT PRIMARY KEY AUTO_INCREMENT,`name` VARCHAR(32) NOT NULL DEFAULT '')CHARSET=utf8DESC `user`; -- 创建pet表CREATE TABLE `pet`(`id` INT PRIMARY KEY AUTO_INCREMENT,`nickname` VARCHAR(32) NOT NULL DEFAULT '',`user_id` INT,FOREIGN KEY (user_id) REFERENCES `user`(id))CHARSET=utf8

3.2.1方式一:配置方式

(1)User和Pet实体类

package com.li.entity; /** * @author 李 * @version 1.0 */public class User {    private Integer id;    private String name;    //因为一个User可以养多个宠物,mybatis使用集合体现这个关系    private List<Pet> pets;        //setter、getter方法省略    //双向映射不要使用toString方法,否则会造成栈溢出错误}
package com.li.entity; /** * @author 李 * @version 1.0 */public class Pet {    private Integer id;    private String nickname;    //一个pet对应一个user对象    private User user;    	//setter、getter方法省略    //双向映射不要使用toString方法,否则会造成栈溢出错误}

(2)UserMapper接口和PetMapper接口

public interface UserMapper {    //通过id获取User对象    public User getUserById(Integer id);}
public interface PetMapper {    //通过user的id获取pet对象,可能有多个因此使用集合接收    public List<Pet> getPetByUserId(Integer userId);}

(3)UserMapper.xml,思路:

1)先通过user_id查询得到user信息

2)再根据user_id,查询对应的pet信息,并映射到user-List< Per> pets

多对多的映射思路和一对一的实现类似,不同的使用使用resultMap映射属性时使用的是collecting标签。

<mapper namespace="com.li.mapper.UserMapper">    <!--通过id获取User对象        public User getUserById(Integer id);-->    <select id="getUserById" parameterType="Integer" resultMap="resultUserMap">        SELECT * FROM `user` WHERE id = #{id};    </select>     <!--User的属性映射-->    <resultMap id="resultUserMap" type="User">        <id property="id" column="id"/>        <result property="name" column="name"/>        <!--1.因为pets属性是一个集合,因此要使用collection标签            2.column="id"的id是SELECT * FROM `user` WHERE id=#{id} 返回的字段            3.ofType="Pet"指定返回的集合存放的数据类型-->        <collection property="pets" column="id" ofType="Pet"                    select="com.li.mapper.PetMapper.getPetByUserId"/>    </resultMap></mapper>

(4)PetMapper.xml,思路和前面大体相同

<mapper namespace="com.li.mapper.PetMapper">    <!--通过user的id获取pet对象,可能有多个因此使用集合接收        public List<Pet> getPetByUserId(Integer userId);-->    <select id="getPetByUserId" parameterType="Integer" resultMap="resultPetMap">        SELECT * FROM `pet` WHERE user_id =#{userId};    </select>     <resultMap id="resultPetMap" type="Pet">        <id property="id" column="id"/>        <result property="nickname" column="nickname"/>        <association property="user" column="user_id"                     select="com.li.mapper.UserMapper.getUserById"/>    </resultMap></mapper>

(5)测试getUserById()方法,通过UserId查找user对象和联系的pet信息

@Testpublic void getUserById() {    User user = userMapper.getUserById(2);    System.out.println("user信息=" + user.getId() + "-" + user.getName());    for (Pet pet : user.getPets()) {        System.out.println("宠物信息=" + pet.getId() + "-" + pet.getNickname());    }    if (sqlSession != null) {        sqlSession.close();    }}

测试结果:

(6)测试getPetByUserId()方法,通过user的id获取pet对象

@Testpublic void getPetByUserId() {    List<Pet> pets = petMapper.getPetByUserId(1);    for (Pet pet : pets) {        System.out.println("UserId=" + pet.getUser().getId()                + "-PetId=" + pet.getId()                + "-PetNickName=" + pet.getNickname());    }    if (sqlSession != null) {        sqlSession.close();    }}

测试结果:

resultMap可以复用,如果有其他方法是返回的和resultMap一样的类型,可以在实现该方法时引用该resultMap。

比如PetMapper接口中新声明了一个方法:

//通过pet的id获取Pet对象,同时查询到pet对象关联的user对象public Pet getPetById(Integer id);

PerMapper.xml文件:

<!--这里可以直接复用之前的resultPetMap--><select id="getPetById" parameterType="Integer" resultMap="resultPetMap">    SELECT * FROM `pet` where id =#{id};</select>

3.2.2方式二:注解方式

需求说明:通过注解的方式,实现双向的级联查询。

在实际开发中推荐使用配置的方式来做

(1)User和Pet实体类不变

(2)直接在接口中,通过注解实现级联查询

UserMapperAnnotation.java

package com.li.mapper; import com.li.entity.User;import org.apache.ibatis.annotations.*; /** * @author 李 * @version 1.0 * 以注解的方式来实现多对一 */public interface UserMapperAnnotation {    //通过id获取User对象    @Select(value = "SELECT * FROM `user` WHERE id = #{id}")    @Results({            @Result(id = true, property = "id", column = "id"),            @Result(property = "name", column = "name"),            //这里对应返回List类型属性pets,使用注解的many属性            @Result(property = "pets", column = "id",                    many = @Many(select =                                  "com.li.mapper.PetMapperAnnotation.getPetByUserId"))    })    public User getUserById(Integer id);}

PetMapperAnnotation.java

package com.li.mapper; import com.li.entity.Pet;import org.apache.ibatis.annotations.*; import java.util.List; /** * @author 李 * @version 1.0 */public interface PetMapperAnnotation {    //通过user的id获取pet对象    @Select(value = "SELECT * FROM `pet` WHERE user_id =#{userId}")    //配置了id之后就可以复用PetResuleMap    @Results(id = "PetResuleMap", value = {            @Result(id = true, property = "id", column = "id"),            @Result(property = "nickname", column = "nickname"),            @Result(property = "user", column = "user_id",                    one = @One(select =                                "com.li.mapper.UserMapperAnnotation.getUserById"))    })    public List<Pet> getPetByUserId(Integer userId);         //通过pet的id获取pet信息    @Select(value = " SELECT * FROM `pet` where id =#{id}")    @ResultMap("PetResuleMap")//复用上面的PetResuleMap    public Pet getPetById(Integer id);}

标题:《Super Auto Pets》四本食物一览,
链接:https://www.miaoshengapp.cn/yxgl/78536.html
版权:文章转载自网络,如有侵权,请联系删除!
资讯推荐
2023摩尔庄园手游7月10日神奇密码推荐
2023摩尔庄园手游7月10日神奇密码推荐

在摩尔庄园游戏中,玩家可以种植各种农作物和

2023-07-10
晶核克罗姆军工厂打法
晶核克罗姆军工厂打法

晶核克罗姆军工厂打法,晶核手游中我们每次完

2023-07-18
不良人3不良何在任务攻略
不良人3不良何在任务攻略

在不良人3游戏中会有很多轶事任务,不良何在

2022-12-23
一人之下手游御物输出手法 御物连招攻略
一人之下手游御物输出手法 御物连招攻略

一人之下御物的输出手法是什么样子的呢?其实

2020-07-11
保卫萝卜4周赛6月27日-保卫萝卜4周赛6月27日周赛攻略
保卫萝卜4周赛6月27日-保卫萝卜4周赛6月27日周赛攻略

保卫萝卜4周赛6月27日,保卫萝卜新的一周又开

2023-06-28
王者荣耀亚连最强出装 亚连最强铭文搭配推荐
王者荣耀亚连最强出装 亚连最强铭文搭配推荐

王者荣耀游戏里面设置了很多不同玩法的英雄

2023-06-20