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语句。
感谢分享,同时希望你们可以好好维护这个网站,发表的文章需要进行一些勘误。以上SQL语句出错,另外:网站UI可以优化一下。加油!
好的。谢谢提醒。
祝你们的网站越来越好,发表的文章质量逐步提高。作最好的mybatis中国分站。
谢谢,我们会努力的
如果我在selectCardById接口上面添加@Cacheable注解对当前数据使用redis缓存,多对一的情况下为了让一能够更高效的查询,此时无效!我如果使用redisCache替换MyBatis默认的二级缓存是可以实现复用的的,但是涉及到两个问题,一个是序列化问题,另一个是生成的key太恶心了,毫无规则,不方便维护,请教一下站长有啥解决方案嘛