MyBatis復(fù)雜對(duì)象處理的兩種方式示例代碼文檔
MyBatis復(fù)雜對(duì)象處理示例代碼文檔
概述
本文檔詳細(xì)介紹了如何在 MyBatis中處理復(fù)雜對(duì)象的兩種方式:
- 使用 MyBatis-Plus TypeHandler 處理嵌套對(duì)象(自動(dòng)序列化為 JSON 存儲(chǔ))
- 使用關(guān)聯(lián)對(duì)象映射處理多個(gè)字段
數(shù)據(jù)庫(kù)表結(jié)構(gòu)
文件路徑: db/schema.sql
-- 創(chuàng)建演示表結(jié)構(gòu) drop table demo_entity; -- 主表,使用TypeHandler方式存儲(chǔ)復(fù)雜對(duì)象 CREATE TABLE `demo_entity` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `name` varchar(100) NOT NULL COMMENT '名稱(chēng)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間', `config_data` varchar(1000) DEFAULT NULL COMMENT '配置數(shù)據(jù)(JSON格式,由TypeHandler處理)', `street` varchar(200) DEFAULT NULL COMMENT '街道地址', `city` varchar(100) DEFAULT NULL COMMENT '城市', `zip_code` varchar(20) DEFAULT NULL COMMENT '郵政編碼', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='演示實(shí)體表'; select * from demo_entity;
實(shí)體類(lèi)設(shè)計(jì)
文件路徑: DemoEntity.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
@Data
@TableName(value = "demo_entity",autoResultMap = true)
public class DemoEntity implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("name")
private String name;
@TableField("create_time")
private LocalDateTime createTime;
// 方式一:使用TypeHandler處理復(fù)雜對(duì)象
@TableField(value = "config_data", typeHandler = FastjsonTypeHandler.class)
private ConfigData configData;
// 方式二:關(guān)聯(lián)對(duì)象(通過(guò)其他字段映射)
// @TableField(exist = false)是由于mybatis-plus的原因,
// 當(dāng)實(shí)體類(lèi)中存在該字段時(shí),會(huì)默認(rèn)認(rèn)為該字段是關(guān)聯(lián)字段,會(huì)自動(dòng)填充關(guān)聯(lián)對(duì)象。
@TableField(exist = false)
private AddressInfo addressInfo;
@Data
public static class ConfigData implements Serializable {
private String theme;
private List<String> permissions;
private Boolean enabled;
}
@Data
public static class AddressInfo implements Serializable {
private String street;
private String city;
private String zipCode;
}
}Mapper 接口
文件路徑: DemoEntityMapper.java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.credithc.risk.entity.DemoEntity;
import org.apache.ibatis.annotations.Param;
public interface DemoEntityMapper extends BaseMapper<DemoEntity> {
/**
* 自定義插入方法,處理關(guān)聯(lián)對(duì)象
*/
int insertWithAddress(@Param("entity") DemoEntity entity);
/**
* 自定義更新方法,處理關(guān)聯(lián)對(duì)象
*/
int updateWithAddress(@Param("entity") DemoEntity entity);
/**
* 根據(jù)ID查詢(xún)包含關(guān)聯(lián)對(duì)象的完整實(shí)體
*/
DemoEntity selectWithAddressById(@Param("id") Long id);
}Mapper XML 映射文件
文件路徑: DemoEntityMapper.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="DemoEntityMapper">
<resultMap id="BaseResultMap" type="DemoEntity">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="createTime" column="create_time"/>
<result property="configData" column="config_data"
typeHandler="com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler"/>
<!-- 關(guān)聯(lián)對(duì)象映射 -->
<association property="addressInfo" javaType="DemoEntity$AddressInfo">
<result property="street" column="street"/>
<result property="city" column="city"/>
<result property="zipCode" column="zip_code"/>
</association>
</resultMap>
<insert id="insertWithAddress" useGeneratedKeys="true" keyProperty="id">
INSERT INTO demo_entity (name, create_time, config_data,street,city, zip_code)
VALUES (#{entity.name}, #{entity.createTime},
#{entity.configData, typeHandler=com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler},
#{entity.addressInfo.street},#{entity.addressInfo.city},#{entity.addressInfo.zipCode})
</insert>
<insert id="updateWithAddress">
UPDATE demo_entity
SET name = #{entity.name},
create_time = #{entity.createTime},
config_data = #{entity.configData, typeHandler=com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler},
street = #{entity.addressInfo.street},
city = #{entity.addressInfo.city},
zip_code = #{entity.addressInfo.zipCode}
WHERE id = #{entity.id}
</insert>
<select id="selectWithAddressById" resultMap="BaseResultMap">
SELECT
id,name,create_time,config_data,
street,
city,
zip_code
FROM demo_entity
WHERE id = #{id}
</select>
</mapper>Service 層接口與實(shí)現(xiàn)
Service 接口
文件路徑: DemoEntityService.java
import com.baomidou.mybatisplus.extension.service.IService;
import com.credithc.risk.entity.DemoEntity;
public interface DemoEntityService extends IService<DemoEntity> {
/**
* 保存Demo實(shí)體,同時(shí)處理兩種不同類(lèi)型的復(fù)雜字段
*/
DemoEntity saveDemoEntity(DemoEntity entity);
/**
* 獲取包含關(guān)聯(lián)地址信息的完整實(shí)體
*/
DemoEntity getDemoEntityWithAddress(Long id);
}Service 實(shí)現(xiàn)類(lèi)
文件路徑: DemoEntityServiceImpl.java
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.credithc.risk.dao.DemoEntityMapper;
import com.credithc.risk.entity.DemoEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Service
public class DemoEntityServiceImpl extends ServiceImpl<DemoEntityMapper, DemoEntity> implements DemoEntityService {
@Resource
private DemoEntityMapper demoEntityMapper;
@Override
@Transactional
public DemoEntity saveDemoEntity(DemoEntity entity) {
// 保存主實(shí)體(TypeHandler方式自動(dòng)處理configData)
if (entity.getId() == null) {
entity.setCreateTime(LocalDateTime.now());
// demoEntityMapper.insert(entity);此方法為mybatis-plus提供的方法,會(huì)自動(dòng)處理configData,但不會(huì)處理addressInfo
// 下面的方法會(huì)自動(dòng)處理configData和addressInfo
demoEntityMapper.insertWithAddress(entity);
} else {
// demoEntityMapper.updateById(entity);此方法為mybatis-plus提供的方法,會(huì)自動(dòng)處理configData,但不會(huì)處理addressInfo
// 下面的方法會(huì)自動(dòng)處理configData和addressInfo
demoEntityMapper.updateWithAddress(entity);
}
return entity;
}
@Override
public DemoEntity getDemoEntityWithAddress(Long id) {
// demoEntityMapper.selectById(id);此方法為mybatis-plus提供的方法,會(huì)自動(dòng)處理configData,但不會(huì)處理addressInfo
// 下面的方法會(huì)自動(dòng)處理configData和addressInfo
return demoEntityMapper.selectWithAddressById(id);
}
}測(cè)試用例
文件路徑: DemoEntityServiceTest.java
import com.credithc.risk.entity.DemoEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class DemoEntityServiceTest {
@Resource
private DemoEntityService demoEntityService;
@Test
public void testSaveAndRetrieveEntityWithTypeHandler() {
// 創(chuàng)建測(cè)試實(shí)體
DemoEntity entity = new DemoEntity();
entity.setName("Test Entity With TypeHandler");
// 設(shè)置TypeHandler處理的復(fù)雜對(duì)象
DemoEntity.ConfigData configData = new DemoEntity.ConfigData();
configData.setTheme("dark");
configData.setPermissions(Arrays.asList("read", "write", "delete"));
configData.setEnabled(true);
entity.setConfigData(configData);
// 保存實(shí)體
DemoEntity savedEntity = demoEntityService.saveDemoEntity(entity);
// 驗(yàn)證保存成功
assertNotNull(savedEntity.getId());
assertEquals("Test Entity With TypeHandler", savedEntity.getName());
assertNotNull(savedEntity.getConfigData());
assertEquals("dark", savedEntity.getConfigData().getTheme());
assertEquals(3, savedEntity.getConfigData().getPermissions().size());
assertTrue(savedEntity.getConfigData().getEnabled());
// 驗(yàn)證從數(shù)據(jù)庫(kù)重新加載的數(shù)據(jù)
DemoEntity reloadedEntity = demoEntityService.getById(savedEntity.getId());
assertNotNull(reloadedEntity);
assertEquals("Test Entity With TypeHandler", reloadedEntity.getName());
assertNotNull(reloadedEntity.getConfigData());
assertEquals("dark", reloadedEntity.getConfigData().getTheme());
assertEquals(3, reloadedEntity.getConfigData().getPermissions().size());
assertTrue(reloadedEntity.getConfigData().getEnabled());
}
@Test
public void testSaveAndRetrieveEntityWithAssociation() {
// 創(chuàng)建測(cè)試實(shí)體
DemoEntity entity = new DemoEntity();
entity.setName("Test Entity With Association");
entity.setCreateTime(LocalDateTime.now());
// 設(shè)置關(guān)聯(lián)對(duì)象
DemoEntity.AddressInfo addressInfo = new DemoEntity.AddressInfo();
addressInfo.setStreet("123 Main St");
addressInfo.setCity("Beijing");
addressInfo.setZipCode("100000");
entity.setAddressInfo(addressInfo);
// 保存實(shí)體
DemoEntity savedEntity = demoEntityService.saveDemoEntity(entity);
// 驗(yàn)證保存成功
assertNotNull(savedEntity.getId());
assertEquals("Test Entity With Association", savedEntity.getName());
assertNotNull(savedEntity.getAddressInfo());
assertEquals("123 Main St", savedEntity.getAddressInfo().getStreet());
assertEquals("Beijing", savedEntity.getAddressInfo().getCity());
assertEquals("100000", savedEntity.getAddressInfo().getZipCode());
// 驗(yàn)證從數(shù)據(jù)庫(kù)重新加載的數(shù)據(jù)(包含關(guān)聯(lián)對(duì)象)
DemoEntity reloadedEntity = demoEntityService.getDemoEntityWithAddress(savedEntity.getId());
assertNotNull(reloadedEntity);
assertEquals("Test Entity With Association", reloadedEntity.getName());
assertNotNull(reloadedEntity.getAddressInfo());
assertEquals("123 Main St", reloadedEntity.getAddressInfo().getStreet());
assertEquals("Beijing", reloadedEntity.getAddressInfo().getCity());
assertEquals("100000", reloadedEntity.getAddressInfo().getZipCode());
}
}技術(shù)要點(diǎn)說(shuō)明
1. TypeHandler 方式處理復(fù)雜對(duì)象
- 在實(shí)體類(lèi)中使用
@TableField注解配合typeHandler參數(shù)指定處理器 - MyBatis-Plus 提供了
FastjsonTypeHandler來(lái)自動(dòng)將復(fù)雜對(duì)象序列化為 JSON 字符串存儲(chǔ)到數(shù)據(jù)庫(kù) - 適用于單個(gè)復(fù)雜對(duì)象字段的處理
2. 關(guān)聯(lián)對(duì)象映射方式
- 使用
@TableField(exist = false)標(biāo)記非數(shù)據(jù)庫(kù)字段 - 在 XML 映射文件中使用
<association>標(biāo)簽進(jìn)行關(guān)聯(lián)對(duì)象映射 - 適用于需要將復(fù)雜對(duì)象的屬性拆分存儲(chǔ)到多個(gè)數(shù)據(jù)庫(kù)字段的情況
3. 自定義 Mapper 方法
為了處理關(guān)聯(lián)對(duì)象,我們自定義了以下方法:
insertWithAddress: 插入實(shí)體及關(guān)聯(lián)對(duì)象數(shù)據(jù)updateWithAddress: 更新實(shí)體及關(guān)聯(lián)對(duì)象數(shù)據(jù)selectWithAddressById: 查詢(xún)實(shí)體及關(guān)聯(lián)對(duì)象數(shù)據(jù)- 關(guān)聯(lián)數(shù)據(jù)的操作切勿使用mybatis-plus默認(rèn)方法可能會(huì)不生效
4. 事務(wù)管理
在 Service 實(shí)現(xiàn)類(lèi)中使用了 @Transactional 注解確保數(shù)據(jù)一致性。
在MyBatis生態(tài)中,當(dāng)處理數(shù)據(jù)庫(kù)中的關(guān)聯(lián)關(guān)系時(shí),Association和Collection是兩個(gè)非常重要的概念。它們能夠幫助我們高效地獲取和處理與主實(shí)體相關(guān)的其他實(shí)體信息。無(wú)論是一對(duì)一(Association)還是一對(duì)多(Collection)的關(guān)系,再加上MyBatis-Plus TypeHandler模式,可以根據(jù)具體業(yè)務(wù)需求選擇合適的方式,正確地使用它們可以讓我們的持久層代碼更加清晰和高效。
到此這篇關(guān)于MyBatis復(fù)雜對(duì)象處理示例代碼文檔的文章就介紹到這了,更多相關(guān)MyBatis復(fù)雜對(duì)象內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis的配置對(duì)象Configuration作用及說(shuō)明
- Mybatis-Plus使用MetaObjectHandler實(shí)現(xiàn)自動(dòng)填充實(shí)體對(duì)象字段
- MyBatis-Plus更新對(duì)象時(shí)將字段值更新為null的四種常見(jiàn)方法
- Mybatis-plus如何查詢(xún)返回對(duì)象內(nèi)有List<String>屬性
- Mybatis返回Map對(duì)象的實(shí)現(xiàn)
- Mybatis批量更新對(duì)象數(shù)據(jù)的兩種實(shí)現(xiàn)方式
- mybatisplus中的xml對(duì)象參數(shù)傳遞問(wèn)題
相關(guān)文章
詳解領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)之事件驅(qū)動(dòng)與CQRS
這篇文章分析了如何應(yīng)用事件來(lái)分離軟件核心復(fù)雜度。探究CQRS為什么廣泛應(yīng)用于DDD項(xiàng)目中,以及如何落地實(shí)現(xiàn)CQRS框架。當(dāng)然我們也要警惕一些失敗的教訓(xùn),利弊分析以后再去抉擇正確的應(yīng)對(duì)之道2021-06-06
MybatisPlus lambdaQueryWrapper中常用方法的使用
本文主要介紹了MybatisPlus lambdaQueryWrapper中常用方法的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
SpringBoot?Java通過(guò)API的方式調(diào)用騰訊智能體(騰訊元寶)代碼示例
這篇文章主要介紹了SpringBoot?Java通過(guò)API的方式調(diào)用騰訊智能體(騰訊元寶)的相關(guān)資料,詳細(xì)說(shuō)明參數(shù)獲取及動(dòng)態(tài)處理方法,提供結(jié)果字符串轉(zhuǎn)數(shù)組技巧,需要的朋友可以參考下2025-06-06
java 根據(jù)前端返回的字段名進(jìn)行查詢(xún)數(shù)據(jù)
本文介紹了如何在Java中使用SpringDataJPA實(shí)現(xiàn)動(dòng)態(tài)查詢(xún)功能,以便根據(jù)前端傳遞的字段名動(dòng)態(tài)構(gòu)建查詢(xún)語(yǔ)句,通過(guò)創(chuàng)建實(shí)體類(lèi)、Repository接口、構(gòu)建動(dòng)態(tài)查詢(xún)、在Service層和Controller中使用動(dòng)態(tài)查詢(xún),實(shí)現(xiàn)了前后端分離架構(gòu)中的靈活查詢(xún)需求2024-11-11
第三方包jintellitype實(shí)現(xiàn)Java設(shè)置全局熱鍵
本文主要介紹了,在java中使用第三方插件包jintellitype來(lái)實(shí)現(xiàn)全局熱鍵,非常的簡(jiǎn)單,但是很實(shí)用,有需要的朋友可以參考下,歡迎一起來(lái)參與改進(jìn)此項(xiàng)目2014-09-09
詳解JAVA使用Comparator接口實(shí)現(xiàn)自定義排序
這篇文章主要介紹了JAVA使用Comparator接口實(shí)現(xiàn)自定義排序,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
spring boot中使用http請(qǐng)求的示例代碼
本篇文章主要介紹了spring boot中 使用http請(qǐng)求的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
Feign實(shí)現(xiàn)多文件上傳,Open?Feign多文件上傳問(wèn)題及解決
這篇文章主要介紹了Feign實(shí)現(xiàn)多文件上傳,Open?Feign多文件上傳問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11

