Consider the diagram:
I've been working with JPA for a short time and, so far, I have never had the need to persist extended classes... As you can see by the example, SNMPNode, IPNode, etc are all extended classes from Node that is also extended from GeoLocation.
I understand that I can annotate the master classes with #MappedSuperclass and IPNode and SNMPNode will inherit their properties for persisting... But in this scenario I will end up with nearly identical tables and, to my understanding, instead of doing that I could just group all information in Node and work with a single table.
Is this the way persistence of extended classes on JPA work or my concepts are wrong?
Same thing as a resumed piece of code:
public class Node extends GeoLocation {
private String name;
private Group group;
private Location location;
private Type type;
private Company company;
}
public class IPNode extends Node {
private Long id;
private String ipAddress;
}
public class SNMPNode extends Node {
private Long id;
private SNMPServer server;
}
[[ EDITED AFTER ANSWER FROM THIS POINT ]]
For the sake of contributing, here's a sample of what I end up doing:
INode:
public interface INode {
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
public String getIpAddress();
public void setIpAddress(String ipAddress);
public String getCommunity();
public void setCommunity(String community);
}
Node:
#Entity
#DiscriminatorValue("N")
#DiscriminatorColumn(name="NODE_TYPE",discriminatorType=DiscriminatorType.STRING, length=20)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Node extends GeoLocation implements INode {
#Id
#GeneratedValue
private Long id;
private String name;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
(... Overrides from INode ...)
}
IPNode:
#Entity
#DiscriminatorValue("I")
public class IPNode extends Node {
private String ipAddress;
public String getIpAddress() { return this.ipAddress;}
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
(... Overrides from INode ...)
}
SNMPNode:
#Entity
#DiscriminatorValue("S")
public class SNMPNode extends Node {
private String community;
public String getCommunity() { return community;}
public void setCommunity(String community) { this.community = community; }
(... Overrides from INode ...)
}
NodeRepository:
#Repository
public interface NodeRepository extends JpaRepository<Node, Long> { }
So now I can do stuff like this:
#ContextConfiguration("classpath:/spring/application-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class NodeRepositoryTest {
#Autowired
NodeRepository repo;
private INode node;
#Before
#Transactional
#Rollback(false)
public void setup() {
node = new IPNode();
node.setName("ipNode");
node.setIpAddress("1.1.1.1");
repo.save((IPNode)node);
node = new SNMPNode();
node.setName("snmpNode");
node.setIpAddress("2.2.2.2");
node.setCommunity("some text");
repo.save((SNMPNode)node);
}
#Test
#Transactional
public void Test() throws Exception {
INode testNode = repo.findOne(1L);
assertNotNull(testNode);
}
}
Both Node types are saved on the same table and so their keys can't repeat... My REST URL can grab them by /nodes/1 or /nodes/2, which was my main goal after all...
Thanks :)
If your base class is annotated with #MappedSuperclass then inheritance is only relevant in the OOP context. The #MappedSuperclass properties are simply copied to each sub-class associated database table and you can only query for sub-class entities.
Single table inheritance yields the best performance (no join or union is involved) at the price of not being able to declare not-nullable all specific sub-class properties (since all base and all sub-class specific properties go to a single database table).
With joined inheritance tables you can have the base class properties in a base database table, and each specific sub-class has it's own associated table. The sub-class table is linked with the base database table through a FK, so you need to join these tables to fetch a sub-class entity. As opossed to #MappedSuperclass, the base class is queryable since both the OOP context and the database reflect the inheritance model.
Related
All tables in our shop have the same set of identical audit columns(system, user, terminal, etc)
So instead of copying the same columns in every class I tried to use the embedded annotation
#Entity
#Table(name="PRODUCT")
public class ProdEntity implements Serializable{
private long prod;
//set/get code
private CommonJrnFields jrn;
#Embedded
public CommonJrnFields getJrn() {return this.jrn;}
public void setJrn(CommonJrnFields jrn) {this.jrn = jrn;}
private OrdEntity ord;
#OneToOne(mappedBy="prod")
public OrdEntity getOrd() {return this.ord;}
public void setOrd(OrdEntity ord) {this.ord = ord;}
}
#Entity
#Table(name="ORDERS")
#NamedQuery(name="OrdEntity.findAll", query="SELECT o FROM OrdEntity o")
public class OrdEntity extends ProdEntity implements Serializable{
private long ord;
//set/get code
private CommonJrnFields jrn;
#Embedded
public CommonJrnFields getJrn() {return this.jrn;}
public void setJrn(CommonJrnFields jrn) {this.jrn = jrn;}
private ProdEntity prod;
#OneToOne(mappedBy="ord")
public ProdEntity getProd () {return this.prod;}
public void setProd(ProdEntity prod) {this.prod = prod;}
}
#Embeddable
public class CommonJrnFields {
private String user;
private String system;
//set/get code
}
As a result embedded columns of extended class overrides base class and named query returns
ProdEntity.prod
OrdEntity.ord
OrdEntity.system
OrdEntity.user
instead of
ProdEntity.prod
ProdEntity.system
ProdEntity.user
OrdEntity.ord
OrdEntity.system
OrdEntity.user
How to fix it?
Please do not offer the #AttributeOverrides solution.
I'm working with almost 100 tables and do attribute override in each class for 8 common columns specifying table="" does not make any sense. It would be much faster just to cut/paste the embeddable code into all entities if there is no other solutions.
I'm using Neo4j ogm to map many (over 20) similar classes into neo4j db which are different in just
relationship name, name and direction.
Each class implements the "Classification" interface with just one method which is the same
in every class (consist on adding relation into collection of node)
Example node:
#NodeEntity
public class ExampleClass implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_1", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass (){}
public ExampleClass (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
and analogously:
#NodeEntity
public class ExampleClass2 implements Classification {
#GraphId
private Long id;
private String name;
#Relationship(type = "EXAMPLE_2", direction = "OUTGOING")
private Set<Species> classification = new HashSet<>();
public ExampleClass2 (){}
public ExampleClass2 (String name) {
this.name = name;
}
public Set<Species> getClassification(){
return classification;
}
#Override
public void specifiedAs(Species species){
classification.add(species);
}
}
I'm looking for possibility to reduce count of those similar classes
and create... maybe one generic class in which I can define label,property name and realtionship type also.
I prefer still using spring-data and Neo4j OGM.
You could improve this by introducing an super class containing all the common properties, and just have the specific relationships in your ExampleClassXXX classes.
Note that the relationship types cannot be dynamic, so you cannot have just a generic class by itself.
I am struggling with the right combination of inheritance strategy, and associations annotations when trying to persist abstract collections bidirectional with Hibernate.
All attempts I made so far fail, most with the complaint that #ManyToOne references an unknown identity.
Given the below example, the questions I have are
what is the best to maintain hibernate inheritence strategy for the
below structure?
which row id generation fits the inheritence strategy?
how to annotate each of the classes (e.g. #Entity, #MappedSuperclass)?
how to annotate the methods or field definitions (e.g. #ManyToOne, #OneToMany?
Since my code is embedded in a bigger structure and each class is rather big, here is a mock about cars - which is still big. Some relations I would model differently if I would really model cars, but this mock hopefully describes the basic problem in a more digestable form, without all the ballast from my real code.
The relations look like this:
the related java code looks like this:
package persistence;
public abstract class MechanicalObject {
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public abstract void someMethod();
}
package persistence;
import java.util.List;
public abstract class Car extends MechanicalObject {
protected List<Engine> potentialEngines; // references to something that also extends MechanicalObject
protected List<Driver> potentialDrivers; // references to something that does not extend MechanicalObject
public List<Engine> getPotentialEngines() {
return potentialEngines;
}
public void setPotentialEngines(List<Engine> potentialEngines) {
this.potentialEngines = potentialEngines;
}
public List<Driver> getPotentialDrivers() {
return potentialDrivers;
}
public void setPotentialDrivers(List<Driver> potentialDrivers) {
this.potentialDrivers = potentialDrivers;
}
}
package persistence;
public abstract class Engine extends MechanicalObject {
private int driveRPM;
private Car parent; // bidirectional reference
public int getDriveRPM() {
return driveRPM;
}
public void setDriveRPM(int driveRPM) {
this.driveRPM = driveRPM;
}
public void someMethod() {
// do something
}
public abstract void anotherMethod();
}
package persistence;
public class Driver {
// db row id
private long id;
private String name;
private String licenceType;
private Car parent; // bidirectional reference
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLicenceType() {
return licenceType;
}
public void setLicenceType(String licenceType) {
this.licenceType = licenceType;
}
public Car getParent() {
return parent;
}
public void setParent(Car parent) {
this.parent = parent;
}
}
package persistence;
public class CombustionEngine extends Engine {
// db row id
private long id;
private int cylinderCapacity;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getCylinderCapacity() {
return cylinderCapacity;
}
public void setCylinderCapacity(int cylinderCapacity) {
this.cylinderCapacity = cylinderCapacity;
}
public void anotherMethod() {
// do something differently
}
}
package persistence;
public class ElectroEngine extends Engine {
// db row id
private long id;
private int nominalVoltage;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getNominalVoltage() {
return nominalVoltage;
}
public void setNominalVoltage(int nominalVoltage) {
this.nominalVoltage = nominalVoltage;
}
public void anotherMethod() {
// do something
}
}
package persistence;
public class TeslaModelX extends Car {
private long id;
private String licenceNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLicenceNumber() {
return licenceNumber;
}
public void setLicenceNumber(String licenceNumber) {
this.licenceNumber = licenceNumber;
}
public void someMethod() {
// do something differently
}
}
package persistence;
public class VWBeetle extends Car {
private long id;
private String serialNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public void someMethod() {
// do something differently
}
}
I would be greatful for any help. Links to web pages that explain the solution are also welcome, as I couldn't find any that handeled this subject completely.
what is the best to maintain hibernate inheritence strategy for the below structure?
That depends on what the rest of your model looks like. The easiest to use strategy would be single-table but if the subclasses differ too much a joined-table strategy or table-per-class strategy might be better. Just read up on those and pick one you're comfortable with. Note, however, that it's hard to change inheritance strategy further down the hierarchy - if it's possible at all (probably depends on the JPA provider).
which row id generation fits the inheritence strategy?
I'd say those are independent as Hibernate should handle that for you. Just pick an id generation strategy that fits your needs. What you'd probably need though is a discriminator column to let Hibernate identify the entity class each row represents.
how to annotate each of the classes (e.g. #Entity, #MappedSuperclass)?
That depends on your needs but I'd assume that Car and Engine aren't that related so they'd be entities while MechanicalObject could be a mapped superclass. Of course, if you want some other entity to reference any MechanicalObject you might have to make it an entity as well, use a different mapping approach or rethink your requirements/model.
how to annotate the methods or field definitions (e.g. #ManyToOne, #OneToMany?
Do as needed and read up on the annotations. Just keep in mind that one side needs to be the owner of the relation, i.e. only changes to that side are persisted on the database. If you don't do that you risk corrupting your model and confusing the JPA provider. The easiest way to do that on bidirectional 1-n relations would be to add mappedBy="..." to the 1-side, i.e. to #OneToMany.
As an example, your Car-Driver relation could look like this:
class Car extends MechanicalObject {
#OneToMany( mappedBy="parent" )
protected List<Driver> potentialDrivers;
}
class Driver {
#ManyToOne
protected Car parent; //I'd use a better name here
}
I have a Base Class.
#Data
class BaseDocument{
String id;
String name;
//Other fields
}
Say I have many classes that extends BaseDocument one below.
class NoteDocument extends BaseDocument{
String description;
Long lastModifiedDate;
//etc
}
It does not make sense to me to send entire document to UI in some cases. Most of the cases I need only id and name.
So for every document I have a VO class.
#Data
class BaseVO {
private String id;
private String name;
}
#Data
class NoteVO extends BaseVO{
//Nothing here now
}
And in NoteDocument I have.
public NoteVO getVo(){
Assert.notNull(getId());
NoteVO noteVo = new NoteVO();
noteVo.setName(getName());
noteVo.setId(getId());
return noteVo;
}
Now I have to copy this method in all the classes that extends BaseDocument.
Instead, I changed my BaseDocument like below.
#Data
class BaseDocument<V extends BaseVO>{
String id;
String name;
public V getVo(Class className) {
Assert.notNull(getId());
V vo = null;
try {
vo = (V) className.newInstance();
vo.setName(getName());
vo.setId(getId());
} catch (IllegalAccessException|InstantiationException e){
e.printStackTrace();
}
Assert.notNull(vo);
return vo;
}
}
I am new to generics. My first question, is this a good practice. Are there any problems in using reflection to create instance, any performance issues? Is there any better way to do achieve (write less code) this.
Edit: Suppose I need to display note in UI, Along with note I need to display name of the User who created note. I am using mongodb, when I save the note I also save UserVO in note, which will have user id and name of the user. If I save only user id while saving the note, I will have to do one more query to get the name of user while displaying. I want to avoid this.
Do not use reflection; use inheritance and maybe covariant return types instead. It will be faster, clearer, more precise, and easier to maintain. You may also find it useful to add methods to populate your VOs incrementally. I didn't come up with a clean way to apply generics to this situation, but I don't think you need them:
class BaseVO {
String id;
String name;
void setId(String id) {
this.id = id;
}
void setName(String name) {
this.name = name;
}
}
class NoteVO extends BaseVO {
// ...
}
#Data
class BaseDocument {
String id;
String name;
//Other fields
protected void populateBaseVO(BaseVO vo) {
vo.setId(id);
vo.setName(name);
}
public BaseVO getVO() {
BaseVO vo = new BaseVO();
populateBaseVO(vo);
return vo;
}
}
#Data
class NoteDocument extends BaseDocument {
String description;
Long lastModifiedDate;
// ....
protected void populateNoteVO(NoteVO vo) {
populateBaseVO(vo);
// ...
}
public NoteVO getVO() {
NoteVO vo = new NoteVO();
populateNoteVO(vo);
return vo;
}
}
I am trying to delete all Type's from my Neo4j database. I have a repository for the Type class, typeRepository, which I call typeRepository.deleteAll();. However, not everything is deleted. Only its node is deleted leaving the BusinessLogic node alive in the database. I am not sure what else to try at this point, since it name of the method implies that it will delete all things including itself and things related to itself. Here is how my persisted class looks, which extends a base type of object that my database contains:
#NodeEntity
public class BaseType {
#GraphId
private Long id;
#Indexed(unique=true) String uid;
private String name;
BaseType() {}
BaseType(String name) {
this.name = name;
}
}
,
public class Type extends BaseType {
#RelatedTo(type="businessLogic")
#Fetch
private BusinessLogic businessLogic;
public Type() {super();}
public Type(String name, BusinessLogic businessLogic) {
super(name);
this.businessLogic = businessLogic;
}
}
,
#NodeEntity
#XmlAccessorType(XmlAccessType.NONE)
public class BusinessLogic implements Serializable {
#GraphId
private Long id;
private static final long serialVersionUID = -634875134095817304L;
#XmlElement
private String create;
public void setCreate(String create) {
this.create = create;
}
public String getCreate() {
return create;
}
}
I only store the Type instances, and I do that by calling
typeRepository.save(new Type(name, businessLogic));.
I don't think SDN is doing cascading delete on its own. So, why don't you first delete the BusinessLogic objects via their respective repository, and then the Type objects?