1

I have two tables in database having one to many relationship. while I fetch the table User, I want to copy the data in the User (with the data related to Vehicle) to another object UserDuplicate (and VehicleDuplicate). I tried using BeanUtils.copyProperties but the nested references still refer to old object. I want to know what is the way to copy the nested objects. Thanks.

import java.util.Set;


public class User {

    private Set<Vehicle> vehs = new HasHSet();

    public Set<Vehicle> getVehs() {
        return vehs;
    }

    public void setVehs(Set<Vehicle> vehs) {
        this.vehs = vehs;
    }
}

class Vehicle {

    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;

    }
}

class UserDuplicate {

    private Set<VehicleDuplicate> vehDup=new HasHSet();

    public Set<VehicleDuplicate> getVehDup() {
        return vehDup;
    }

    public void setVehDup(Set<VehicleDuplicate> vehDup) {
        this.vehDup = vehDup;
    }   
}

class VehicleDuplicate {

    private UserDuplicate userDup;

    public UserDuplicate getUserDup() {
        return userDup;
    }

    public void setUserDup(UserDuplicate userDup) {
        this.userDup = userDup;
    }   
}
Aadil
  • 45
  • 3
  • 11
  • 1
    I don't understand why you have the _classes_ `VehicleDuplicate` and `UserDuplicate ` - they are **identical** to `Vehicle` and `User`; what possible purpose can they serve? – Boris the Spider Dec 21 '17 at 11:42
  • 1
    You have to create a recursive method to walk through the fields of your objects and call the clone method. Further, every object must implements/override clone method for doing that. **Loop over all fields in a Java class:** https://stackoverflow.com/a/17095665/1715121 – Ele Dec 21 '17 at 11:46
  • we have an external data ingestion to the the table and I am writing a job to copy data from one table to another after proper cleanup.So I am copying data from stagging to application tables with more or less same schema. – Aadil Dec 21 '17 at 11:46

2 Answers2

0

I like to use copy constructors in these cases:

class UserDuplicate {

...

UserDuplicate(User user) {

...

    if (user.getVehs() != null) {
        vehDup = new HashSet<>();
        for (Vehicle v: user.getVehs()) {
            vehDup.add(new VehicleDuplicate(this, v));
        }
    }
}

...

class VehicleDuplicate {

...

VehicleDuplicate(UserDuplicate userDup, Vehicle veh) {
    this.userDup = userDup;

...

}
Maurice Perry
  • 9,261
  • 2
  • 12
  • 24
  • I did the changes and called the constructor as :UserDuplicate u= new UserDuplicate(user); But I get UserDuplicate object containing vehileDuplicate field as an empty arraylist. – Aadil Dec 21 '17 at 12:43
  • @Aadil so what does it suggest to you? – Maurice Perry Dec 21 '17 at 12:48
  • @Aadil I can't help you there: my code obviously adds as many VehicleDuplicate as there are Vehicles. The problem is elsewhere... – Maurice Perry Dec 21 '17 at 12:53
  • Thankyou. I set the vehDup value inside constructor to the this object and it worked.. this.setVehDup(vehDup) thanks – Aadil Dec 21 '17 at 12:54
0

One approach could be to use a mapper to copy what you need to the duplicated object, this approach can have a lighter footprint on your code...

You can for example use jackson-databind

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
UserDuplicate duplicated = mapper.readValue(mapper.writeValueAsString(source), UserDuplicate.class);
// set the correct duplicate user in the duplicated vehicule
duplicated.getVehDup().forEach(x -> x.setUserDup(duplicated));

This will serialize the source object into JSON and then deserialize it into an instance of the duplicated object.

Because you are saying that your schemas are more or less the same you can take care of the annotations provided by Jackson for example to ignore some field.

static class User {

    @JsonIgnore // Allows to ignore attributes from stadging to production
    private String iDontWantToCopyThis = "blablabla";

    private Set<Vehicle> vehs;

    public Set<Vehicle> getVehs() {
        return vehs;
    }

    public void setVehs(Set<Vehicle> vehs) {
        this.vehs = vehs;
    }
}

Doing this the iDontWantToCopyThis field won't be copied into the duplicated object.

Because your Vehicle contains a reference to the user you need to annotate with @JsonIgnore to avoid the recursivity during the deserialization.

static class Vehicle {

    @JsonIgnore 
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;

    }
}

And because the name of the vehicule set is different into the UserDuplicate class you must use the annotation @JsonProperty("vehs") to let the mapper know how to match the datas.

 static class UserDuplicate {

    @JsonProperty("vehs") // need to specify the source name into the json used to load the user duplicated
    private Set<VehicleDuplicate> vehDup;

    public Set<VehicleDuplicate> getVehDup() {
        return vehDup;
    }

    public void setVehDup(Set<VehicleDuplicate> vehDup) {
        this.vehDup = vehDup;
    }   
}

If you have data transformations too complex to be processed just by the annotations you can also create custom serializer or deserializer...

Guillaume Barré
  • 4,168
  • 2
  • 27
  • 50