I have following classes
class A {
private Long id
private List<B> listB;
private C c;
...
}
class B {
private Long id
private A a;
private List<D> listD;
...
}
class C {
private Long id
private A a;
...
}
class D {
private Long id
private B b;
...
}
I need a copy of A, include all of it's properties except all id column.
I have 2 solutions:
1. Clone each object and set all of the ids null;
2. Make a constructor like this:
public A (A a){
//copy all properties except id
this.xxx = a.xxx;
...
}
But i need write so much code for this function, any one has some better method to implement this function?
Thanks a lot.
When you are saying Deep cloning of the object particularly the one of type Class A where you have an instance variable of a container type, you have below two widely known ways:
1) You Serialize and Deserialize the object.
2) You traverse through each method and call clone explicitely.
For first implementation, you may mark the id fields as transient and that should solve your purpose.
For second approach, you may override the clone method in each class where you set the id field as 0/-1) and call super.clone()
You can use Mapstruct , it's a powerful mapping library where you can configure all the fields exclusions for your specific class or nested class, without having to write all the getters/setters manually.
I personally use it for deep cloning jpa entities excluding ids or auditable fields.
Ex:
#Mapping(target="id",ignore=true")
EntityA cloneEntityAWithoutId(EntityA origin);
Mapstruct will generate for you the implementations using getters and setters of the EntityA, excluding the id field.
Obviously is a lot configurable, see the documentation I shared above.
Related
I'm new to java and spring. I have two classes
Class 1
class A {
Long id;
String attr1;
String attr2;
//getters & setters
}
Class 2
class B {
Long id;
String attr1;
String attr2;
String attr3;
String attr4;
#ManyToOne
A a;
//getters & setters
}
I want when I call my api by giving the id of an existing object of class A, I'll be able to insert the common attributes to the new object of class B.
Please what's the best way to to do this
what do you think about doing that way ?
#PostMapping("/test/{idObjectA}")
public B createNewB(#PathVariable Long idObjectA, #RequestBody B objectB){
//verify if ids are null..
A objectA = aRepository.getAbyId(idObjectA)
objectB.setAttr1(objectA.getAttr1);
objectB.setAttr2(objectA.getAttr2);
objectB.setA(objectA);
B result = bRepository.save(objectB);
return result;
}
You said REST, then follow the REST.
From your description, it looks like Object A is a container type and could have multiple instances of A (based on id), e.g. jobs object would have job-id
With that in mind
/test/A [HTTP POST] // POST, GET, PUT at container object level
To an item of A
/test/A/{id} // POST, GET, PUT at container's item level
To create a child resource B of A
/test/A/{id}/B // POST, GET, PUT at B level (which is a child of A)
Since you used #ManyToOne for B, it seems an instance of A could have multiple child items B
To access B
/test/A/{id}/B
To access an item of B
/test/A/{id}/B/{id}
A real life example of something like this would be:
mycompany/jobs/123/positions/1
Finally a word of note:
For most cases, you can use #OneToMany for Entity A (instead of #ManyToOne on entity B), because usually you would need to access child from parent. But if you need to access parent from child too, you could do bidirectional. i.e. use #OneToMany in Entity A and #ManyToOne in Entity B.
Coming to your question about using common properties between Object A and B
Some context first:
You can inherit properties (by same name) form a base entity. For example if you have a "person" as base entity - it could have name, last name, age etc. now an "employee", "manager" etc can inherit from "person". But in this example, person could really be an abstract class since you will create a person entity on its own. See this to learn more.
In a different example, if you have a "children" entity inherited from "parent" - both of these could independently exist and they could have same property names (name, last_name, age etc) - but they will have different data.
In your case it seems you not only want to have same properties for entity A and B, but you actually want to share the same data. That is a wrong design. It is not normalized DB design. You should ideally have no data duplication in DB.
But if you must do that (for some inexplicable reason), you can do something like this (use composition instead of inheritance):
Class SharedData{
String sharedAttr1;
String sharedAttr2;
}
Class A{
SharedData sharedAttributes;
String attrA1;
#OneToMany(). // if you want navigation from A to B
B b;
}
Class B{
SharedData sharedAttributes;
String attrB1;
#ManyToOne // if you want navigation from A to B
#JoinColumn(name="<identity_column_of_A>", nullable=false)
A a;
}
The main objective is to avoid duplication of fields with best inheritance model on existing stuff:
I have following class hierarchy:
class A
{
private String fieldA1;
private String fieldA2
// getters and setters
}
Class B extends A
{
private String fieldB1;
private String fieldB2;
// getters and setters
}
A 'User' class will either extend A or B having its own fields.
Assume existing API's uses above data models.
Now there is a requirement to add new fields in Class A for new set of API's.
I have certain restriction not to modify existing data models since it exposes or breaks existing API's with these new fields . So new data models need to be created by extending existing ones such that new API's uses them with all existing fields + new fields. I have below solution which may not be the best approach as it adds duplication of fields.
class A
{
private String fieldA1;
private String fieldA2
// getters and setters
}
Class B extends A implements Common
{
private String fieldB1;
private String fieldB2;
// getters and setters
}
Class NewA extends A
{
private String fieldNewA;
// getters and setters
}
Class NewB extends NewA implements Common
{
private String fieldB1;
private String fieldB2;
private String fieldNewB;
// getters and setters
}
Interface Common
{
//marker interface
}
Please suggest if there is any better approach.
Thanks...
I think you need to take a step back and look at what is it that is causing you to keep adding new fields to your existing classes. Maybe you should just have one field which is a collection of property and values. This way any new field will just be another property/value in your collection. You won't have to then worry about adding new fields frequently.
Your example is not great, for example sub-classes cannot access private fields and it is not clear what you intend your subclasses to achieve.
If you wish to inherit state, your example should use either protected or public fields, or show getter and setter methods being inherited. When inheriting state, implementing an interface like Common is redundant. More detail may help clarify your intent.
That aside, yes you can extend your data models as you describe in order to add data types to an existing class. An example of wanting to do this might be wishing to create a Square class from a Triangle class, requiring adding a new point. And also requiring changing behaviour to make use of the new field.
I have a parent class Product and some children class like Camera, TV, etc...
Now I want to create a method in Product that fetches from db common attributes to all children and returns an instance of a child, according to the object fetched from the db (I have a lot of children class).
Is it possible to do this? If yes, can you show me a little example?
My idea is to call this method from a similar method in child class and then fetching from db all those non-common attributes.
First, it seems like you'd want to put the db access code into your classes, which I wouldn't recommend.
As for your real problem: try and use an ORM framework such as EclipseLink or Hibernate. Those use a discriminator column to determine the actual class of an entity and create and populate the instance for you. As an alternative (if you can decide on the database) you could also have a look at ObjectDB.
I think you should have a look at decorator pattern
http://javarevisited.blogspot.co.uk/2011/11/decorator-design-pattern-java-example.html
public class Product{
int productId;
protected Product(int productId){ //avoid creating pure product objects
this.productId = productId;
//load all the common properties
}
public Product getProduct(int productId){
// read the product with productId from the table
// identify the type of the product
String type = .... (assume its "camera")
if("camera".equals(type)){
return new Camera(productId);
}
}
}
in the child class,
public class Camera extends Product{
public Camera(int productId){
super(productId);
}
}
usage
Product p = Product.getProduct(4025);
the method will load the relevant field from the database, identify the type of the product, create a subclass object based on the type of the product and call its constructor.
the constructor of the child class calls the super class constructor.
the super class constructor loads all the common properties.
Suppose i have a class A:
class A
{
private String value;
private B field;
public C otherField;
}
class C
{
private String otherValue;
}
class B
{
private String name;
}
Now, if i do a A.class.getClasses(), ill get an array with one element (the one which is public, something that makes sense based on what javadoc of Class.java says).
My question is: is there a way to get return a list of public + private fields of a class?
Thanks
The getClasses() method is not the correct way to access the Fields that are part of the A class. You need to use the getDeclaredFields() method to access an array of Field objects representing the fields in the class. From there, you'll need to set the accessibility of the field to true with a call to setAccessible(). There is more information available by looking into the Java Reflection API as well as here
You should notice, that getDeclaredField will only return the fields of the class which are declared in the class. Fields which are inherited from a super class will not be returned. To get all fields of a class you have to iterate over the super classes (using Class.getSuperclass()).
I have problems in mapping custom collection with JPA (Hiberante provider). For example when I am using object with attribute
List<Match> matches;
with
<one-to-many name="matches">
<cascade>
<cascade-all />
</cascade>
</one-to-many>
in my ORM file, it is allright; But if I replace "List matches;" by
private Matches matches;
,where "Matches" is defined like:
public class Matches extends ArrayList<Match> {
private static final long serialVersionUID = 1L;
}
It produces following error:
Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a #OneToMany, #ManyToMany or #CollectionOfElements: by.sokol.labs.jpa.MatchBox.matches
Thanks for your attention!
You can, but you have to refer to it as one of the common collections - List or Set.
so:
private List matches = new Matches();
Why? Because Hibernate makes proxies to your collections to enable lazy loading, for example. So it creates PersistentList, PersistentSet and PersistentBag, which are List but aren't Matches. So, if you want to add additional methods to that collection - well, you can't.
Check this article for more details.
You have a solution, however. Don't use inheritance, use composition. You can, for example, add a method to your entity called getMatchesCollection() (in addition to the traditional getter), which looks like:
public Matches getMatchesCollection() {
return new Matches(matches);
}
And your Matches class would look like (using google-collections' ForwardingList):
public class Matches extends ForwardingList {
private List<Match> matches;
public Matches(List<Match> matches) { this.matches = matches; }
public List<Match> delegate() { return matches; }
// define your additional methods
}
If you can't use google collections, simply define the ForwardingList yourself - it's calling all the methods of the underlying List
If you don't need any additional methods to operate on the structure, then don't define a custom collection.
Hibernate requires persistent collection-valued fields to be declared as an interface type (because they will be replaced with Hibernate's implementation for lazy loading purposes). From the reference documentation:
6.1. Persistent collections
Hibernate requires that persistent collection-valued fields be declared as an interface type. For example:
public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
}
The actual interface might be
java.util.Set, java.util.Collection,
java.util.List, java.util.Map,
java.util.SortedSet,
java.util.SortedMap or anything you
like ("anything you like" means you
will have to write an implementation
of
org.hibernate.usertype.UserCollectionType.)
Notice how the instance variable was
initialized with an instance of
HashSet. This is the best way to
initialize collection valued
properties of newly instantiated
(non-persistent) instances. When you
make the instance persistent, by
calling persist() for example,
Hibernate will actually replace the
HashSet with an instance of
Hibernate's own implementation of Set.
So your second approach is not possible, at least not the way you declared it. But to be honest, I don't really see the point.