映射关系一对一
2023年11月16日大约 9 分钟约 1830 字
官方文档
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
基本介绍
项目中 1 对 1 的关系是一个基本的映射关系,比如:Person(人) --- IDCard(身份证)。
映射方式
- 通过配置 XxxMapper.xml 实现 1 对 1 [配置方式]
- 通过注解的方式实现 1 对 1 [注解方式]
配置 Mapper.xml 方式实现
方式1
通过配置 XxxMapper.xml 的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到对应的 idencard 信息。
创建 person表和 idencard表
CREATE TABLE person(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32) NOT NULL DEFAULT '',
card_id INT , -- 对应idencard的主键id
FOREIGN KEY (card_id) REFERENCES idencard(id)
)CHARSET utf8;
-- 创建 mybatis_idencard 表
CREATE TABLE idencard (
id INT PRIMARY KEY AUTO_INCREMENT,
card_sn VARCHAR(32) NOT NULL DEFAULT ''
)CHARSET utf8 ;
INSERT INTO idencard VALUES(1,'111111111111110');
INSERT INTO person VALUES(1,'张三',1);
创建新的 module(mybatis-mapping),相关配置文件可以从上一个 module 拷贝

创建 entity
package com.lzw.entiy;
/**
* @author LiAng
*/
public class IdenCard {
private Integer id;
private String card_sn;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCard_sn() {
return card_sn;
}
public void setCard_sn(String card_sn) {
this.card_sn = card_sn;
}
@Override
public String toString() {
return "IdenCard{" +
"id=" + id +
", card_sn='" + card_sn + '\'' +
'}';
}
}
//==================================================================
package com.lzw.entiy;
/**
* @author LiAng
*/
public class Person {
private Integer id;
private String name;
//因为需要实现一个级联操作,一个人需要对应一个身份证
//这里需要直接定义IdenCard对象属性
private IdenCard card;
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 IdenCard getCard() {
return card;
}
public void setCard(IdenCard card) {
this.card = card;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", card=" + card +
'}';
}
}
创建 IdenCardMapper.java
package com.lzw.mapper;
import com.lzw.entiy.IdenCard;
/**
* @author LiAng
*/
public interface IdenCardMapper {
//根据id获取到身份证序列号
public IdenCard getIdenCardById(Integer id);
}
创建 IdenCardMapper.xml
<?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="com.lzw.mapper.IdenCardMapper">
<!--
1、配置/实现//根据id获取到身份证序列号
2、public IdenCard getIdenCardById(Integer id);
-->
<select id="getIdenCardById" parameterType="Integer" resultType="IdenCard">
select * from `idencard` where `id` = #{id}
</select>
</mapper>
创建 PersonMapper.java
package com.lzw.mapper;
import com.lzw.entity.Person;
/**
* @author LiAng
*/
public interface PersonMapper {
//通过Person的id获取到Person,包括这个Person关联的IdenCard对象[级联查询]
public Person getPersonById(Integer id);
}
创建 PersonMapper.xml
<?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="com.lzw.mapper.PersonMapper">
<!--
1、配置/实现public Person getPersonById(Integer id);
2、完成通过Person的id获取到Person,包括这个Person关联的IdenCard对象[级联查询]
3. 如果配置成 resultType="Person" 问题就是没有实现级联查询
4. 自定义resultMap 搞定 映射返回的结果
5. 因为 getPersonById 最终返回的是 Person对象[只是有级联的对象属性], type仍然配置"Person"
-->
<resultMap id="PersonResultMap" type="Person">
<!--<result property="id" column="id"/>-->
<!--id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
1.property="id" 表示person 属性 id ,通常是主键
2.column="id" 表示对应表的字段
-->
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--association – 一个复杂类型的关联
1. property="card" 表示 Person对象的 card 属性
2. javaType="IdenCard" 表示card 属性 的类型
3. column="id" 是从我们的 下面这个语句查询后返回的字段
SELECT * FROM `person`,`idencard` WHERE `person`.id=1
AND `person`.card_id = `idencard`.id
-->
<association property="card" javaType="IdenCard">
<result property="id" column="id"/>
<result property="card_sn" column="card_sn"/>
</association>
</resultMap>
<select id="getPersonById" parameterType="Integer" resultMap="PersonResultMap">
SELECT * FROM `person` ,`idencard` WHERE `person`.id = #{id} AND `person`.card_id = `idencard`.id
</select>
</mapper>
创建 PersonMapperTest.java
package com.lzw.mapper;
import com.lzw.entity.Person;
import com.lzw.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
/**
* @author LiAng
*/
public class PersonMapperTest {
private SqlSession sqlSession;
private PersonMapper personMapper;
//初始化
@Before
public void init() {
sqlSession = MyBatisUtils.getSqlSession();
personMapper = sqlSession.getMapper(PersonMapper.class);
}
@Test
public void getPersonById(){
Person person = personMapper.getPersonById(1);
System.out.println("person-" + person);
if(sqlSession != null){
sqlSession.close();
}
}
}

方式2
通过配置 XxxMapper.xml 的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到对应的 idencard 信息。
修改 PersonMapper.java
//通过Person的id获取到Person,包括这个Person关联的IdenCard对象,方式2
public Person getPersonById2(Integer id);
修改 PersonMapper.xml
<!--
1、通过Person的id获取到Person,包括这个Person关联的IdenCard对象,方式2
2、public Person getPersonById2(Integer id);
3. 这里的方式和前面不同.
1) 先通过 SELECT * FROM `person` WHERE `id` = #{id} 返回 person信息
2) 再通过 返回的card_id 值,再执行操作,得到IdenCard 数据
-->
<resultMap id="PersonResultMap2" type="Person">
<id property="id" column="id"/>
<result property="name" column="name"/>
<!--
1. mybatis第二种方式核心思想: 将这个多表联查,分解成单表操作 , 这样简洁,而且易于维护 ,推荐
2. 而且可以复用你已经写好的方法 -组合
3. property="card": 表示 Person对象的 card 属性
4. column="card_id" 这个是
SELECT * FROM `person` WHERE `id` = #{id} 返回的 字段 card_id 信息/数据
5. 返回的 字段 card_id 信息/数据 作为getIdenCardById入参, 来执行
-->
<association property="card" column="card_id" select="com.lzw.mapper.IdenCardMapper.getIdenCardById" />
</resultMap>
<select id="getPersonById2" parameterType="Integer" resultMap="PersonResultMap2">
select * from `person` where `id` = #{id}
</select>
修改 PersonMapperTest.java
@Test
public void getPersonById2(){
Person person = personMapper.getPersonById2(1);
System.out.println("person--" + person);
if(sqlSession != null){
sqlSession.commit();
}
}

注解方式实现
通过注解的方式来实现下面的 1 对 1 的映射关系,实现级联查询,通过 person 可以获取到对应的 idencard 信息。
在实际开发中还是推荐使用配置方式。
创建 IdenCardMapperAnnotation.java
package com.lzw.mapper;
import com.lzw.entity.IdenCard;
import org.apache.ibatis.annotations.Select;
/**
* @author LiAng
* 使用注解方式实现1对1的映射
*/
public interface IdenCardMapperAnnotation {
//根据 id 获取到身份证
// 这个方法不需要返回任何级联对象
@Select("SELECT * FROM idencard WHERE id=#{id}")
public IdenCard getIdenCardById(Integer id);
}
创建 PersonMapperAnnotation.java
package com.lzw.mapper;
import com.lzw.entity.Person;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
/**
* @author LiAng
*/
public interface PersonMapperAnnotation {
//注解的形式就是对前面xml配置方式的体现
@Select("select * from `person` where `id` = #{id}")
@Results({
@Result(id = true, property = "id", column = "id"),
@Result(property = "name", column = "name"),
@Result(property = "card", column = "card_id",
one = @One(select = "com.lzw.mapper.IdenCardMapper.getIdenCardById"))
})
public Person getPersonById(Integer id);
}
创建 PersonMapperAnnotationTest.java
package com.lzw.mapper;
import com.lzw.entity.Person;
import com.lzw.util.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
/**
* @author LiAng
*/
public class PersonMapperAnnotationTest {
private SqlSession sqlSession;
private PersonMapperAnnotation personMapperAnnotation;
//初始化
@Before
public void init() {
sqlSession = MyBatisUtils.getSqlSession();
personMapperAnnotation = sqlSession.getMapper(PersonMapperAnnotation.class);
}
@Test
public void getPersonById(){
Person person = personMapperAnnotation.getPersonById(1);
System.out.println("person注解-" + person);
if(sqlSession != null){
sqlSession.close();
}
}
}

注意事项和细节
表是否设置外键,对 MyBatis 进行对象/级联映射没有影响。
作业
求通过查询 IdenCard,也可以级联查询到 Person。
修改 IdenCard.java
加入下列内容
//通过查询IdenCard可以级联查询得到 Person
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
@Override
public String toString() {
return "IdenCard{" +
"id=" + id +
", card_sn='" + card_sn + '\'' +
", person=" + person +
'}';
}
修改 IdenCardMapper.java
//根据id获取到身份证序列号, 并查询级联的person
public IdenCard getIdenCardById2(Integer id);
修改 IdenCardMapper.xml
<!--
1、根据id获取到身份证序列号, 并查询级联的person
2、public IdenCard getIdenCardById2(Integer id);
3. 自定义一个resultMap , 完成属性值映射
-->
<resultMap id="IdenCardResultMap" type="IdenCard">
<id property="id" column="id"/>
<result property="card_sn" column="card_sn"/>
<association property="person" column="id" select="com.lzw.mapper.PersonMapper.getPersonByCardId"/>
</resultMap>
<select id="getIdenCardById2" parameterType="Integer" resultMap="IdenCardResultMap">
select * from `idencard` where id = #{id}
</select>
修改 PersonMapper.java
//编写方法,通过card_id 查询得到person对象/数据
public Person getPersonByCardId(Integer cardId);
修改 PersonMapper.xml
<!--
1、//编写方法,通过card_id 查询得到person对象/数据
2、public Person getPersonByCardId(Integer cardId);
-->
<select id="getPersonByCardId" parameterType="Integer" resultType="Person">
select * from `person` where `card_id`=#{card_id}
</select>
修改IdenMapperTest.java
@Test
public void getIdenCardById2(){
IdenCard idenCard = idenCardMapper.getIdenCardById2(1);
System.out.println("idenCard2-" + idenCard);
if(sqlSession != null){
sqlSession.commit();
}
}
