别再写满屏的 get & set 了,太 Low,试试 MapStruct 高级玩法

别再写满屏的 get-set 了,太 Low!MapStruct 高级玩法,这篇栈长带你上正道!

1、自定义映射

当我们映射 DTO 的时候,如果某些参数的值 MapStruct 的映射配置不能满足要求,可以使用自定义方法。

别再写满屏的 get & set 了,太 Low!试试 MapStruct 高级玩法

新增两个 DTO 类:

UserCustomDTO 类里面包含了 UserExtDTO 对象。

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Data
public class UserCustomDTO {

    private String name;

    private int sex;

    private boolean married;

    private String birthday;

    private String regDate;

    private UserExtDTO userExtDTO;

    private String memo;


}
/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Data
public class UserExtDTO {

    private String regSource;

    private String favorite;

    private String school;

    private int kids;

    private String memo;

}

自定义映射:

如果 UserExtDTO 对象不想使用默认的映射,可以添加一个该参数的自定义映射方法。

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserCustomStruct {

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userExtDO", target = "userExtDTO")
    @Mapping(target = "memo", ignore = true)
    UserCustomDTO toUserCustomDTO(UserDO userDO);

    default UserExtDTO toUserExtDTO(UserExtDO userExtDO) {
        UserExtDTO userExtDTO = new UserExtDTO();
        userExtDTO.setKids(userExtDO.getKids());
        userExtDTO.setFavorite(userExtDO.getFavorite());

        // 覆盖这两个值
        userExtDTO.setRegSource("默认来源");
        userExtDTO.setSchool("默认学校");

        return userExtDTO;
    }

}

当映射 UserExtDTO 对象的时候,会自动调用该接口中的自定义 toUserExtDTO 方法,完成自定义映射。

来看下生成的实现类源码:

@Component
public class UserCustomStructImpl implements UserCustomStruct {
    public UserCustomStructImpl() {
    }

    public UserCustomDTO toUserCustomDTO(UserDO userDO) {
        if (userDO == null) {
            return null;
        } else {
            UserCustomDTO userCustomDTO = new UserCustomDTO();
            if (userDO.getBirthday() != null) {
                userCustomDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
            }

            userCustomDTO.setUserExtDTO(this.toUserExtDTO(userDO.getUserExtDO()));
            userCustomDTO.setName(userDO.getName());
            userCustomDTO.setSex(userDO.getSex());
            userCustomDTO.setMarried(userDO.isMarried());
            userCustomDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));
            return userCustomDTO;
        }
    }
}

没错,setUserExtDTO 方法调用了 this.toUserExtDTO 自定义方法映射。

Spring Boot 基础这篇就不介绍了,系列基础教程和示例源码可以看这里:https://github.com/javastacks/spring-boot-best-practice

测试一下:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCustomStructTest {

    @Autowired
    private UserCustomStruct userCustomStruct;

    @Test
    public void test1() {
        UserExtDO userExtDO = new UserExtDO();
        userExtDO.setRegSource("公众号:Java技术栈");
        userExtDO.setFavorite("写代码");
        userExtDO.setSchool("社会大学");
        userExtDO.setKids(1);

        UserDO userDO = new UserDO();
        userDO.setName("栈长自定义方法");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");
        userDO.setUserExtDO(userExtDO);

        UserCustomDTO userCustomDTO = userCustomStruct.toUserCustomDTO(userDO);
        System.out.println("=====自定义方法=====");
        System.out.println(userCustomDTO);
    }
}

输出结果:

别再写满屏的 get & set 了,太 Low!试试 MapStruct 高级玩法

可以看到自定义方法覆盖的两个值,结果验证成功。

2、多参数映射

之前介绍的映射方法中只有一个参数,如果有多个参数映射成一个 DTO,该怎么弄呢?

比如:有两具单独的 DO,UserDO、UserAddressDO 映射成 UserMultiDTO。

直接上代码:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserMultiStruct {

    @Mapping(source = "userDO.birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "userDO.regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(user.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userAddressDO.postcode", target = "postcode")
    @Mapping(source = "userAddressDO.address", target = "address")
    @Mapping(target = "memo", ignore = true)
    UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO);

}

直接使用指定的 对象名.属性名 形式映射即可。

来看下生成的实现类源码:

@Component
public class UserMultiStructImpl implements UserMultiStruct {
    public UserMultiStructImpl() {
    }

    public UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO) {
        if (userDO == null && userAddressDO == null) {
            return null;
        } else {
            UserMultiDTO userMultiDTO = new UserMultiDTO();
            if (userDO != null) {
                if (userDO.getBirthday() != null) {
                    userMultiDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
                }

                userMultiDTO.setName(userDO.getName());
                userMultiDTO.setSex(userDO.getSex());
                userMultiDTO.setMarried(userDO.isMarried());
                if (userDO.getRegDate() != null) {
                    userMultiDTO.setRegDate((new SimpleDateFormat()).format(userDO.getRegDate()));
                }
            }

            if (userAddressDO != null) {
                userMultiDTO.setPostcode(userAddressDO.getPostcode());
                userMultiDTO.setAddress(userAddressDO.getAddress());
            }

            return userMultiDTO;
        }
    }
}

测试一下:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMultiStructTest {

    @Autowired
    private UserMultiStruct userMultiStruct;

    @Test
    public void test1() {
        UserDO userDO = new UserDO();
        userDO.setName("多参数映射");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");

        UserAddressDO userAddressDO = new UserAddressDO();
        userAddressDO.setProvince("广东省");
        userAddressDO.setCity("深圳市");
        userAddressDO.setPostcode("666666");
        userAddressDO.setAddress("001号大街Java技术栈公众号");
        userAddressDO.setMemo("地址信息");

        UserMultiDTO userMultiDTO = userMultiStruct.toUserMultiDTO(userDO, userAddressDO);
        System.out.println("=====多参数映射=====");
        System.out.println(userMultiDTO);
    }
}

输出结果:

别再写满屏的 get & set 了,太 Low!试试 MapStruct 高级玩法

个人信息和地址信息都输出来了,结果验证成功。

本文实战源代码完整版已经上传:

https://github.com/javastacks/spring-boot-best-practice

3、嵌套映射

如果一个 DTO 中的值都是从一个对象中的多个嵌套对象映射时,如果不想一个个写映射,目标可以用 . 表示。

直接上代码:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserNestedStruct {

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userNestedDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userAddressDO", target = ".")
    @Mapping(source = "userExtDO", target = ".")
    @Mapping(source = "userExtDO.memo", target = "memo")
    UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO);

}

如果嵌套对象中出现重名的映射冲突,可以手动指定来源哪个嵌套对象。

来看下生成的实现类源码:

@Component
public class UserNestedStructImpl implements UserNestedStruct {
    public UserNestedStructImpl() {
    }

    public UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserNestedDTO userNestedDTO = new UserNestedDTO();
            if (userNestedDO.getBirthday() != null) {
                userNestedDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userNestedDO.getBirthday()));
            }

            userNestedDTO.setMemo(this.userNestedDOUserExtDOMemo(userNestedDO));
            userNestedDTO.setCity(this.userNestedDOUserAddressDOCity(userNestedDO));
            userNestedDTO.setAddress(this.userNestedDOUserAddressDOAddress(userNestedDO));
            userNestedDTO.setRegSource(this.userNestedDOUserExtDORegSource(userNestedDO));
            userNestedDTO.setFavorite(this.userNestedDOUserExtDOFavorite(userNestedDO));
            userNestedDTO.setSchool(this.userNestedDOUserExtDOSchool(userNestedDO));
            userNestedDTO.setName(userNestedDO.getName());
            userNestedDTO.setSex(userNestedDO.getSex());
            userNestedDTO.setMarried(userNestedDO.isMarried());
            userNestedDTO.setRegDate(DateFormatUtils.format(userNestedDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));
            return userNestedDTO;
        }
    }

    private String userNestedDOUserExtDOMemo(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userNestedDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String memo = userExtDO.getMemo();
                return memo == null ? null : memo;
            }
        }
    }

    private String userNestedDOUserAddressDOCity(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();
            if (userAddressDO == null) {
                return null;
            } else {
                String city = userAddressDO.getCity();
                return city == null ? null : city;
            }
        }
    }

    private String userNestedDOUserAddressDOAddress(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();
            if (userAddressDO == null) {
                return null;
            } else {
                String address = userAddressDO.getAddress();
                return address == null ? null : address;
            }
        }
    }

    private String userNestedDOUserExtDORegSource(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userNestedDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String regSource = userExtDO.getRegSource();
                return regSource == null ? null : regSource;
            }
        }
    }

    private String userNestedDOUserExtDOFavorite(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userNestedDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String favorite = userExtDO.getFavorite();
                return favorite == null ? null : favorite;
            }
        }
    }

    private String userNestedDOUserExtDOSchool(UserNestedDO userNestedDO) {
        if (userNestedDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userNestedDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String school = userExtDO.getSchool();
                return school == null ? null : school;
            }
        }
    }
}

从源码可以看到,从嵌套对象来的值都会新增一个方法判断一下,以避免出现空指定。

测试一下:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserNestedStructTest {

    @Autowired
    private UserNestedStruct userNestedStruct;

    @Test
    public void test1() {
        UserExtDO userExtDO = new UserExtDO();
        userExtDO.setRegSource("公众号:Java技术栈");
        userExtDO.setFavorite("写代码");
        userExtDO.setSchool("社会大学");
        userExtDO.setKids(1);
        userExtDO.setMemo("扩展信息");

        UserAddressDO userAddressDO = new UserAddressDO();
        userAddressDO.setProvince("广东省");
        userAddressDO.setCity("深圳市");
        userAddressDO.setPostcode("666666");
        userAddressDO.setAddress("001号大街Java技术栈公众号");
        userAddressDO.setMemo("地址信息");

        UserNestedDO userNestedDO = new UserNestedDO();
        userNestedDO.setName("栈长嵌套映射");
        userNestedDO.setSex(1);
        userNestedDO.setAge(18);
        userNestedDO.setBirthday(new Date());
        userNestedDO.setPhone("18888888888");
        userNestedDO.setMarried(true);
        userNestedDO.setRegDate(new Date());
        userNestedDO.setMemo("666");
        userNestedDO.setUserExtDO(userExtDO);
        userNestedDO.setUserAddressDO(userAddressDO);

        UserNestedDTO userNestedDTO = userNestedStruct.toUserNestedDTO(userNestedDO);
        System.out.println("=====嵌套映射=====");
        System.out.println(userNestedDTO);
    }
}

输出结果:

别再写满屏的 get & set 了,太 Low!试试 MapStruct 高级玩法

可以看到嵌套对象值,并且 memo 也是从指定的嵌套对象来的,结果验证成功。

4、映射现有实例

以上介绍的都是映射并生成一个新的 DTO 实例,如果是已有的现有 DTO 实例呢,该怎么映射呢?

直接上代码:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@Mapper(componentModel = "spring")
public interface UserExistStruct {

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userExtDO.regSource", target = "registerSource")
    @Mapping(source = "userExtDO.favorite", target = "favorite")
    @Mapping(target = "name", ignore = true)
    @Mapping(target = "memo", ignore = true)
    void toUserShowDTO(@MappingTarget UserShowDTO userShowDTO, UserDO userDO);

}

在方法上新增 DTO 对象参数并使用 @MappingTarget 对象修饰,参数位置可以调换。

来看下生成的实现类源码:

@Component
public class UserExistStructImpl implements UserExistStruct {
    public UserExistStructImpl() {
    }

    public void toUserShowDTO(UserShowDTO userShowDTO, UserDO userDO) {
        if (userDO != null) {
            if (userDO.getBirthday() != null) {
                userShowDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
            } else {
                userShowDTO.setBirthday((String)null);
            }

            userShowDTO.setRegisterSource(this.userDOUserExtDORegSource(userDO));
            userShowDTO.setFavorite(this.userDOUserExtDOFavorite(userDO));
            userShowDTO.setSex(userDO.getSex());
            userShowDTO.setMarried(userDO.isMarried());
            userShowDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));
        }
    }

    private String userDOUserExtDORegSource(UserDO userDO) {
        if (userDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String regSource = userExtDO.getRegSource();
                return regSource == null ? null : regSource;
            }
        }
    }

    private String userDOUserExtDOFavorite(UserDO userDO) {
        if (userDO == null) {
            return null;
        } else {
            UserExtDO userExtDO = userDO.getUserExtDO();
            if (userExtDO == null) {
                return null;
            } else {
                String favorite = userExtDO.getFavorite();
                return favorite == null ? null : favorite;
            }
        }
    }
}

userShowDTO 是作为方法参数传入的,而不是新创建的。

测试一下:

/**
 * 微信公众号:Java技术栈
 * @author 栈长
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserExistStructTest {

    @Autowired
    private UserExistStruct userExistStruct;

    @Test
    public void test1() {
        UserExtDO userExtDO = new UserExtDO();
        userExtDO.setRegSource("公众号:Java技术栈");
        userExtDO.setFavorite("写代码");
        userExtDO.setSchool("社会大学");

        UserDO userDO = new UserDO();
        userDO.setName("栈长");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");
        userDO.setUserExtDO(userExtDO);

        System.out.println("=====映射现有实例前=====");
        UserShowDTO userShowDTO = new UserShowDTO();
        userShowDTO.setName("栈长NAME");
        userShowDTO.setMemo("栈长MEMO");
        System.out.println(userShowDTO);

        System.out.println("=====映射现有实例后=====");
        userExistStruct.toUserShowDTO(userShowDTO, userDO);
        System.out.println(userShowDTO);
    }
}

输出结果:

别再写满屏的 get & set 了,太 Low!试试 MapStruct 高级玩法

可以看到已有 DTO 对象的值及新映射的值,结果验证成功。

注意:默认是以覆盖原有值的方式映射的,如果要保留原有 XX 的值,使用 ignore 忽略即可

总结

本文栈长介绍了 MapStruct 的 4 个高级玩法,足以应对各种 Bean 类映射了,其实还有很多复杂的、个性化用法,一篇难以写完,后面有时间会整理出来,陆续给大家分享。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用Lettuce创建一个Map并调用set和get函数的示例代码: ```java import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.RedisClient; public class LettuceMapExample { public static void main(String[] args) { // 创建一个 RedisClient 对象 RedisClient redisClient = RedisClient.create("redis://localhost:6379"); // 创建一个 StatefulRedisConnection 对象 StatefulRedisConnection<String, String> connection = redisClient.connect(); // 获取 Redis 命令对象 RedisCommands<String, String> syncCommands = connection.sync(); // 创建一个名为 myMap 的 Map syncCommands.hset("myMap", "key1", "value1"); syncCommands.hset("myMap", "key2", "value2"); // 获取 myMap 中的值 String value1 = syncCommands.hget("myMap", "key1"); String value2 = syncCommands.hget("myMap", "key2"); System.out.println(value1); // 输出:value1 System.out.println(value2); // 输出:value2 // 关闭连接 connection.close(); // 关闭 Redis 客户端 redisClient.shutdown(); } } ``` 在这个示例中,我们使用 Redis 的命令 `hset` 和 `hget` 来创建和获取 Map 中的键值对。注意,我们使用了 `sync()` 方获取同步命令对象,这意味着这些命令将阻塞并等待 Redis 服务器响应。如果需要异步执行命令,请使用 `async()` 方获取异步命令对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值