10.1 MyBatis关联映射
什么是关联关系
客观世界中的对象很少有孤立存在的,例如班级,往往与班级的学生存在关联关系,如果得到某个班级的实例,那么应该可以直接获取班级对应的全部学生。反过来,如果已经得到一个学生的实例,那么也应该可以访问该学生对应的班级。这种实例之间的互相访问就是关联关系。
关联关系分类
关联关系是面向对象分析、面向对象设计最重要的知识,MyBatis
完全可以理解这种关联关系,如果映射得当,MyBatis
的关联映射将可以大大简化持久层数据的访问。关联关系大致有如下分类。
10.1.1 一对一联系
在实际项目开发中,经常存在一对一关系,比如一个人只能有一个身份证,一个身份证只能给一个人使用,这就是一对一的关系。一对一关系推荐使用唯一主外键关联,即两张表使用外键关联关系,由于是一对一关联,因此还需要给外键列增加unique
唯约束。下面我们就用一个示例来看看MyBatis
怎么处理一对一关系。
示例: OneToOneTest
创建数据库表
首先,给之前创建的mybatis
数据库创建两个表tb_card
和tb_person
,并插入测试数据。SQL
脚本如下
使用方式:保存成.sql
文件,然后使用Navicat
导入(设置编码格式utf-8
,免得乱码).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| DROP TABLE IF EXISTS `tb_card`; CREATE TABLE `tb_card` ( `id` int(11) NOT NULL AUTO_INCREMENT, `code` varchar(18) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `tb_card` VALUES ('1', '43280119980091');
DROP TABLE IF EXISTS `tb_person`; CREATE TABLE `tb_person` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(18) NOT NULL, `sex` varchar(18) NOT NULL, `age` int(11) DEFAULT NULL, `card_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `card_id` (`card_id`), CONSTRAINT `tb_person_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `tb_card` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `tb_person` VALUES ('1', '小明', '男', '22', '1');
|
tb_person
表的card_id
作为外键参照tb_card
表的主键id
,因为是一对一关系,即一个card_id
只能让一个person
使用,所以把card_id
做成了唯一键约束。如此一来,person
使用了一个card_id
之后,其他的person
就不能使用该card_id
了.
所以,一对一关系就是外键约束
加上唯一约束
在mybatis
数据库中执行SQL
脚本,完成创建数据库和表的操作。
创建持久化类
接下来,创建一个Card
对象和一个Person
对象分别映射tb_card
和tb_peson
表
Card.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Card implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String code; public Card() { super(); } @Override public String toString() { return "Card [id=" + id + ", code=" + code + "]"; } }
|
Person.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Person implements Serializable { private static final long serialVersionUID = 1L; private Integer id; private String name; private String sex; private Integer age; private Card card; public Person() { super(); } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]"; } }
|
人和身份证号码之间是一对一的关系,即一个人只有一个身份证。在Person
类中定义了一个card
属性,该属性是一个card
类型,用来映射一对一的关联关系,表示这个人的身份证。
XML映射文件
再接下来是XML
映射文件。
PersonMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.fkit.mapper.PersonMapper"> <select id="selectPersonById" parameterType="int" resultMap="personMapper"> SELECT * from tb_person where id = #{id} </select> <resultMap type="org.fkit.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 property="card" column="card_id" select="org.fkit.mapper.CardMapper.selectCardById" javaType="org.fkit.domain.Card"/> </resultMap> </mapper>
|
在PersonMapper.xml
中定义了一个select
标签,其根据id
查询Peson
信息,由于Peson
类除了简单的属性id
、name
、sex
和age
之外,还有一个关联对象card
,所以返回的是一个名为personMapper
的resultMap
。personMapper
中使用了association
元素映射一对一的关联关系,select
属性表示会使用column
属性的card_id
值作为参数执行CardMapper.xml
中定义的selectCardById
查询对应的card
数据.查询出的数据将被封装到property
表示的card
对象当中.
CardMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.fkit.mapper.CardMapper"> <select id="selectCardById" parameterType="int" resultType="org.fkit.domain.Card"> SELECT * from tb_card where id = #{id} </select> </mapper>
|
mapper接口
之前的测试都是使用SqlSession
对象调用insert
、update
、delete
和select
方法进行测试。实际上,Mybatis
官方手册建议通过mapper
接口的代理对象访问Mybatis
,该对象关联了SqlSession
对象,开发者可以通过mapper
接口的代理对象直接调用方法操作数据库。
下面定义一个mapper
接口对象,需要注意的是:
mapper
接口对象的类名必须和之前的XML
文件中的mapper
的namespace
一致,
而方法名
必须和XML
文件中的select
元素的id
属性一致,参数
必须和XML
文件中的select
元素的parameterType
属性一致。
1 2 3 4 5 6 7 8 9
| public interface PersonMapper {
Person selectPersonById(Integer id); }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class OneToOneTest { public static void main(String[] args) { SqlSession sqlSession = null; try { sqlSession = FKSqlSessionFactory.getSqlSession(); PersonMapper pm =sqlSession.getMapper(PersonMapper.class); Person p = pm.selectPersonById(1); System.out.println(p); System.out.println(p.getCard()); sqlSession.commit(); } catch (Exception e) { sqlSession.rollback(); e.printStackTrace(); } finally { if (sqlSession != null) sqlSession.close(); } } }
|
运行OneToOneTest
类的main
方法,通过SqlSession
的getMapper(Class<T> type)
方法获得mapper
接口的代理对象PersonMapper
,调用selectPersonById
方法时会执行PersonMapper.xml
中id="selectPersonById"
的select
元素中定义的SQL
语句。控制台显示如下:
1 2 3 4 5 6 7 8
| DEBUG [main] ==> Preparing: SELECT * from tb_person where id = ? DEBUG [main] ==> Parameters: 1(Integer) DEBUG [main] ====> Preparing: SELECT * from tb_card where id = ? DEBUG [main] ====> Parameters: 1(Integer) DEBUG [main] <==== Total: 1 DEBUG [main] <== Total: 1 Person [id=1, name=小明, sex=男, age=22] Card [id=1, code=43280119980091]
|
可以看到,查询Person
信息时Person
对应的Card
对象也被查询出来了。
原文链接: 10.1 MyBatis关联映射 10.1.1 一对一联系