美文网首页Spring boot
Springboot整合Dozer深度复制

Springboot整合Dozer深度复制

作者: 三也视界 | 来源:发表于2021-02-04 09:31 被阅读0次

    Dozer

    Dozer是一种Java Bean到Java Bean的映射器,递归地将数据从一个对象复制到另一个对象,它是一个强大的,通用的,灵活的,可重用的和可配置的开源映射框架。

    常用于:

    • 代码层与层之间javabean转换, 如dao层PO转前端VO
    • 分布式中, DAO层PO转DTO, DO 以及web层DTO转VO

    注意的场景:

    • 由于bean之间的深度复制, 在进行一些类似更新, 插入操作时尤其要注意最终接收到PO的一些关键字段如ID是否是真正需要的. 场景: 传入的DTO A为查出的DTO B复制后的, 这时候A里会有B的ID, 在插入A的时候很有可能造成主键冲突.

    建议:

    • 不用Dozer最好, Dozer带来的是性能开销.(这是不可能…)
    • 某些特殊操作可以用切面控制特殊字段进行置空操作

    SpringBoot整合Dozer

    jar依赖引入

    pom.xml加入以下依赖

    <!--       深度复制: Dozer可以灵活的对对象进行转换,且使用简单-->
            <dependency>
                <groupId>net.sf.dozer</groupId>
                <artifactId>dozer-spring</artifactId>
                <version>5.5.1</version>
            </dependency>
            <dependency>
                <groupId>net.sf.dozer</groupId>
                <artifactId>dozer</artifactId>
                <version>5.5.1</version>
                <exclusions>
                    <exclusion>
                        <artifactId>jcl-over-slf4j</artifactId>
                        <groupId>org.slf4j</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
    

    dozer配置xml引入

    resource文件夹下新建dozer文件夹, 并新建bean-mappings.xml, global-configuration.xml

    bean-mappings.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://dozer.sourceforge.net
              http://dozer.sourceforge.net/schema/beanmapping.xsd">
    </mappings>
    

    global-configuration.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <mappings xmlns="http://dozer.sourceforge.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://dozer.sourceforge.net
              http://dozer.sourceforge.net/schema/beanmapping.xsd">
        <configuration>
            <date-format>yyyy-MM-dd HH:mm:ss</date-format>
            <wildcard>true</wildcard>
            <trim-strings>false</trim-strings>
            <!-- 自定义的枚举与Integer转换器, 下节介绍 -->
            <!--<custom-converters>
                <converter type="com.dongao.beacon.ds.rest.admin.tookit.EnumIntegerBiDirectionalDozerConverter">
                    <class-a>java.lang.Enum</class-a>
                    <class-b>java.lang.Integer</class-b>
                </converter>
            </custom-converters>-->
        </configuration>
    </mappings>
    

    自定义DozerConverter,实现CustomConverter并重写convert
    枚举转换与Integer转换, 只是简单的把枚举index与Integer转换, 同理枚举转字符串只是转的name. 复杂枚举name, code, dispalyname的转换就不适用默认规则了, 这时需要自定义转换器.

    public class EnumIntegerBiDirectionalDozerConverter implements CustomConverter {
    
        @Override
        public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
            if (EmptyUtils.isEmpty(source)) {
                return null;
            }
            if (source instanceof Enum) {
                return getInteger(destinationClass, source);
            } else if (source instanceof Integer) {
                return getEnum(destinationClass, source);
            } else {
                throw new MappingException(new StringBuilder("Converter ").append(this.getClass().getSimpleName())
                        .append(" was used incorrectly. Arguments were: ").append(destinationClass.getClass().getName())
                        .append(" and ").append(source).toString());
            }
        }
    
        private Object getInteger(Class<?> destinationClass, Object source) {
            try {
                Enum<?> em = (Enum<?>) source;
                Class<?> clazz = em.getDeclaringClass(); 
                Method getCode = clazz.getMethod("getCode"); 
                Object code = getCode.invoke(source);
                return Integer.valueOf((String) code);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        private Object getEnum(Class<?> destinationClass, Object source) {
            try {
                Method m = destinationClass.getDeclaredMethod("valueOfCode", String.class);
                Object enumeration = m.invoke(destinationClass.getClass(), String.valueOf(source));
                return enumeration;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
    }
    
    

    Dozer的JavaConfig

    用于SpringBoot寻找DozerBeanMapperFactoryBean的配置
    新建DozerMapperConfig.java

    @Configuration
    public class DozerMapperConfig {
        @Bean
        public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(@Value("classpath*:dozer/*.xml" ) Resource[] resources) throws Exception {
            final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean();
            dozerBeanMapperFactoryBean.setMappingFiles(resources);
            return dozerBeanMapperFactoryBean;
        }
    
     /*   @Bean
        public IGenerator ejbGenerator() {
            return new EJBGenerator();
        }*/
    }
    

    格式化工厂

    建议新建包专门放置Dozer工具

    接口定义: 新建IGenerator.java接口

    public interface IGenerator {
    
        /**
         * 转换
         *
         * @param s   数据对象
         * @param clz 复制目标类型
         * @return {@link T}
         * @Description: 单个对象的深度复制及类型转换,vo/domain , po
         * @author banjuer@outlook.com
         * @Time 2018年5月9日 下午3:53:24
         */
        <T, S> T convert(S s, Class<T> clz);
    
        /**
         * @Description: 深度复制结果集(ResultSet为自定义的分页结果集)
         * @param s 数据对象
         * @param clz 复制目标类型
         * @return
         * @author banjuer@outlook.com
         * @Time 2018年5月9日 下午3:53:24
         */
        //<T, S> Result<T> convert(Result<S> s, Class<T> clz);
    
        /**
         * 转换
         * @param s   数据对象
         * @param clz 复制目标类型
         * @return {@link List<T>}
         * @Description: list深度复制
         * @author banjuer@outlook.com
         * @Time 2018年5月9日 下午3:54:08
         */
        <T, S> List<T> convert(List<S> s, Class<T> clz);
    
        /**
         *
         * @param s
         * @param clz
         * @param <T>
         * @param <S>
         * @return
         */
        <T, S> Paging<T> convertPaging(Paging<S> s, Class<T> clz);
        /**
         * @param s   数据对象
         * @param clz 复制目标类型
         * @return
         * @Description: set深度复制
         * @author banjuer@outlook.com
         * @Time 2018年5月9日 下午3:54:39
         */
        <T, S> Set<T> convert(Set<S> s, Class<T> clz);
    
        /**
         * @param s   数据对象
         * @param clz 复制目标类型
         * @return
         * @Description: 数组深度复制
         * @author banjuer@outlook.com
         * @Time 2018年5月9日 下午3:54:57
         */
        <T, S> T[] convert(S[] s, Class<T> clz);
    
        /**
         * 分页信息转换
         * @return {@link PageResult<T>}
         */
        <T, S> PageResult<T> convertPageInfo(PageInfo<S> s, Class<T> clz);
    }
    

    IGenerator实现

    @Component
    @Lazy(true)
    public class EJBGenerator implements IGenerator {
    
        @Autowired
        protected Mapper dozerMapper;
    
        @Override
        public <T, S> T convert(final S s, Class<T> clz) {
            return s == null ? null : this.dozerMapper.map(s, clz);
        }
    
        @Override
        public <T, S> List<T> convert(List<S> s, Class<T> clz) {
            return s == null ? null : s.stream().map(vs -> this.dozerMapper.map(vs, clz)).collect(Collectors.toList());
        }
    
        @Override
        public <T, S> Paging<T> convertPaging(Paging<S> paging, Class<T> clz) {
            Paging<T> pagingVo=new  Paging<T>();
            pagingVo.setRecords(convert(paging.getRecords(),clz));
            pagingVo.setTotal(paging.getTotal());
            return pagingVo;
        }
    
        @Override
        public <T, S> Set<T> convert(Set<S> s, Class<T> clz) {
            return s == null ? null : s.stream().map(vs -> this.dozerMapper.map(vs, clz)).collect(Collectors.toSet());
        }
    
        @Override
        public <T, S> T[] convert(S[] s, Class<T> clz) {
            if (s == null) {
                return null;
            }
            @SuppressWarnings("unchecked")
            T[] arr = (T[]) Array.newInstance(clz, s.length);
            for (int i = 0; i < s.length; i++) {
                arr[i] = this.dozerMapper.map(s[i], clz);
            }
            return arr;
        }
    
        @Override
        public <T, S> PageResult<T> convertPageInfo(PageInfo<S> s, Class<T> clz) {
            return new PageResult(s.getTotal(), convert(s.getList(), clz));
        }
    }
    

    demo使用

    使用Demo
    一般在公共父类中引入, 此处例子为前端公共Controller引入

    @Controller
    public class BaseController  {
    
        @Autowired
        protected EJBGenerator ejbGenerator = new EJBGenerator();
    
    
    }
    
    // 个人信息变更记录session
    SessionUserDetails userDetails = ejbGenerator.convert(userVo, SessionUserDetails.class);
    

    相关文章

      网友评论

        本文标题:Springboot整合Dozer深度复制

        本文链接:https://www.haomeiwen.com/subject/orlmtltx.html