MyBatis教程大全


 MyBatis SQL 映射

     MyBatis select 标签

     MyBatis 多数据库支持

     MyBatis selectKey 标签作用

     MyBatis @SelectKey注解用法介绍

     MyBatis @SelectKey注解用法详细介绍

     MyBatis keyProperty 属性介绍

     MyBatis insert、update 和 delete 元素

     MyBatis sql 元素

 MyBatis SQL 参数映射

     MyBatis SQL 参数映射

 MyBatis 动态SQL

     MyBatis 动态SQL与数据准备

     MyBatis if 标签

     MyBatis if else 用法

     MyBatis choose、when、otherwise 标签

     MyBatis where 标签

     MyBatis set 标签

     MyBatis foreach 标签

     MyBatis bind 标签

     MyBatis trim 标签

 MyBatis SQL 结果映射

 MyBatis SQL 结果之关系映射

 MyBatis 使用介绍

     MyBatis typeAliases 类型别名

     MyBatis typeHandlers 类型处理器

     MyBatis Transaction 事务接口

     MyBatis transactionManager 事务管理

     SqlSessionFactory 介绍

     MyBatis 核心对象 SqlSession

     MyBatis 初始化 创建 Session 实例

     MyBatis ObjectFactory 对象工厂

     MyBatis缓存机制:一级缓存和二级缓存

     MyBatis 常用注解

 MyBatis 配置文件

     MyBatis 配置文件

 MyBatis 映射

     MyBatis 映射简介

     MyBatis ResultMap 映射

     MyBatis 自动映射

     MyBatis 高级映射

     MyBatis 集合映射

     MyBatis 关联映射

     MyBatis 一对一关联映射

     MyBatis 一对多关联映射

     MyBatis 多对多关联映射

     MyBatis 一对一(注解形式)

     MyBatis 一对多(注解形式)

     MyBatis 多对多(注解形式)

     MyBatis resultMap 元素

 MyBatis 面试题库

     #{}和${}的区别是什么?

     数据库链接中断如何处理?

     数据库插入重复如何处理?

     事务执行过程中宕机的应对处理方式

     Java客户端中的一个Connection问题

MyBatis的一对一关联关系

本文修订日期: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语句。