MyBatis复杂对象处理的两种方式示例代码文档
MyBatis复杂对象处理示例代码文档
概述
本文档详细介绍了如何在 MyBatis中处理复杂对象的两种方式:
- 使用 MyBatis-Plus TypeHandler 处理嵌套对象(自动序列化为 JSON 存储)
- 使用关联对象映射处理多个字段
数据库表结构
文件路径: db/schema.sql
-- 创建演示表结构 drop table demo_entity; -- 主表,使用TypeHandler方式存储复杂对象 CREATE TABLE `demo_entity` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(100) NOT NULL COMMENT '名称', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `config_data` varchar(1000) DEFAULT NULL COMMENT '配置数据(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='演示实体表'; select * from demo_entity;
实体类设计
文件路径: 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处理复杂对象
@TableField(value = "config_data", typeHandler = FastjsonTypeHandler.class)
private ConfigData configData;
// 方式二:关联对象(通过其他字段映射)
// @TableField(exist = false)是由于mybatis-plus的原因,
// 当实体类中存在该字段时,会默认认为该字段是关联字段,会自动填充关联对象。
@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> {
/**
* 自定义插入方法,处理关联对象
*/
int insertWithAddress(@Param("entity") DemoEntity entity);
/**
* 自定义更新方法,处理关联对象
*/
int updateWithAddress(@Param("entity") DemoEntity entity);
/**
* 根据ID查询包含关联对象的完整实体
*/
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"/>
<!-- 关联对象映射 -->
<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 层接口与实现
Service 接口
文件路径: DemoEntityService.java
import com.baomidou.mybatisplus.extension.service.IService;
import com.credithc.risk.entity.DemoEntity;
public interface DemoEntityService extends IService<DemoEntity> {
/**
* 保存Demo实体,同时处理两种不同类型的复杂字段
*/
DemoEntity saveDemoEntity(DemoEntity entity);
/**
* 获取包含关联地址信息的完整实体
*/
DemoEntity getDemoEntityWithAddress(Long id);
}Service 实现类
文件路径: 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) {
// 保存主实体(TypeHandler方式自动处理configData)
if (entity.getId() == null) {
entity.setCreateTime(LocalDateTime.now());
// demoEntityMapper.insert(entity);此方法为mybatis-plus提供的方法,会自动处理configData,但不会处理addressInfo
// 下面的方法会自动处理configData和addressInfo
demoEntityMapper.insertWithAddress(entity);
} else {
// demoEntityMapper.updateById(entity);此方法为mybatis-plus提供的方法,会自动处理configData,但不会处理addressInfo
// 下面的方法会自动处理configData和addressInfo
demoEntityMapper.updateWithAddress(entity);
}
return entity;
}
@Override
public DemoEntity getDemoEntityWithAddress(Long id) {
// demoEntityMapper.selectById(id);此方法为mybatis-plus提供的方法,会自动处理configData,但不会处理addressInfo
// 下面的方法会自动处理configData和addressInfo
return demoEntityMapper.selectWithAddressById(id);
}
}测试用例
文件路径: 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() {
// 创建测试实体
DemoEntity entity = new DemoEntity();
entity.setName("Test Entity With TypeHandler");
// 设置TypeHandler处理的复杂对象
DemoEntity.ConfigData configData = new DemoEntity.ConfigData();
configData.setTheme("dark");
configData.setPermissions(Arrays.asList("read", "write", "delete"));
configData.setEnabled(true);
entity.setConfigData(configData);
// 保存实体
DemoEntity savedEntity = demoEntityService.saveDemoEntity(entity);
// 验证保存成功
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());
// 验证从数据库重新加载的数据
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() {
// 创建测试实体
DemoEntity entity = new DemoEntity();
entity.setName("Test Entity With Association");
entity.setCreateTime(LocalDateTime.now());
// 设置关联对象
DemoEntity.AddressInfo addressInfo = new DemoEntity.AddressInfo();
addressInfo.setStreet("123 Main St");
addressInfo.setCity("Beijing");
addressInfo.setZipCode("100000");
entity.setAddressInfo(addressInfo);
// 保存实体
DemoEntity savedEntity = demoEntityService.saveDemoEntity(entity);
// 验证保存成功
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());
// 验证从数据库重新加载的数据(包含关联对象)
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());
}
}技术要点说明
1. TypeHandler 方式处理复杂对象
- 在实体类中使用
@TableField注解配合typeHandler参数指定处理器 - MyBatis-Plus 提供了
FastjsonTypeHandler来自动将复杂对象序列化为 JSON 字符串存储到数据库 - 适用于单个复杂对象字段的处理
2. 关联对象映射方式
- 使用
@TableField(exist = false)标记非数据库字段 - 在 XML 映射文件中使用
<association>标签进行关联对象映射 - 适用于需要将复杂对象的属性拆分存储到多个数据库字段的情况
3. 自定义 Mapper 方法
为了处理关联对象,我们自定义了以下方法:
insertWithAddress: 插入实体及关联对象数据updateWithAddress: 更新实体及关联对象数据selectWithAddressById: 查询实体及关联对象数据- 关联数据的操作切勿使用mybatis-plus默认方法可能会不生效
4. 事务管理
在 Service 实现类中使用了 @Transactional 注解确保数据一致性。
在MyBatis生态中,当处理数据库中的关联关系时,Association和Collection是两个非常重要的概念。它们能够帮助我们高效地获取和处理与主实体相关的其他实体信息。无论是一对一(Association)还是一对多(Collection)的关系,再加上MyBatis-Plus TypeHandler模式,可以根据具体业务需求选择合适的方式,正确地使用它们可以让我们的持久层代码更加清晰和高效。
到此这篇关于MyBatis复杂对象处理示例代码文档的文章就介绍到这了,更多相关MyBatis复杂对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
SpringBoot @Scope与@RefreshScope注解使用详解
spring的bean管理中,每个bean都有对应的scope。在BeanDefinition中就已经指定scope,默认的RootBeanDefinition的scope是prototype类型,使用@ComponentScan扫描出的BeanDefinition会指定是singleton,最常使用的也是singleton2022-11-11
Hibernate环境搭建与配置方法(Hello world配置文件版)
这篇文章主要介绍了Hibernate环境搭建与配置方法,这里演示Hello world配置文件版的具体实现步骤与相关代码,需要的朋友可以参考下2016-03-03
Windows环境IDEA下Ranger1.2.0源码编译详细流程
本文给大家讲解Windows环境IDEA下Ranger1.2.0源码编译过程,通过配置Tomcat,发布 security-admin-web项目,编译启动tomcat即可完成,需要的朋友参考下2021-06-06
Spring中的@ExceptionHandler注解详解与应用示例
本文详细介绍了Spring框架中的@ExceptionHandler注解的用法,包括基本用法、全局异常处理、结合@ResponseStatus注解以及返回值类型,通过示例展示了如何使用@ExceptionHandler注解处理不同类型的异常,并提供定制化的异常处理响应,需要的朋友可以参考下2024-11-11


最新评论