2021-09-21

dto和vo和pojo他们是我们开发过程经常遇到的几种实体类,他们之间的转化是我们经常遇到的 什么是dto,vo do

用户提交请求(可能是填写表单),表单的数据在展示层被匹配为 VO。
服务层把 VO 转换为服务层对应方法所要求的 DTO,传送给服务层。
服务层首先根据 DTO 的数据构造一个 DO (或重建),调用 DO 的业务方法完成具体业务。
服务层把 DO 转换为持久层对应的 PO(一般使用 ORM 工具),调用持久层的持久化方法,把 PO 传递给它,完成持久化操作

下面就有我带大家经常用的几种方法

BeanUtils.copyProperties(copy靠本的死)

原理


 copyProperties使用了jdk自带的自省机制。
 自省 简单来说就是jdk在 反射 上又做了一层包装,
 针对于Bean对象的属性读写。

遍历target(逃改的)对象的属性,获取target对象属性的write(为的)方法,
根据target对象的属性名,获取source对象的属性
如果有同样名字的属性,调用source(搜死)对象属性的read(软的)
方法得到值,调用target对象属性的write方法,给属性赋值

源码(核心)

  public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, (Class)null, (String[])null);
    }
  private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException {
        Assert.notNull(source"Source must not be null");
        Assert.notNull(target, "Target must not be null");
        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]");
            }

            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        List<String> ignoreList = ignoreProperties != null ? Arrays.asList(ignoreProperties) : null;
        PropertyDescriptor[] var7 = targetPds;
        int var8 = targetPds.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
                if (sourcePd != null) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }

                            Object value = readMethod.invoke(source);
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }

                            writeMethod.invoke(target, value);
                        } catch (Throwable var15) {
                            throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var15);
                        }
                    }
                }
            }
        }

    }

方法

将给定源bean的属性值复制到目标bean中
public static void copyProperties(Object source, Object target)

疑问

BeanUtils是深拷贝,还是浅拷贝?
是浅拷贝。
浅拷贝: 只是调用子对象的set方法,并没有将所有属性拷贝。(也就是说,引用的一个内存地址)
深拷贝: 将子对象的属性也拷贝过去

演示一下

浅拷贝


public class Person {
    //两个属性值:分别代表值传递和引用传递
    private Age age;
    private String name;
    public Person(Age age,String name) {
        this.age=age;
        this.name=name;
    }
    //拷贝构造方法
    public Person(Person p) {
        this.name=p.name;
        this.age=p.age;
    }

    public Age getAge() {
        return age;
    }

    public void setAge(Age age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name=name;
    }

    public String toString() {
        return this.name+" "+this.age;
    }
}

public class Age {
    private int age;
    public Age(int age) {
        this.age=age;
    }

    public void setAge(int age) {
        this.age=age;
    }

    public int getAge() {
        return this.age;
    }

    public String toString() {
        return getAge()+"";
    }
}
去实现浅拷贝

第一种 进行拷贝以后,修改字段值和对象值看结果

public class CopyConstructor {
    public static void main(String[] args) {
        Age a=new Age(20);
        Person p1=new Person(a,"摇头耶稣");
        Person p2=new Person(p1);
        System.out.println("p1是"+p1);
        System.out.println("p2是"+p2);
        //修改p1的各属性值,观察p2的各属性值是否跟随变化
        p1.setName("小傻瓜");
        a.setAge(99);
        Age a1=new Age(201);
//        p1.setAge(a1);

        System.out.println("修改后的p1是"+p1);
        System.out.println("修改后的p2是"+p2);
    }
}
}

可以看到使用对象的方法去修改,拷贝以后和拷贝之前都改变 第2种情况

public class CopyConstructor {
    public static void main(String[] args) {
        Age a=new Age(20);
        Person p1=new Person(a,"摇头耶稣");
        Person p2=new Person(p1);
        System.out.println("p1是"+p1);
        System.out.println("p2是"+p2);
        //修改p1的各属性值,观察p2的各属性值是否跟随变化
        p1.setName("小傻瓜");
//        a.setAge(99);
        Age a1=new Age(201);
        p1.setAge(a1);

        System.out.println("修改后的p1是"+p1);
        System.out.println("修改后的p2是"+p2);
    }
}

为什么

这里对Person类选择了两个具有代表性的属性值:
一个是引用传递类型;另一个是字符串类型(属于常量)
看出地址还是一样,对象地址不改变,但是字段会改变,不会随着前一个进行改变

如果在拷贝构造方法中,对引用数据类型变量逐一开辟新的内存空间,
创建新的对象,也可以实现深拷贝。
而对于一般的拷贝构造,则一定是浅拷贝

深拷贝

对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,
还要为所有引用数据类型的成员变量申请存储空间,
并复制每个引用数据类型成员变量所引用的对象
,直到该对象可达的所有对象。也就是说,
对象进行深拷贝要对整个对象图进行拷贝!

代码

public class DeepCopyBySerialization {
    public static void main(String[] args) throws IOException, ClassNotFoundException  {
        Age a=new Age(20);
        Person stu1=new Person(a,"摇头耶稣");
        //通过序列化方法实现深拷贝
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(stu1);
        oos.flush();
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Person stu2=(Person)ois.readObject();
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
        System.out.println();
        //尝试修改stu1中的各属性,观察stu2的属性有没有变化
        stu1.setName("大傻子");
        //改变age这个引用类型的成员变量的值
        a.setAge(99);

        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
    }
}
这里出现问题实体类必须实现Serializable(谁的瑞来的包)接口

第一种情况的结果

public class DeepCopyBySerialization {
    public static void main(String[] args) throws IOException, ClassNotFoundException  {
        Age a=new Age(20);
        Person stu1=new Person(a,"摇头耶稣");
        //通过序列化方法实现深拷贝
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(stu1);
        oos.flush();
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Person stu2=(Person)ois.readObject();
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
        System.out.println();
        //尝试修改stu1中的各属性,观察stu2的属性有没有变化
        stu1.setName("大傻子");
        //改变age这个引用类型的成员变量的值
//        a.setAge(99);
        Age a1=new Age(200);
        stu1.setAge(a1);

        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
    }
}

结果

结果分析

1怎么改变 2就是不改变 地址不同

第2种转化

使用mapstruct(买瓶死装要死)

    <org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
        <org.projectlombok.version>1.16.20</org.projectlombok.version>
  <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
            <scope>provided</scope>
        </dependency>

出现的问题,出现null,解决方法

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <layers>
                        <enabled>true</enabled>
                    </layers>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${org.projectlombok.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok-mapstruct-binding</artifactId>
                            <version>0.2.0</version>  <!-- 如果是0.1.0 有可能出现生成了maptruct的实现类,但该类只创建了对象,没有进行赋值 -->  这个没有解决null的话,将这个去掉
                        </path>

                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

实体类

入参

@Data

@NoArgsConstructor
public class DepartmentidDto {
    private Integer departmentid;
    private String departmentname;

}
pojo实体类

@Data

public class Department implements Serializable {
    private Integer departmentid;
    private String departmentname;
    private Integer managerid;
}
写一个接口去实现复制
@Mapper(componentModel = "spring", uses = EntityFactory.class)
public interface AbsencedutyEntr3 {
     Department cppyDepartmentidDto(DepartmentidDto department);
}

核心

@Component
public class EntityFactory implements ApplicationContextAware {
    /**
     * 从ApplicationContextAware获取ApplicationContext上下文的情况,仅仅适用于当前运行的代码和已启动的Spring代码处于同一个Spring上下文,否则获取到的ApplicationContext是空的
     *
     * 
     */
    private static ApplicationContext applicationContext = null;

    public EntityFactory() {
    }

    public void setApplicationContext(ApplicationContext applicationContext) {
        if (EntityFactory.applicationContext == null) {
            EntityFactory.applicationContext = applicationContext;
        }

    }


    public <T> T createEntity(@TargetType Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

}

测试

@SpringBootTest
public class TTT {

    @Resource
    private AbsencedutyEntr3 absencedutyEntr;

    @Test
    public void contextLoads() {
        
        DepartmentidDto department=new DepartmentidDto();
        department.setDepartmentid(1);
        department.setDepartmentname("11111111111");
        System.out.println(department.toString());

        Department department1 = absencedutyEntr.cppyDepartmentidDto(department);
        System.out.println(department1.toString());
    }
}

看生成的接口的类(核心这个是生成的)

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-09-21T21:14:05+0800",
    comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_261 (Oracle Corporation)"
)
@Component
public class AbsencedutyEntr3Impl implements AbsencedutyEntr3 {

    @Autowired
    private EntityFactory entityFactory;

    @Override
    public Department cppyDepartmentidDto(DepartmentidDto department) {
        if ( department == null ) {
            return null;
        }

        Department department1 = entityFactory.createEntity( Department.class );

        department1.setDepartmentid( department.getDepartmentid() );
        department1.setDepartmentname( department.getDepartmentname() );

        return department1;
    }
}

这个就是简单的浅拷贝

其他的方法看

https://www.cnblogs.com/homejim/p/11313128.html

简单带大家去了解一般开发过程中转话

经常碰到这样变态的需求:

假设是从A复制到B: 
需求1:如果B中某字段有值(不为null),则该字段不复制;
也就是B中该字段没值时,才进行复制,适合于对B进行补充值的情况。 
需求2:如果A中某字段没值(为null),则该字段不复制
也就是不要把null复制到B当中。

这里使用是import org.apache.commons.beanutils.BeanUtils; 记住跟刚才不一样复制 依赖

   <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>

源码 还是这个方法 BeanUtils.copyProperties();


public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
        BeanUtilsBean.getInstance().copyProperties(dest, orig);
    }
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
        if (dest == null) {
            throw new IllegalArgumentException("No destination bean specified");
        } else if (orig == null) {
            throw new IllegalArgumentException("No origin bean specified");
        } else {
            if (this.log.isDebugEnabled()) {
                this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
            }

            int var5;
            int var6;
            String name;
            Object value;
            if (orig instanceof DynaBean) {
                DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
                DynaProperty[] var4 = origDescriptors;
                var5 = origDescriptors.length;

                for(var6 = 0; var6 < var5; ++var6) {
                    DynaProperty origDescriptor = var4[var6];
                    name = origDescriptor.getName();
                    if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                        value = ((DynaBean)orig).get(name);
                        this.copyProperty(dest, name, value);
                    }
                }
            } else if (orig instanceof Map) {
                Map<String, Object> propMap = (Map)orig;
                Iterator var13 = propMap.entrySet().iterator();

                while(var13.hasNext()) {
                    Entry<String, Object> entry = (Entry)var13.next();
                    String name = (String)entry.getKey();
                    if (this.getPropertyUtils().isWriteable(dest, name)) {
                        this.copyProperty(dest, name, entry.getValue());
                    }
                }
            } else {
                PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);
                PropertyDescriptor[] var14 = origDescriptors;
                var5 = origDescriptors.length;

                for(var6 = 0; var6 < var5; ++var6) {
                    PropertyDescriptor origDescriptor = var14[var6];
                    name = origDescriptor.getName();
                    if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                        try {
                            value = this.getPropertyUtils().getSimpleProperty(orig, name);
                            this.copyProperty(dest, name, value);
                        } catch (NoSuchMethodException var10) {
                        }
                    }
                }
            }

        }
    }

这里使用是赋值 演示一下

public class YY {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        DepartmentidDto department=new DepartmentidDto();
        department.setDepartmentid(1);
        Department departmentid=new Department();
        BeanUtils.copyProperties(departmentid,department);
        System.out.println(departmentid.toString());


    }
}
22:45:49.411 [main] DEBUG org.apache.commons.beanutils.BeanUtils - BeanUtils.copyProperties(Department(departmentid=null, departmentname=null, managerid=null), DepartmentidDto(departmentid=1, departmentname=null))
22:45:49.424 [main] DEBUG org.apache.commons.beanutils.converters.IntegerConverter - Converting 'Integer' value '1' to type 'Integer'
22:45:49.424 [main] DEBUG org.apache.commons.beanutils.converters.IntegerConverter -     No conversion required, value is already a Integer
Department(departmentid=1, departmentname=null, managerid=null)

假设是从A复制到B: 需求1:如果B中某字段有值(不为null),则该字段不复制; 也就是B中该字段没值时,才进行复制,适合于对B进行补充值的情况。 需求2:如果A中某字段没值(为null),则该字段不复制 也就是不要把null复制到B当中。 这个就留给大家,感兴趣可以加我微信我可以告诉你怎么解决这个问题

可以关注我微信公共号Java代码学习营地

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值