Mybatis中使用association进行关联的几种方式

本文详细介绍了如何在MyBatis中使用XML和注解处理一对一关联查询,包括内连接、级联属性、association内联result设置、引用resultMap及select方式,同时提供了示例代码和扩展类的实现,涵盖了延迟加载等特性。
摘要由CSDN通过智能技术生成

系列文章目录

一对一单向关联的示例(含XML版本和注解版本)
一对多(以及多对一)的示例(含XML版本和注解版本)
多对多(以及多对一)的示例(含XML版本和注解版本)
自关联示例(含XML版本和注解版本)



前言

当使用 MyBatis 进行查询的时候如果一个 JavaBean 中包含另一个 JavaBean 或者 Collection 时,可以通过 MyBatis 的嵌套查询来获取需要的结果;
这里以一对一单向关联为例。对使用或不使用association的配置进行举例。


实体类

实体类代码如下(示例):

@Data
@ToString
@NoArgsConstructor
public class IdCard {
    private Integer id;
    private String number;
    private Date expiredTime;

    public IdCard(Integer id) {
        this.id = id;
    }
}

@Data
@ToString
@NoArgsConstructor
public class Person {
    protected Integer id;
    protected String name;
    protected IdCard idCard;

    public Person(Integer id) {
        this.id = id;
    }
}

使用内连接+级联属性(不使用association)

代码如下(示例):

<?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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />
        
        <!-- 一对一关联:单向; 方式:使用级联属性 -->
        <result property="idCard.id" column="cid"/>
        <result property="idCard.number" column="number"/>
        <result property="idCard.expiredTime" column="expired_time"/>
    </resultMap>
    <select id="selectById" parameterType="Integer" resultMap="PersonMap">
        select p.id id, p.name name,c.id cid,c.number
        number,c.expired_time expired_time from t_person p
        inner join t_idcard
        c on p.idcard_id=c.id and p.id=#{id}
    </select>
</mapper>


一、关联属性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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />
        
        <!-- 一对一关联:单向;方式一:使用扩展类,必须重写setter方式,并且父类必须将字段修改成protected,同时修改type。不推荐 -->
        <result property="cardId" column="cid" />
        <result property="cardNumber" column="number" />
        <result property="cardExpiredTime" column="expired_time" />
    </resultMap>
    <select id="selectById" parameterType="Integer" resultMap="PersonMap">
        select p.id id, p.name name,c.id cid,c.number
        number,c.expired_time expired_time from t_person p
        inner join t_idcard
        c on p.idcard_id=c.id and p.id=#{id}
    </select>
</mapper>

扩展类:

package com.sunwii.mybatis.beanresult;

import java.util.Date;
import com.sunwii.mybatis.bean.IdCard;
import com.sunwii.mybatis.bean.Person;

@SuppressWarnings("unused")
public class PersonResult extends Person{
    private Integer cardId;
    private String cardNumber;
    private Date cardExpiredTime;

    public PersonResult() {
        super();
        //即时实例化关联对象
        super.setIdCard(new IdCard());
    }

    public void setCardId(Integer cardId) {
        this.cardId = cardId;
        //设置
        super.getIdCard().setId(cardId);
    }

    public void setCardNumber(String cardNumber) {
        this.cardNumber = cardNumber;
        //设置
        super.getIdCard().setNumber(cardNumber);
    }
    
    public void setCardExpiredTime(Date cardExpiredTime) {
        this.cardExpiredTime = cardExpiredTime;
        //设置
        super.getIdCard().setExpiredTime(cardExpiredTime);
    }
}

方式二:使用内连接+association内联result设置

代码如下(示例):

<?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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />

        <!-- 一对一关联:单向;方式二:使用内联方式直接列出。 -->
        <association property="idCard" column="idcard_id" javaType="IdCard">
            <id column="cid" property="id" />
            <result column="number" property="number" />
            <result column="expired_time" property="expiredTime" />
        </association>
    </resultMap>
    <select id="selectById" parameterType="Integer"
        resultMap="PersonMap">
        select p.id id, p.name name,c.id cid,c.number
        number,c.expired_time expired_time from t_person p
        inner join t_idcard
        c on p.idcard_id=c.id and p.id=#{id}
    </select>
</mapper>

方式三:使用内连接+association引用resultMap

代码如下(示例):

<?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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />

        <!-- 一对一关联:单向;方式三:使用resultMap引用。
             注意的是column名称必须与关联表select时的一致(需要修改关联表的select,所以更建议使用select引用方式(见下) -->
        <association property="idCard" column="cid"
            resultMap="com.sunwii.mybatis.mapper.IdCardMapper.IdCardMap" />
    </resultMap>
    <select id="selectById" parameterType="Integer" resultMap="PersonMap">
        select p.id id, p.name name,c.id cid,c.number number,
        c.expired_time expired_time 
        from t_person p
        inner join t_idcard
        c on p.idcard_id=c.id and p.id=#{id}
    </select>
</mapper>

注意: 对于IdCardMapper,为配合方式三需要修改查询到的id属性为cid(即指定column=“cid”):

<mapper namespace="com.sunwii.mybatis.mapper.IdCardMapper">
    <resultMap type="IdCard" id="IdCardMap">
        <id property="id" column="cid" />
        <result property="number" column="number" />
        <result property="expiredTime" column="expired_time" />
    </resultMap>
    <select id="selectById" parameterType="Integer"
        resultMap="IdCardMap">
        select id as cid ,number,expired_time from t_idcard where id=#{id}
    </select>
</mapper>

方式四:使用单表查询+association引用select方式,不用inner查询(以避免再次查询),可以利用延迟加载

代码如下(示例):

<?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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />

        <!-- 一对一关联:单向;方式四:使用select引用,可以设置延迟加载方式 -->
        <association property="idCard" column="idcard_id"
            javaType="IdCard"
            select="com.sunwii.mybatis.mapper.IdCardMapper.selectById" fetchType="lazy"/>
    </resultMap>
    <select id="selectById" parameterType="Integer"
        resultMap="PersonMap">
        select id, name, idcard_id from t_person p where p.id=#{id}
    </select>
</mapper>


二、关联属性注解版本

一个一对一单向关联使用注解的例子:(采用方式五非join方式,延迟加载)

Mapper接口类:

@Mapper
public interface IdCardMapper {
    @Select("select id,number,expired_time from t_idcard where id=#{id}")
    @Results(id="IdCardMap", value = {
            @Result(property = "id", column = "id"),
            @Result(property = "number", column = "number"),
            @Result(property = "expiredTime", column = "expired_time"), 
    })
    public IdCard selectById(Integer id);

    @Insert("insert into t_idcard(number,expired_time) values(#{number},#{expiredTime}")
    @Options(keyColumn = "id",keyProperty = "id",useGeneratedKeys = true)
    public int insertIdCard(IdCard idCard);

    @Update("update t_idcard set number=#{number},expired_time=#{expiredTime}")
    public int updateIdCard(IdCard idCard);

    @Delete("delete from t_idcard where id=#{id}")
    public int deleteIdCard(IdCard idCard);
}
@Mapper
public interface PersonMapper {
    @Select("select id,name,idcard_id from t_person where id=#{id}")
    @Results(id="PersonMap",value = {
            @Result(property = "id",column = "id"),
            @Result(property = "name",column = "name"),
            @Result(property = "idCard",column = "idcard_id", 
            one=@One(select = "com.sunwii.mybatis.mapper.IdCardMapper.selectById",fetchType = FetchType.LAZY))
    })
    public Person selectById(Integer id);
    
    @Insert("insert into t_person(name,idcard_id) values(#{name},#{idCard.id})")
    @Options(keyColumn = "id",keyProperty = "id",useGeneratedKeys = true)
    public int insertPerson(Person person);
    
    // 使用动态sql
    @UpdateProvider(type = com.sunwii.mybatis.provider.PersonDynamicSqlProvider.class, method = "update")
    public int updatePerson(Person person);
    
    @Delete("delete from t_person where id=#{id}")
    public int deletePerson(Person person);
}

动态SQL支持:

public class PersonDynamicSqlProvider {
    public String update(Person person) {
        return new SQL() {
            {
                UPDATE("t_person");
                SET("name=#{name}");
                
                if(person.getIdCard()!=null) {
                    SET("idcard_id=#{idCard.id}");
                }
                
                WHERE("id=#{id}");
            }
        }.toString();
    }
}

业务层:

@Service
public class IdCardServiceImpl implements IdCardService{
    @Autowired
    private IdCardMapper idCardMapper;

    @Override
    public IdCard getIdCard(Integer id) {
        return idCardMapper.selectById(id);
    }

    @Override
    @Transactional
    public void updateIdCard(IdCard idCard) {
        idCardMapper.updateIdCard(idCard);
    }

    @Override
    @Transactional
    public void insertIdCard(IdCard idCard) {
        idCardMapper.insertIdCard(idCard);
    }
}
@Service
public class PersonServiceImpl implements PersonService {
    @Autowired
    private PersonMapper personMapper;

    @Autowired
    private IdCardMapper idCardMapper;

    @Override
    public Person getPerson(Integer id) {
        return personMapper.selectById(id);
    }

    @Override
    @Transactional
    public void insertPersion(Person person) {
        // 一对一单向关联:执行主表的插入前,先执行从被关联表的插入并获取其最新插入的主健
        // 由于插件后会自动更新关联实体的ID,所以这里不需要进行设置
        IdCard idCard = person.getIdCard();

        // 这种方式将对Person的属性设置时不进行setIdCart(),会将IdCard的插入延迟,可在后续进行补充的添加(不要求一定要有IdCard)。
        // 必须配合<if test="idCard != null">来操作
        if (idCard != null) {
            idCardMapper.insertIdCard(idCard);
        }

        personMapper.insertPerson(person);
    }

    @Override
    @Transactional
    public void updatePersion(Person person) {
        // 每次更新都要先更新被关联表,这样不行,必须独立到从表的更新去=>idCardService.updateIdCard(idCard)
        // IdCard idCard = person.getIdCard();
        // if (idCard != null && idCard.getId() != null) {
        // idCardMapper.updateIdCard(idCard);
        // }

        personMapper.updatePerson(person);

    }

    @Override
    @Transactional
    public void deletePersion(Person person) {
        // 一对于单向关联:执行主表的删除后,再执行被关联表的删除
        // 由于插件后会自动更新关联实体的ID,所以这里不需要进行设置
        personMapper.deletePerson(person);
        
        IdCard idCard = person.getIdCard();
        // 有IdCard则删除
        if (idCard != null) {
            idCardMapper.deleteIdCard(idCard);
        }
    }
}

SpringUtil工具类:

public class SpringUtil {
    private static ApplicationContext context = null; 
    static {
        context = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    public static ApplicationContext getContext() {
        return context;
    }
}

Mybatis配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <settings>
        <setting name="lazyLoadingEnabled" value="true" />
        <setting name="aggressiveLazyLoading" value="false" />
    </settings>
    
    <typeAliases>
        <package name="com.sunwii.mybatis.bean" />
    </typeAliases>
    
</configuration>

Spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
    http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

    <!-- 引入jdbcs配置文件 -->
    <context:property-placeholder
        location="classpath:jdbc.properties" />

    <!-- 数据库连接池 -->
    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${user}" />
        <property name="password" value="${password}" />
        <property name="maxActive" value="210" />
        <property name="maxIdle" value="50" />
    </bean>

    <!-- mybatis -->
    <bean id="sessionFactory"
        class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation"
            value="classpath:mybatis-config.xml" />
        <property name="mapperLocations"
            value="classpath:com/sunwii/mybatis/bean/*.xml" />
    </bean>

    <!-- Mapper动态代理开发扫描 -->
    <mybatis:scan base-package="com.sunwii.mybatis.mapper" />

    <!-- 事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 事务注解 -->
    <tx:annotation-driven
        transaction-manager="transactionManager" />

    <!-- 组件扫描 -->
    <!-- Service扫描 -->
    <context:component-scan
        base-package="com.sunwii.mybatis.service.impl" />
</beans>

测试类:

public class TestOne2OnePerson {
    private ApplicationContext context = SpringUtil.getContext();
    private PersonService personService = (PersonService) context.getBean(PersonService.class);
    private IdCardService idCardService = (IdCardService) context.getBean(IdCardService.class);

    @Test
    public void testPersonInsert() {
        // 一对一单向:添加。
        Person person = new Person();
        person.setName("person-1");

        IdCard idCard = new IdCard();
        idCard.setNumber(UUID.randomUUID().toString());
        idCard.setExpiredTime(CurrentUtil.currentDate());
        
        person.setIdCard(idCard);

        personService.insertPersion(person);
    }
    

    @Test
    public void testPersonInsert2() {
        // 一对一单向:添加。
        Person person = new Person();
        person.setName("person-8");
        personService.insertPersion(person);
        
        //后续的操作,添加idCard并更新Person
        IdCard idCard = new IdCard();
        idCard.setNumber(UUID.randomUUID().toString());
        idCard.setExpiredTime(CurrentUtil.currentDate());
        idCardService.insertIdCard(idCard);
        person.setIdCard(idCard);
        personService.updatePersion(person);
    }

    @Test
    public void testPersonSelect() {
        // 一对一单向:查询。
        int id = 6;
        Person person = personService.getPerson(id);
        System.out.println(person.toLasyString());
        System.out.println(person.toString());
    }
    
    @Test
    public void testPersonUpdate() {
        // 一对一单向:更新。
        int id = 6;
        Person person = personService.getPerson(id);
        person.setName("person-1-update");
        personService.updatePersion(person);
        
        System.out.println(person);
    }
    

    @Test
    public void testIdCardUpdate() {
        // 一对一单向:更新。
        int id = 3;
        Person person = personService.getPerson(id);
    
        IdCard idCard = person.getIdCard();
        idCard.setNumber(UUID.randomUUID().toString());
        
        idCardService.updateIdCard(idCard);
        
        System.out.println(person);
    }
    

    @Test
    public void testPersonDelete() {
        // 一对一单向:删除。
        int id = 3;
        Person person = personService.getPerson(id);
        personService.deletePersion(person);
    }
}

以上为注解版的一对一的使用示例,也可以使用非注解版(XML版本),需要增加Mapper映射文件。

Mapper映射文件:
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.sunwii.mybatis.mapper.PersonMapper">
    <resultMap type="PersonResult" id="PersonMap">
        <id property="id" column="id" />
        <result property="name" column="name" />
      
        <!-- 一对一关联:单向; 方式五:使用select引用方式 -->
        <association property="idCard" column="idcard_id"
            javaType="IdCard"
            select="com.sunwii.mybatis.mapper.IdCardMapper.selectById"
            fetchType="lazy" />

    </resultMap>
    <select id="selectById" parameterType="Integer"
        resultMap="PersonMap">
        select id, name, idcard_id from t_person p where p.id=#{id}
    </select>

    <insert id="insertPerson" parameterType="Person" keyColumn="id"
        keyProperty="id" useGeneratedKeys="true">
        insert into t_person(name,idcard_id)
        values(#{name},#{idCard.id})
    </insert>

    <update id="updatePerson" parameterType="Person">
        update t_person set name=#{name}
        <if test="idCard != null">
            ,idcard_id=#{idCard.id}
        </if>
        where id=#{id}
    </update>

    <delete id="deletePerson" parameterType="Person">
        delete from t_person
        where id=#{id}
    </delete>
</mapper>

IdCardMapper.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.sunwii.mybatis.mapper.IdCardMapper">
    <resultMap type="IdCard" id="IdCardMap">
        <id property="id" column="cid" />
        <result property="number" column="number" />
        <result property="expiredTime" column="expired_time" />
    </resultMap>
     
    <select id="selectById" parameterType="Integer" resultMap="IdCardMap">
        select id as cid ,number,expired_time from t_idcard where id=#{id}
    </select>
    
    <select id="selectById2" parameterType="Integer" resultType="IdCard">
        select id,number,expired_time as expiredTime from t_idcard where id=#{id}
    </select>

    <insert id="insertIdCard" parameterType="IdCard" keyColumn="id"
        keyProperty="id" useGeneratedKeys="true">
        insert into
        t_idcard(number,expired_time) values(#{number},#{expiredTime})
    </insert>

    <update id="updateIdCard" parameterType="IdCard">
        update t_idcard set
        number=#{number},expired_time=#{expiredTime} where id=#{id}
    </update>

    <delete id="deleteIdCard" parameterType="IdCard">
        delete from t_idcard
        where id=#{id}
    </delete>
</mapper>
MyBatis实现一对一关联有两种方式: 1. 嵌套结果映射 在主表和从表的查询语句使用嵌套查询语句并在查询结果映射文件使用<association>标签将从表映射到主表。例如: ```xml <!--主表查询语句--> <select id="findUserById" resultType="User"> SELECT * FROM user WHERE id=#{id} </select> <!--从表查询语句--> <select id="findOrderById" resultType="Order"> SELECT * FROM order WHERE user_id=#{id} </select> <!--结果映射文件--> <resultMap id="UserMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <<association property="order" column="id" javaType="Order" select="findOrderById"/>> </resultMap> <resultMap id="OrderMap" type="Order"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="user_id" column="user_id"/> </resultMap> ``` 2. 延迟加载 使用<association>标签的fetchType属性设置为lazy,表示延迟加载。在查询主表时,并不会立即查询从表,而是等到真正需要从表数据的时候才会查询。例如: ```xml <!--主表查询语句--> <select id="findUserById" resultMap="UserMap"> SELECT * FROM user WHERE id=#{id} </select> <!--结果映射文件--> <resultMap id="UserMap" type="User"> <id property="id" column="id"/> <result property="name" column="name"/> <<association property="order" column="id" javaType="Order" select="findOrderById" fetchType="lazy"/>> </resultMap> ``` 注意:使用延迟加载时,需要保证在查询从表时,主表的session没有关闭。否则会抛出异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值