After asking the following question before : rest api and polymorphism
I wonder maybe my whole db schema and class structure is wrong.
the current structure is:
PERSON BASE ABSTRACT CLASS contains name and and age
CHILD EXTENDS PERSON contains favorite TV show
PARENT EXTENDS PERSON contains a list of children
GRANDPARENT EXTENDS PERSON contains a list of parents
In db the tables organized in table for each subtype
I thought of maybe refactoring the classes since the subtybes don't add any valuable fields, except to list of children/parents to this, only one class that include all the
fields:
#Entity
class Person{
#ManyToOne
private Person parent; //parent==null is a grandparent
#OneToMany
private List<Person> children;
}
However, the problem with this is that in my business logic, parent child and grandparent do have different behaviors, and this way its harder to distinguish
furthermore, a child parent can not be a grandparent.
Another problem is, say I am staying with the current class structure
separating the classes doesn't really helps me because I am using a service layer
and I have no way to no which service class I need, example :
class PeopleController extends Controller {
public Result savePerson() {
Person p = objectMapper.readValue.. // deseralizes to correct subtype
// saving logic is different for each subtype, hence I need to
// find correct repository for this subtype but I don't want to use
// instance of or switch case, but to use polymorphism, but can't think
of a way without implementing active record which I don't want
}
}
The determination of type can be completely determined based on state and it doesn't require the use of polymorphism as your question hasn't provided any real basis for using it in the first place. The attributes you've defined as collecting seem reasonable for all Persons.
A Parent is any person that contains a non-empty child set.
All Person instances constitute a Child, but for this exercise it could imply any person that contains an empty child set.
A GrandParent is any person that is a Parent, but also requires that at least one of its instances in the child set be a Parent.
With that in mind, we could consider restructuring the data model as follows.
#Entity
public class Person {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer age;
// any person can have this, not just children imo :)
private String favoriteShow;
#OneToMany(mappedBy = "parent")
private Set<Person> children;
#ManyToOne
private Person parent;
#Transient
public boolean isChild() {
return children == null || children.isEmpty();
}
#Transient
public boolean isParent() {
return !isChild();
}
#Transient
public boolean isGrandParent() {
return isParent()
&& children.stream().filter( Person::isParent ).count() > 0;
}
}
Even with this approach, your logic can branch based on the boolean transient method checks. There are ways you can optimize these methods of course but I wouldn't be too worried about that as these checks are already well optimized on JDK8.
The benefit here though is you can simply have a Person service your controller interacts with and most likely a Person repository since the data is aligned as one type.
I realize your saving logic is different per type, but my question is does it really have to be different? Perhaps more in-depth reasoning why these need to be split can help give us more context.
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;
}
I'm using Spring Data Neo4j and I'd like to pull a list of owners. An owner can be a :Person (human) or an :Entity (company). I'm not sure what Type<T> should be. I could use in the GraphRepository<Type> interface.
My Query is:
MATCH ()-[r:OWNED_BY]->(o) RETURN
And this is the code I tried:
public interface Owners extends GraphRepository<Object> {
#Query("start o=MATCH ()-[r:OWNED_BY]->(o) RETURN o;")
Iterable<Object> getOwners();
}
I had an idea that I could perhaps extend a common base class, such as PersistentBaseObject with an id and a name, or an interface like HasIdAndName. Not sure how I'd integrate this though,
Yes,you could extend a common base class, perhaps like this-
public class Owner {
Long id;
String name;
...
}
public class Person extends Owner {
private int age;
...
}
public class Entity extends Owner {
private String location;
...
}
And add a matching repository for Owner
public interface OwnerRepository extends GraphRepository<Owner> {
}
which will allow you to do stuff such as ownerRepository.findAll()
But since you're using a #Query, there is no reason you can't put this method on even the PersonRepository (at least in SDN 4. I'm not sure about SDN 3.x)
#Query("start o=MATCH ()-[r:OWNED_BY]->(o) RETURN o;")
Iterable<Owner> getOwners();
Note however, that now your Person and Entity nodes are going to have an extra Owner label.
EDIT:
The additional label can be avoided by changing Owner to an interface.
Then the #Query on a repository returning a collection of Owner should still work.
Whole day I have tried to find a answer on the question:
"How to add auto-Incrementing "id" field in an Entity class?".
I am using Morphia (a type-safe java library for Mongo DB). After a couple hours of digging in source code and googling I have found a LongIdEntity class in org.mongodb.morphia.utils package. Based on this class I have implemented the following solution. See below:
City class:
#Entity
public class City {
#Id
private Long id;
}
Hotel class:
#Entity
public class Hotel {
#Id
private Long id;
}
CityLongIdEntity class:
public class CityLongIdEntity extends LongIdEntity {
public CityLongIdEntity(Datastore ds) {
super(ds);
}
}
HotelLongIdEntity class:
public class HotelLongIdEntity extends LongIdEntity {
public HotelLongIdEntity(Datastore ds) {
super(ds);
}
}
DAO implementation:
CityDAO class:
public class CityDAO extends BasicDAO<City, Long> {
public CityDAO(Datastore ds) {
super(ds);
}
#Override
public Key<City> save(City c) {
if (c.getId() == null) {
CityLongIdEntity ent = new CityLongIdEntity(getDs());
getDs().save(ent);
c.setId(ent.getMyLongId());
}
return getDs().save(c);
}
}
HotelDAO class:
public class HotelDAO extends BasicDAO<Hotel, Long> {
public HotelDAO(Datastore ds) {
super(ds);
}
#Override
public Key<Hotel> save(Hotel h) {
if (h.getId() == null) {
HotelLongIdEntity ent = new HotelLongIdEntity(getDs());
getDs().save(ent);
h.setId(ent.getMyLongId());
}
return getDs().save(h);
}
}
Or you can see all this code on the Github
The UML diagram is also available:
All this code works as expected and I am happy, but I have a couple questions:
As you can see, for each Entity I need to create additional Entity, for example: for entity City I created CityLongIdEntity (this entity is crucial part of auto-incrementing functionality) . In this case, if my app will have 20 Entities (City, Address, Hotel, User, Room, Order etc.) I will need to create a 40 classes! I am afraid, but I think it will be "Code smell". Am I right?
Also, the Entity doesn't know about EntityNameLongIdEntity and EntityNameLongIdEntity has no idea who is Entity. And only specific EntityDAO class combines ans uses those classes together. Is it ok? Or it is again code smell?
Each EntityDAO class overrides extends BasicDAO class and overrides method save(). The difference between overrided methods save() for different DAO classes is minimal. I am afraid. that is code duplication and code smell again. Am I right?
please provide your opinion.
We require numeric IDs on some entities, but our implementation is a little different:
We use a regular ObjectId on all entities. Where required, we add a numeric ID.
There is a dedicated AutoIncrementEntity, which keeps a counter for different keys — that would be your class name.
We don't use DAOs but a generic save method, where we check if we have an instance of a class with a numeric ID. If that ID hasn't been set, we fetch one and update the AutoIncrementEntity. The relevant method isn't used at the moment — let me know if it's totally unclear and I'll finish that code.
Two more things from my implementation, which might be a little confusing:
You can always provide a starting number, so our numeric IDs could be 1000, 1001, 1002,... instead of 1, 2, 3,...
The key in the AutoIncrementEntity isn't required to be a class, it could also be a subset. For example, you want to number employees within a company, then the key for employees of company A would be employee-A, for company B company-B,...
I want to create a class type that has n-children of same type and one parent of same type as members. Every child class will have n-children and one parent. If I persist this model in datastore, will it be costly resource wise? Because each child has n-children and so on.
Say,
#PersistenceCapable
public class Human {
#Persistent
List<Human> children;
#Persistent
Human parent;
//the getters and setters
//null checks
//add child to list
//add parent
}
public class Test {
public static void main(String[] args){
Human parentHuman = new Human();
Human childHuman = new Human();
parentHuman.addChild(childHuman);
childHuman.addParent(parentHuman);
Human newChildHuman = new Human();
newChildHuman.addParent(childHuman);
newChildHuman.addChild(//another child);
}
}
I must also be able to get the number of children a human object has at any point of time and also the number of children of their children.
So will storing this type in Datastore create new entities for every child or will it refer to entity of the human object and reuse it?
If my question is not explanatory enough I can put some more light into my question. Any help/suggestion would be much appreciated. Thanks
The answer, in general, is that no, as long as you are using keys to establish relationships between entities, you will be safe from unnecessary inline duplication. Always use keys for entity relationships. For JDO, the specifics can be found here.
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.