Hibernate: Inheritance strategy, and annotations when persist abstract collections bidirectional - java

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
}

Related

Max number of results from GraphQL

I am working on a project with GraphQL-java and Hibernate with MariaDB.
In my current solution, I get 18938 results back. I just want to see the last 10 of these. So I am looking for a solution to limit the number of results.
On the internet I see examples of limiting the number of results (https://graphql.org/learn/pagination/). They call it pagination. However, I cannot find the server implementation of this. Does anyone have experience with this?
I have an Entity class, with some properties : Test.java
#Entity
#Table(name = "test")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 64)
#Column(nullable = false)
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent")
private Test parent;
public Test() {
}
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 Test getParent() {
return parent;
}
public void setParent(Test parent) {
this.parent = parent;
}
My repository class: TestRepository.java
public interface TestRepository extends CrudRepository<Test, Integer> {}
My GraphQL resolver class: Query.java
#Component
public class Query implements GraphQLQueryResolver {
private TestRepository testRepository;
#Autowired
public Query(TestRepository testRepository) {
this.testRepository = testRepository;
}
public Iterable<Test> findAllTests(Integer first) {
return testRepository.findAll();
}
public long countTests() {
return testRepository.count();
}
}
My GraphQL schema: test.graphqls
type Test {
id: ID!
name: String!
parent: Test
}
#extend query
type Query {
findAllTests(first: Int): [Test]!
countTests: Int!
}
To summarize my last comment here is what I would do:
Instead of extending CrudRepository, extend PagingAndSortingRepository (which is extending CrudRepository)
public interface TestRepository extends PagingAndSortingRepository<Test, Integer> {
}
In your Query class pass two args to findAllTests method, page and size that will be used to create the Pageable object
#Component
public class Query implements GraphQLQueryResolver {
// other properties & methods are omitted for brevity
public Iterable<Test> findAllTests(Integer page, Integer size) {
Pageable pageable = PageRequest.of(page, size);
return testRepository.findAll(pageable).getContent(); // findAll returns Page and we can get the underlying List with getContent
}
}
Add two params from above in your GraphQL schema (I set default page size to be 20)
#extend query
type Query {
findAllTests(page: Int = 0, size: Int = 20): [Test]!
countTests: Int!
}
Since I have no experience with GraphQL, I'm not sure if this works, but you can give me feedback if there are some problems.

Hibernate with mysql overwrites pk

Well, I'm using Hibernate for the first time and, unexpectedly, it works. Except for one thing: an insert with a pk already inserted overwrite the record instaed of preventing it.
That's my simple code:
#Controller
public class SimpleController {
#Autowired
UserRepository userRepository;
#GetMapping("/mainPage")
public String viewMainPage(){
return "mainPage";
}
#GetMapping("/nuovo-utente")
public String viewInserisciUtente(Model model){
model.addAttribute("nuovoUtente", new Utente());
return "nuovo-utente";
}
#PostMapping("/nuovo-utente")
public String memorizzaUtente(#ModelAttribute Utente utente){
userRepository.save(utente);
return "output";
}
}
#Entity
public class Utente {
#Id
private int id;
private String citta=null;
private String genere=null;
private String data_nascita=null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCitta() {
return citta;
}
public void setCitta(String citta) {
this.citta = citta;
}
public String getGenere() {
return genere;
}
public void setGenere(String genere) {
this.genere = genere;
}
public String getData_nascita() {
return data_nascita;
}
public void setData_nascita(String data_nascita) {
this.data_nascita = data_nascita;
}
}
Any help will be appreciated.
EDIT: I've added the entity class to help you understanding my problem. Hoping that this will help.
Thanks you all
If you look at CrudRepository documentation, then we don't have update method, but we only have save method, which is used to add or update existing records.
In your case, you might have updated an entity (except its Id field) and tried saving the entity. So, CrudRepository will update the existing value for given Id, since it is already present.
Try adding ID generation strategy to id field.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

Spring Data REST hides technical entity fields (#Version, #Id) from JSON by default. How to return them as usual properties?

I have a base class
#MappedSuperclass
#Data //lombok annotation for getters/setter
public class BaseEntity implements Identifiable<Long> {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Long version;
}
For any derived class Spring Data REST returns JSON without "id" and "version" attributes.
I found 2 solutions:
Projections.
Add getters/setters with another names:
public Long getRevision() {
return version;
}
public void setRevision(Long revision) {
this.version = revision;
}
public Long getIdentifier() {
return id;
}
public void setIdentifier(Long identifier) {
this.id = identifier;
}
Both solutions look like hacks. Does better approach exist?
Showing the ID of the entity is configuring in the RepositoryRestConfigurerAdapter:
#Bean
public RepositoryRestConfigurerAdapter repositoryRestConfigurerAdapter() {
return new RepositoryRestConfigurerAdapter() {
/**
* Exposing ID for some entities
*/
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.exposeIdsFor(MyEntity.class);
super.configureRepositoryRestConfiguration(config);
}
};
}

What is the appropriate way persist extended classes?

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.

Generics in POJO - Is this a good practice

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

Categories

Resources