Copying nested objects with references to each other - java

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;
}
}

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;
...
}

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...

Related

Is it possible to make a specific method working only with a specific constructor?

I have a class called User.java
public class User {
public User(Context context) {
}
public User() {
}
public void getUserId(){
}
public void getUserName(){
}
}
If I create an object of user class then I can reach to all methods such as getUserId and getUserName
User user_1 = new User(this);
user_1.getUserId();
user_1.getUserName();
User user_2 = new User();
user_2.getUserId();
user_2.getUserName();
The main question is can I get getUserName only if I called a constructor that has one parameter? But if I call a constructor that does not have a parameter then I can't get getUserName. Is it possible in Java?
There are multiple ways to achieve (roughly) what you want.
The easy, but possibly annoying way is to throw an Exception whenever a "forbidden" method is called:
public String getUserName(){
if (someRequiredContext == null) {
throw new IllegalStateException("method not allowed!");
}
// do stuff
}
This "works", but doesn't help the user avoid calling those methods, since there's no easy way for them to know if they can call getuserName on a given User object.
The more involved version would be to have two classes (for example User and UserWithContext), but then you can't instantiate them the right way. But you could use factory methods instead of constructors:
public abstract class User {
User() { ... };
public static User createUser() {
return new BasicUser();
};
public static UserWithContext createUser(Context context) {
return new UserWithContext(context);
}
public String getId() { ... }
}
class BasicUser extends User { // this class need not be public!
}
public class UserWithContext extends User {
UserWithContext(Context context) { ... }
public String getuserName() { ... };
}
This way the type will inform the users of your API which methods are allowed with a given User object.
No, it is not possible. But you could use interfaces to say for instance LimitedUser and FullUser
public interface LimitedUser {
int userId();
}
public interface FullUser extends LimitedUser {
String getUsername();
}
Then use factory methods on for instance the User implementation
public class User implements FullUser {
private final int id;
private final String username;
private User(int id, String username) {
this.id = id;
this.username = username;
}
public static LimitedUser createLimitedUser(int id) {
return new User(id, null);
}
public static FullUser createFullUser(int id, String username) {
return new User(id, username);
}
public int getId() {
return id;
}
public String getUsername() {
return username;
}
}

Delete all objects from hashmap that match specific condition

Following situation. I have:
public class Management {
private HashMap<Integer, Book> allBooks = new HashMap<>();
public void deleteAllBooksFromOwner(Owner owner) {
}
public class Owner {
private String name;
// getters
}
public class Book {
private Owner owner;
// getters
}
}
I want to write a method in Management class to delete books:
public void deleteAllBooksFromOwner(Owner owner){
}
I don't know how to access the book owner, for my comparison.
Getters are available.
You can use removeIf
public void deleteAllBooksFromOwner(Owner owner) {
allBooks.entrySet().removeIf(entry -> entry.getValue().getOwner().equals(owner));
}

Vaadin Table - AddNestedContainerProperty

public class LocationBasedRole extends AbstractEntity{
#ManyToMany(fetch=FetchType.LAZY)
private Set<Role> roles=new HashSet<Role>();
#ManyToMany(fetch=FetchType.LAZY)
private Set<Location> locations=new HashSet<Location>();
}
public class Role extends AbstractEntity{
private String name;
}
public class Location extends AbstractEntity{
private String location;
}
I have an entity named locationBasedRole which has 2 properties named roles and locations. Both roles and locations have a #ManyToMany relation with locationBasedRole.
Now I want to have one property of each in a Vaadin Table. It should be something like this,
public class UserForm extends OgsAbstractForm<User>{
MTable<LocationBasedRole> locationBasedRoleTable = new MTable<LocationBasedRole>().withHeight("100%").withWidth("100%");
#Override
protected Component createContent() {
Set<LocationBasedRole> lbRoles=new HashSet<LocationBasedRole>();
roles.addAll(locationBasedRoleFasade.findAll());
BeanItemContainer<LocationBasedRole> bean=new BeanItemContainer<LocationBasedRole>(LocationBasedRole.class);
//It returns an error on the next both lines and I know the reason, but don't know how to solve it.
// If it was no ManyToMany relation and the properties weren't a collection, it would work
bean.addNestedContainerProperty("roles.name");
bean.addNestedContainerProperty("locations.location");
bean.removeContainerProperty("persistent");
bean.removeContainerProperty("id");
bean.addAll(lbRoles);
locationBasedRoleTable.setContainerDataSource(bean);
return new VerticalLayout(locationBasedRoleTable);
}
}
When I remove the properties from the NestedContainerProperties it shows me at least something in the table.
bean.addNestedContainerProperty("roles");
bean.addNestedContainerProperty("locations");
I could use any help!
Thanks in advance!
So if I understand your question right, you want to have the Collections of your BeanItemContainer-Entity displayed in one column each?
I see two possibilities for that.
Option 1 - use a wrapper class for your Sets and use addNestedContainerBean
One possibility would be to not use Sets inside your LocationBasedRole but to use a wrapper class that extends HashSet.
Then you could use the addNestedContainerBean method.
I created a small example with the BeanItemContainer-Entity Team
public class Team {
private String teamName;
private Members teamMembers;
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public Members getTeamMembers() {
return teamMembers;
}
public void setTeamMembers(Members teamMembers) {
this.teamMembers = teamMembers;
}
}
Which consists of a name and teamMembers. The latter is of type Members:
public class Members extends HashSet<TeamMember> {
public String getMembers() {
return this.stream()
.map(member -> member.getFirstName() + " " + member.getLastName())
.collect(Collectors.joining(","));
}
}
Which is a simple wrapper for the Set that contains instances of TeamMember:
public class TeamMember {
private String firstName;
private String lastName;
private Integer age;
// getters and setters
}
As you can see in the Members class, there is a method getMembers which returns a String, containing a comma separated list of the team members names.
If we now use addNestedContainerBean("teamMembers") Vaadin tries to display all properties contained in the class Members. Vaadin will think getMembers is a getter for a String property called members and so generate a column for it.
Vaadin will also display a column "empty" because it will find the isEmpty method of Set and think empty is a property to display in a column. So we tell Vaadin to remove that column.
The final code of my example looks like:
protected Component createContent() {
Set<Team> teams=new HashSet<>();
for (int teamCounter = 0; teamCounter < 5; teamCounter++) {
Team team = createTeam();
addMembersToTeam(5, team);
teams.add(team);
}
BeanItemContainer<Team> bean=new BeanItemContainer<>(Team.class);
bean.addNestedContainerBean("teamMembers");
bean.removeContainerProperty("teamMembers.empty");
bean.addAll(teams);
teamTable.setContainerDataSource(bean);
return new VerticalLayout(teamTable);
}
The result looks like:
Option 2 - create fake getters and use addNestedContainerProperty
The only thing you have to do for this is extend your BeanItemContainer-Entity (LocationBasedRole) and create a fake getter for each Set you want to be displayed in a column. In your example those two fake getters could be public String getTheRoles() and public String getTheLocations(). Then you can use bean.addNestedContainerProperty("theRoles") and bean.addNestedContainerProperty("theLocations").
In my example my TeamMember class (the counterpart to your Role / Location classes) would still look like in the option above:
public class TeamMember {
private String firstName;
private String lastName;
private Integer age;
// getters and setters
}
And my Team class (your LocationBasedRole) would look like:
public class Team {
private String teamName;
private Set<TeamMember> teamMembers;
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public Set<TeamMember> getTeamMembers() {
return teamMembers;
}
public void setTeamMembers(Set<TeamMember> teamMembers) {
this.teamMembers = teamMembers;
}
public String getMembers() {
if (teamMembers != null) {
return teamMembers.stream()
.map(member -> member.getFirstName() + " " + member.getLastName())
.collect(Collectors.joining(","));
} else {
return "No members";
}
}
}
Now you can tell vaadin to add the (not existing) property "members" and Vaadin will find the getter getMembers and use this for generating the column. We also have to tell vaadin not to display the original "teamMembers" property. So the final code is:
protected Component createContent() {
Set<Team> teams=new HashSet<>();
for (int teamCounter = 0; teamCounter < 5; teamCounter++) {
Team team = createTeam();
addMembersToTeam(5, team);
teams.add(team);
}
BeanItemContainer<Team> bean=new BeanItemContainer<>(Team.class);
bean.addNestedContainerProperty("members");
bean.removeContainerProperty("teamMembers");
bean.addAll(teams);
teamTable.setContainerDataSource(bean);
return new VerticalLayout(teamTable);
}
and the result looks like:

Can I prefix a function with "get" in Springboot

I have a Mcq class associated to a MongoRepository, and I want to get an instance of my Mcq which apply several changes (Answers shuffle, Questions draw, etc). I declared my function "myMcq.getInstance()", but I can't do that because every time I want to send a Mcq in a ResponseEntity there is an error in the JSON output because Springboot thinks that there is a "instance" property in my class.
Here is my java class :
#Document(collection = "Mcqs")
public class Mcq {
#Id public String id;
#DBRef public User creator;
public String title;
public String categoryID;
public List<McqChapter> chapterList = new ArrayList<>();
public Difficulty difficulty;
public Mcq() {}
public Mcq(String title) {
this();
this.title = title;
}
public ArrayList<String> getQuestionsIDs() {
ArrayList<String> result = new ArrayList<>();
for (McqChapter chapter : chapterList) result.addAll(chapter.getQuestionIDs());
return result;
}
public McqInstance getInstance() {
return new McqInstance(this);
}
}
To prevent the error add #JsonIgnore to getInstance() method:
#JsonIgnore
public McqInstance getInstance() {
return new McqInstance(this);
}
Marker annotation that indicates that the annotated method or field is to be ignored by introspection-based serialization and deserialization functionality. That is, it should not be consider a "getter", "setter" or "creator".

How can I implement these 2 simple queries using Spring data JPA with the "query creation from method names" strategy?

I am pretty new in Spring Data and I have to write what in the official documentation seems to be called Query creation from method names, here the reference:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation
As you can see in the previous example show the creation of a query by the definition of a method name, for example:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
that I think return a list of Person object that have a specific email affress and a specific lastname.
So I am trying to do the same thing in my project that use Hibernate as JPA provider.
In my project I have this Twb1012Regione entity class that map the anagrafiche.TWB1012_REGIONE on the database:
#Entity
#Table(name="anagrafiche.TWB1012_REGIONE")
#NamedQuery(name="Twb1012Regione.findAll", query="SELECT t FROM Twb1012Regione t")
public class Twb1012Regione implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="COD_REG")
private String codReg;
#Column(name="COD_ARE_GEO")
private String codAreGeo;
#Column(name="COD_CIT")
private String codCit;
#Column(name="COD_IST")
private int codIst;
#Column(name="COD_PGM_ULT_MOV")
private String codPgmUltMov;
#Column(name="COD_UTE_ULT_MOV")
private String codUteUltMov;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="DAT_ORA_ULT_MOV")
private Date datOraUltMov;
#Column(name="DES_REG")
private String desReg;
//bi-directional many-to-one association to Tpg1029Provnuoist
#OneToMany(mappedBy="twb1012Regione")
private List<Tpg1029Provnuoist> tpg1029Provnuoists;
//bi-directional many-to-one association to Twb1013Provincia
#OneToMany(mappedBy="twb1012Regione")
private List<Twb1013Provincia> twb1013Provincias;
public Twb1012Regione() {
}
public String getCodReg() {
return this.codReg;
}
public void setCodReg(String codReg) {
this.codReg = codReg;
}
public String getCodAreGeo() {
return this.codAreGeo;
}
public void setCodAreGeo(String codAreGeo) {
this.codAreGeo = codAreGeo;
}
public String getCodCit() {
return this.codCit;
}
public void setCodCit(String codCit) {
this.codCit = codCit;
}
public int getCodIst() {
return this.codIst;
}
public void setCodIst(int codIst) {
this.codIst = codIst;
}
public String getCodPgmUltMov() {
return this.codPgmUltMov;
}
public void setCodPgmUltMov(String codPgmUltMov) {
this.codPgmUltMov = codPgmUltMov;
}
public String getCodUteUltMov() {
return this.codUteUltMov;
}
public void setCodUteUltMov(String codUteUltMov) {
this.codUteUltMov = codUteUltMov;
}
public Date getDatOraUltMov() {
return this.datOraUltMov;
}
public void setDatOraUltMov(Date datOraUltMov) {
this.datOraUltMov = datOraUltMov;
}
public String getDesReg() {
return this.desReg;
}
public void setDesReg(String desReg) {
this.desReg = desReg;
}
public List<Tpg1029Provnuoist> getTpg1029Provnuoists() {
return this.tpg1029Provnuoists;
}
public void setTpg1029Provnuoists(List<Tpg1029Provnuoist> tpg1029Provnuoists) {
this.tpg1029Provnuoists = tpg1029Provnuoists;
}
public Tpg1029Provnuoist addTpg1029Provnuoist(Tpg1029Provnuoist tpg1029Provnuoist) {
getTpg1029Provnuoists().add(tpg1029Provnuoist);
tpg1029Provnuoist.setTwb1012Regione(this);
return tpg1029Provnuoist;
}
public Tpg1029Provnuoist removeTpg1029Provnuoist(Tpg1029Provnuoist tpg1029Provnuoist) {
getTpg1029Provnuoists().remove(tpg1029Provnuoist);
tpg1029Provnuoist.setTwb1012Regione(null);
return tpg1029Provnuoist;
}
public List<Twb1013Provincia> getTwb1013Provincias() {
return this.twb1013Provincias;
}
public void setTwb1013Provincias(List<Twb1013Provincia> twb1013Provincias) {
this.twb1013Provincias = twb1013Provincias;
}
public Twb1013Provincia addTwb1013Provincia(Twb1013Provincia twb1013Provincia) {
getTwb1013Provincias().add(twb1013Provincia);
twb1013Provincia.setTwb1012Regione(this);
return twb1013Provincia;
}
public Twb1013Provincia removeTwb1013Provincia(Twb1013Provincia twb1013Provincia) {
getTwb1013Provincias().remove(twb1013Provincia);
twb1013Provincia.setTwb1012Regione(null);
return twb1013Provincia;
}
}
So, into my project I have defined a Twb1012RegioneRepository interface that is my repository class defined on the previous Twb1012Regione entity class:
#RepositoryDefinition(domainClass=Twb1012Regione.class, idClass=String.class)
public interface Twb1012RegioneRepository extends JpaRepository<Twb1012Regione, String> {
// I have to implement it
}
Now my problem is that I want to create 2 methods (that implement 2 queries by method name as described by the previous tutorial) that perform the following tasks:
1) Return the list of all the Twb1012Regione representing all the record of the TWB1012_REGIONE table on the DB.
2) Given a specific id (the value of the String codReg field, PK of the Twb1012Regione class) I want to obtain the Twb1012Regione object associated to this record.
How can I implement these queries? I have some difficulties to do it
Tnx
You don't need to implement the methods. The Spring Data Repository API will construct query for you as the JpaRepository already has following methods:
List findAll(Iterable ids)
T getOne(ID id)
That's the whole point with the Spring Data Repository - To reduce the boiler plate code that you write.

Categories