Enterprise Java

Copy Specific Fields via Spring BeanUtils.copyProperties Example

1. Introduction

In this example, I will demonstrate all the available methods to copy specific fields via BeanUtils.copyProperties in a Spring application. The Spring Framework’s BeanUtils class provides three static void copyProperties methods to copy properties from one bean to another.

2. Setup

In this step, I will create a gradle project via Spring Initializr with the Lombok dependency. Here is the generated build.gradle file.

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.4'
	id 'io.spring.dependency-management' version '1.1.7'
}

group = 'org.zheng.demo.spring.beanutils'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}

3. Java Beans

A JavaBean is a standard Java class with three conventions: all properties are private with getters and setters, has a public no-argument constructor, and implements the Serializable interface. In this step, I will create four Java Beans.

  • DtoPOJO – has six data members: age, email, isValid, name, notes, and rank.
  • ModelPOJO – has five data members: age, isValid, name, notes, and rank.
  • EditableTarget – has three data members: age, name, and rank. It is a subset of DtoPOJO and ModelPOJO.
  • ModelPOJO2 – extends from EditableTarget and has a total of six data members: age, email, isValid, name, notes, and rank.

3.1 DtoPOJO

A DtoPOJO JavaBean has the following six data members:

  • private int age;
  • private String email;
  • private boolean isValid;
  • private String name;
  • private List <String> notes ;
  • private Integer rank;
copy specific fields beanutils copyproperties
Figure 1. DtoPOJO Class

DtoPOJO.java

package org.zheng.demo.spring.beanutils.dto;

import java.io.Serializable;
import java.util.List;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class DtoPOJO implements Serializable  {

	private static final long serialVersionUID = -6849421475135764851L;
	private int age;
	private String email;
	private boolean isValid;
	private String name;
	private List notes;
	private Integer rank;

}

3.2 ModelPOJO

A ModelPOJO JavaBean is similar to the DtoPOJO with two differences: no primitive data type and without the email field. It has the following five data members:

  • private Integer age;
  • private Boolean isValid;
  • private String name;
  • private List<String> notes;
  • private Integer rank;

ModelPOJO.java

package org.zheng.demo.spring.beanutils.dto;

import java.io.Serializable;
import java.util.List;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class ModelPOJO implements Serializable {

	private static final long serialVersionUID = -1821365191492214587L;
	private Integer age;
	private Boolean isValid;
	private String name;
	private List notes;
	private Number rank;

}

3.3 ModelPOJO2

A ModelPOJO2 JavaBean extends from EditableTarget and has three additional properties:

  • private String email;
  • private List<String> notes;
  • private Boolean isValid;

ModelPOJO2.java

package org.zheng.demo.spring.beanutils.dto;

import java.util.List;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
public class ModelPOJO2 extends EditableTarget {
	private static final long serialVersionUID = 9064042642762325288L;
	private String email;
	private List notes;
	private Boolean isValid;

}

3.4 EditableTarget

An EditableTarget JavaBean has the following three data members:

  • privat Integer age ;
  • private String name;
  • private Integer rank;

EditableTarget.java

package org.zheng.demo.spring.beanutils.dto;

import java.io.Serializable;

import lombok.Data;

@Data
public class EditableTarget implements Serializable {

	private static final long serialVersionUID = -3717196137057564979L;
	private Integer age;
	private String name;
	private Integer rank;
}

4. Copy Specific Fields Beanutils CopyProperties

In this step, I will copy the fields with BeanUtils.copyProperties methods utilizing the Java Beans created in step 3. Besides the exact type match, BeanUtils.copyProperties support primitive int to Integer copy. Here is the non-exhaustive set of examples of source and target property types that can be copied.

Source Property TypeTarget Property TypecopyProperties Supported?
IntegerNumberYes
NumberIntegerNo
booleanBooleanNo
BooleanbooleanNo
intIntegerYes
IntegerintYes

4.1 CopyDtoToModelTest

In this step, I will create a CopyDtoToModelTest.java to test the copyProperties methods.

  • test_copyProperties_defaultcopyProperties(source, target) method with the default copying. Note: the int to Integer and Integer to Number are copied.
  • test_copyProperties_with_editableTarget_exception – the copyProperties(source, target, EditableTarget.class) throws an exception because modelObj is not extended from the EditableTarget class.
  • test_copyProperties_with_editableTarget – using the ModePOJO2 as the destination object, noticed only the fields defined in EditableTarget are copied except the rank field. This is because the rank‘s type Number is not copied to the Integer type.
  • test_copyProperties_with_ignored_fields – verify the ignored fields are not copied.

CopyDtoToModelTest.java

package org.zheng.demo.spring.beanutils.dto;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;

class CopyDtoToModelTest {
	DtoPOJO dtoObj;
	ModelPOJO modelObj;

	@BeforeEach
	void setup() {
		dtoObj = new DtoPOJO();
		modelObj = new ModelPOJO();

		dtoObj.setAge(10);
		dtoObj.setRank(1);
		dtoObj.setName("Joe Smith");
		dtoObj.setValid(true);
		dtoObj.setNotes(List.of("note1", "note2"));
		dtoObj.setEmail("test@Test.com");

	}

	@Test
	void test_copyProperties_default() {
		BeanUtils.copyProperties(dtoObj, modelObj);
		// int to Integer is copied
		assertEquals(10, modelObj.getAge());
		// Integer to Number is copied
		assertEquals(1, modelObj.getRank());

		assertEquals("Joe Smith", modelObj.getName());
		assertEquals(2, modelObj.getNotes().size());
		assertEquals("note1", modelObj.getNotes().get(0));
		assertEquals("note2", modelObj.getNotes().get(1));

		// boolean to Boolean is NOT copied
		assertNull(modelObj.getIsValid());
	}

	@Test
	void test_copyProperties_with_editableTarget() {
		ModelPOJO2 targetObj = new ModelPOJO2();

		BeanUtils.copyProperties(dtoObj, targetObj, EditableTarget.class);

		assertEquals(10, targetObj.getAge());
		assertEquals("Joe Smith", targetObj.getName());
		assertEquals(1, targetObj.getRank());

		assertNull(targetObj.getEmail());
		assertNull(targetObj.getIsValid());
		assertNull(targetObj.getNotes());

	}

	@Test
	void test_copyProperties_with_editableTarget_exception() {
		assertThrows(IllegalArgumentException.class,
				() -> BeanUtils.copyProperties(dtoObj, modelObj, EditableTarget.class));

	}

	@Test
	void test_copyProperties_with_ignored_fields() {
		BeanUtils.copyProperties(dtoObj, modelObj, "notes", "rank", "name");

		// int to Integer is copied
		assertEquals(10, modelObj.getAge());

		// boolean to Boolean is NOT copied
		assertNull(modelObj.getIsValid());

		// ignored fields
		assertNull(modelObj.getName());
		assertNull(modelObj.getNotes());
		assertNull(modelObj.getRank());

	}

}
  • Line 33: copyProperties with only source and target. The assertions verified the data is copied.
  • Line 52: copyProperties with EditableTarget throw an exception if the target is not extended from EditableTarget.
  • Line 59: copyProperties with EditableTarget only copies the fields from EditableTarget.
  • Line 73: copyProperties copies the fields after ignoring the “notes“, “rank“, and “name” fields.

4.2 CopyModelToDtoTest

In this step, I will create a CopyModelToDtoTest.java to test the copyProperties methods.

  • test_copyProperties_defaultcopyProperties(source, target) method with the default copying. Note: the Integer to int is copied but Number to Integer is not copied.
  • test_copyProperties_with_editableTarget_exception – the copyProperties(source, target, EditableTarget.class) throws an exception because dtoObj is not extended from the EditableTarget class.
  • test_copyProperties_with_editableTarget – only the fields defined in EditableTarget are copied except the rank field because Number is not copied to Integer.
  • test_copyProperties_with_ignored_fields – verify the ignored fields are not copied.

CopyModelToDtoTest.java

package org.zheng.demo.spring.beanutils.dto;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.BeanUtils;

class CopyModelToDtoTest {
	DtoPOJO dtoObj;
	ModelPOJO modelObj;

	@BeforeEach
	void setup() {
		dtoObj = new DtoPOJO();
		modelObj = new ModelPOJO();

		modelObj.setAge(10);
		modelObj.setRank(1);
		modelObj.setName("Joe Smith");
		modelObj.setIsValid(true);
		modelObj.setNotes(List.of("note1", "note2"));

	}

	@Test
	void test_copyProperties_default() {
		BeanUtils.copyProperties(modelObj, dtoObj);
		// Integer to int is copied
		assertEquals(10, dtoObj.getAge());

		// Number to Integer is NOT copied
		assertNull(dtoObj.getRank());
		// Boolean to boolean is NOT copied
		assertFalse(dtoObj.isValid());

		assertEquals("Joe Smith", dtoObj.getName());
		assertEquals(2, dtoObj.getNotes().size());
		assertEquals("note1", dtoObj.getNotes().get(0));
		assertEquals("note2", dtoObj.getNotes().get(1));

	}

	@Test
	void test_copyProperties_with_editableTarget_exception() {
		assertThrows(IllegalArgumentException.class,
				() -> BeanUtils.copyProperties(modelObj, dtoObj, EditableTarget.class));

	}

	@Test
	void test_copyProperties_with_editableTarget() {
		ModelPOJO2 targetObj = new ModelPOJO2();
		BeanUtils.copyProperties(modelObj, targetObj, EditableTarget.class);

		assertEquals(10, targetObj.getAge());
		assertEquals("Joe Smith", targetObj.getName());
		// Number to Integer is NOT copied
		assertNull(targetObj.getRank());

		assertNull(targetObj.getEmail());
		assertNull(targetObj.getIsValid());
		assertNull(targetObj.getNotes());

	}

	@Test
	void test_copyProperties_with_ignored_fields() {
		BeanUtils.copyProperties(modelObj, dtoObj, "notes", "name");

		// Integer to int is copied
		assertEquals(10, dtoObj.getAge());
		// boolean to Boolean is NOT copied
		assertFalse(dtoObj.isValid());

		assertNull(dtoObj.getName());
		assertNull(dtoObj.getNotes());

		// Number to Integer is NOT copied
		assertNull(dtoObj.getRank());

		assertNull(dtoObj.getEmail());

	}

}
  • Line 33: copyProperties with only source and target. The assertions verified the data is copied.
  • Line 52: copyProperties with EditableTarget throw an exception because the dtoObj is not extended from EditableTarget.
  • Line 59: copyProperties with EditableTarget only copies the fields from EditableTarget .
  • Line 74: copyProperties copies the fields after ignoring the “notes” and “name” fields.

5. Demo

In this step, I will run the tests and capture the test results.

copy specific fields beanutils copyproperties
Figure 2. Test Results

6. Conclusion

In this example, I showed examples to copy specific fields beanutils copyproperties. When copying specific fields via the Spring BeanUtils.copyProperties method, the source and target classes do not have to match or even be derived from each other, as long as the properties match. Any bean properties that the source bean exposes but the target bean does not will silently be ignored.

7. Download

This was an example of a gradle project that copied specific fields between Java beans via BeanUtils.copyProperties.

Download
You can download the full source code of this example here: Copy Specific Fields via Spring BeanUtils.copyProperties Example

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button