本文修订日期:2019年11月25日

SQL脚本

在实际项目开发中,经常存在一对一关系,比如一个人只能有一个身份证,一个身份证只能给一个人使用,这就是一对一的关系。一对一关系推荐使用唯一主外键关联,即两张表使用外键关联,由于是一对一关联,因此还需要给外键列增加unique唯一约束。下面我们就用一个简单示例来看看MyBatis怎么处理一对一关系。

首先,在数据库创建两个表tb_card和tb_person,并插入测试数据。SQL 脚本如下:

  • drop table if exists tb_card;
  • create table tb_card (
  • id int primary key auto_increment,
  • code varchar(18)
  • );
  • insert into tb_card(code) values ('432801198009191038');
  • drop table if exists tb_person;
  • create table tb_person (
  • id int primary key auto_increment,
  • name varchar(18),
  • sex varchar(18),
  • age int,
  • card_id int unique,
  • foreign key(card_id) peferences tb_card(id)
  • );
  • insert into tb_person(name, sex, age, card_id) values ('jack','男',23,1) ;

提示:
tb_person表的card_id作为外键,其参照tb_card表的主键id,因为是一对一关系,即一个card只能让一个person使用,所以card_id做成了唯一键约束。如此一来,当一个person使用了一个card之后,其他的person就不能使用该card了。

实体类

接下来,创建一个Card对象和一个Person对象分别映射tb_card和tb_person表。

  • package cn.mybatis.mydemo.domain;
  • import java.io.Serializable;
  • public class Card implements Serializable
  • {
  • private static final long serialVersionUID = 1L;
  • private Integer id; // 主键id
  • private String code; // 身份证编号
  • public Card()
  • {
  • super();
  • }
  • public Integer getId()
  • {
  • return id;
  • }
  • public void setId(Integer id)
  • {
  • this.id = id;
  • }
  • public String getCode()
  • {
  • return code;
  • }
  • public void setCode(String code)
  • {
  • this.code = code;
  • }
  • @Override
  • public String toString()
  • {
  • return "Card [id=" + id + ", code=" + code + "]";
  • }
  • }
  • package cn.mybatis.mydemo.domain;
  • import java.io.Serializable;
  • public class Person implements Serializable
  • {
  • private static final long serialVersionUID = 1L;
  • private Integer id; // 主键id
  • private String name; // 姓名
  • private String sex; // 性别
  • private Integer age; // 年龄
  • // 人和身份证是一对一的关系,即一个人只有一个身份证
  • private Card card;
  • public Person()
  • {
  • super();
  • }
  • public Integer getId()
  • {
  • return id;
  • }
  • public void setId(Integer id)
  • {
  • this.id = id;
  • }
  • public String getName()
  • {
  • return name;
  • }
  • public void setName(String name)
  • {
  • this.name = name;
  • }
  • public String getSex()
  • {
  • return sex;
  • }
  • public void setSex(String sex)
  • {
  • this.sex = sex;
  • }
  • public Integer getAge()
  • {
  • return age;
  • }
  • public void setAge(Integer age)
  • {
  • this.age = age;
  • }
  • public Card getCard()
  • {
  • return card;
  • }
  • public void setCard(Card card)
  • {
  • this.card = card;
  • }
  • @Override
  • public String toString()
  • {
  • return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
  • }
  • }

映射文件

人和身份证是一对一的关系,即一个人只有一个身份证。在Person类中定义了一个card属性,该属性是一个Card类型,用来映射一对一的关联关系,表示这个人的身份证。接下来是XML映射文件。

  • <mapper namespace="cn.mybatis.mydemo.mapper.CardMapper">
  • <!-- 根据id查询Card,返回Card对象 -->
  • <select id="selectCardById" parameterType="int" resultType="cn.mybatis.mydemo.domain.Card">
  • select * from tb_card where id = #{id}
  • </select>
  • </mapper>
  • <mapper namespace="cn.mybatis.mydemo.mapper.PersonMapper">
  • <!-- 根据id查询Person,返回resultMap -->
  • <select id="selectPersonById" parameterType="int" resultMap="personMapper">
  • select * from tb_person where id = #{id}
  • </select>
  • <!-- 映射Peson对象的resultMap -->
  • <resultMap type="cn.mybatis.mydemo.domain.Person" id="personMapper">
  • <id property="id" column="id" />
  • <result property="name" column="name" />
  • <result property="sex" column="sex" />
  • <result property="age" column="age" />
  • <!-- 一对一关联映射:association -->
  • <association property="card" column="card_id"
  • select="cn.mybatis.mydemo.mapper.CardMapper.selectCardById"
  • javaType="cn.mybatis.mydemo.domain.Card" />
  • </resultMap>
  • </mapper>

在PersonMapper.xml中定义了一个<select.../>,其根据id查询Person信息,由于Person类除了简单的属性id、name、sex和age之外,还有一个关联对象card,所以返回的是一个名为personMapper的resultMap。personMapper中使用了<association .../>元素映射一对一的关联关系。其中,select属性表示会使用column属性的card_id值作为参数执行CardMapper中定义的selectCardById语句,查询对应的Card数据,查询出的数据将被封装到property表示的card对象当中。

映射接口

我们通常都是使用SqlSession对象调用insert,update,delete和select方法进行测试。实际上,Mybatis官方手册建议通过mapper接口的代理对象访问mybatis,该对象关联了SqlSession对象,开发者可以通过该对象直接调用方法操作数据库。

下面定义一个mapper接口对象,需要注意的是,mapper接口对象的类名必须和之前的XML文件中的mapper的namespace一致,而方法名和参数也必须和XML文件中的<select.../>元素的id属性和parameterType属性一致。

  • package cn.mybatis.mydemo.mapper;
  • import cn.mybatis.mydemo.domain.Person;
  • public interface PersonMapper
  • {
  • /**
  • * 根据id查询Person,方法名和参数必须和XML文件中的<select.../>元素的id属性和parameterType属性一致
  • * */
  • Person selectPersonById(Integer id);
  • }

测试类

  • public class App
  • {
  • public static void main(String[] args) throws Exception
  • {
  • // 读取mybatis-config.xml文件
  • InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
  • // 初始化mybatis,创建SqlSessionFactory类的实例
  • SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  • // 创建Session实例
  • SqlSession session = sqlSessionFactory.openSession();
  • Person p = session.selectOne("cn.mybatis.mydemo.mapper.PersonMapper.selectPersonById", 1);
  • System.out.println(p);
  • System.out.println(p.getCard().getCode());
  • // 获得mapper接口的代理对象
  • PersonMapper pm = session.getMapper(PersonMapper.class);
  • // 直接调用接口的方法,查询id为1的Peson数据
  • Person p2 = pm.selectPersonById(1);
  • // 打印Peson对象
  • System.out.println(p2);
  • // 打印Person对象关联的Card对象
  • System.out.println(p2.getCard());
  • // 提交事务
  • session.commit();
  • // 关闭Session
  • session.close();
  • }
  • }

运行APP类的main方法,通过SqlSession的getMapper(Class<?> type)方法获得mapper接口的代理对象PersonMapper,调用selectPersonByld方法时会执行PersonMapper.xml中<select.../>元素中定义的sql语句。

标签: none

已有 5 条评论

  1. 互联网微尘 互联网微尘

    感谢分享,同时希望你们可以好好维护这个网站,发表的文章需要进行一些勘误。以上SQL语句出错,另外:网站UI可以优化一下。加油!

    1. 好的。谢谢提醒。

  2. 互联网微尘 互联网微尘

    祝你们的网站越来越好,发表的文章质量逐步提高。作最好的mybatis中国分站。

    1. 谢谢,我们会努力的

  3. 王聪 王聪

    如果我在selectCardById接口上面添加@Cacheable注解对当前数据使用redis缓存,多对一的情况下为了让一能够更高效的查询,此时无效!我如果使用redisCache替换MyBatis默认的二级缓存是可以实现复用的的,但是涉及到两个问题,一个是序列化问题,另一个是生成的key太恶心了,毫无规则,不方便维护,请教一下站长有啥解决方案嘛

添加新评论