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语句。