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 一对一联系