About inheritance mapping and best practices in Java - java

I'm learning about the ways of mapping inheritance from database to java with JPA/Hibernate. I've found several examples of how to do it, but not how to apply it.
Now, I'm trying to apply this knowledge on a small project, but I run into a problem where I can't do it the way I thought it would be ideal.
About the code below, the problem is: I have an "Expense" class that records a new expense (credit card debt, etc.), this debt has a creditor, which can be a person (PF) or institution (PJ). A expense has only one creditor, but I'm forced to model with one of each subclass.
#Data
#Entity
#Table(name = "expense")
public class Expense {
// CODE
#ManyToOne
#JoinColumn(name = "creditorPF")
private CreditorPF creditorPF;
#ManyToOne
#JoinColumn(name = "creditorPJ")
private CreditorPJ creditorPJ;
}
#Data
#Entity
#Table(name = "creditor")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Creditor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idCreditor")
protected Long id;
#NonNull
protected String description;
}
#Getter
#Setter
#Entity
#Table(name = "creditor_pf")
#PrimaryKeyJoinColumn(name = "idCreditor")
public class CreditorPF extends Creditor {
private String cpf;
#Builder
public CreditorPF() {
super("");
}
#Builder
public CreditorPF(String cpf, String nome) {
super(nome);
this.cpf = cpf;
}
}
#Getter
#Setter
#Entity
#Table(name = "creditor_pj")
#PrimaryKeyJoinColumn(name = "idCreditor")
public class CreditorPJ extends Creditor {
private String cnpj;
#Builder
public CreditorPJ(String cnpj, String nome) {
super(nome);
this.cnpj = cnpj;
}
#Builder
public CreditorPJ() {
super("");
}
}
This works fine, but I don't think it's a good design, because the design is allowing one more creditor per subclass, even if I add validations to prevent it, the design would be semantically incorrect.
Is there a way I can get a design like this code below, but that I can get the subclass information when I retrieve the object through hibernate?
#Data
#Entity
#Table(name = "expense")
public class Expense {
// CODE
#ManyToOne
#JoinColumn(name = "creditor")
private Creditor creditor;
}

In case of getting the Credit type from THROUGH the Expense Object ,there's no way besides using instanceof or getClass() as #Chris said ,btw it's preferable to use composition over inheritence,since it doesn't introduce this problem and preserves database consistency since you can"t be FORCED to have nullable fields, and in your case you can implement it using a class Creditor containing an enum which holds the creditor type since it's know to you ,hope this helps !

Related

How is it possible that querying base entity in joined inheritance strategy also fetches sub entities?

I have an abstract base class:
#Inheritance(strategy = InheritanceType.JOINED)
#Getter
#Setter
#Entity
#ToString
public abstract class BillingDetails {
#javax.persistence.Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "pk_for_inheritance")
Long Id;
#NotNull
private String owner;
}
and one subclass extending base class
#Entity
#Getter
#Setter
#ToString(callSuper = true)
public class CreditCard extends BillingDetails{
#Basic(optional = false)
private String cardNumber;
#Basic(optional = false)
private LocalDate expDate;
#Basic(optional = false)
private String cardKey;
}
when I query against base Entity BillingDetails and print results like so:
List<BillingDetails> details=billingDetailsRepository.findAll();
details.forEach(System.out::println);
I get the following output:
CreditCard(super=BillingDetails(Id=1, owner=Mehmet Dogan), cardNumber=6145 1233 4577 2360, expDate=2022-05-03, cardKey=673)
My question is:
Although I understand in joined strategy hibernates joins related base and sub tables ,How is it possible that I can print properties of subclass CreditCard when my result list is of type BillingDetails and only Id and Owner properties are declared in my base class ?
What I have missed here was I think polymorphism. When I tried to get Class of results like
List<BillingDetails> details=billingDetailsRepository.findAll();
details.forEach(x-> System.out.println(x.getClass()));
I got the following output :
class com.rumlor.domainmodelmapping.models.inheritancemodels.CreditCard
so somehow I missed somewhere even if results are cast to List Of BillingDetails ,Hibernate polymorphed every sub instance to base entity for me.
additional check :
CreditCard card= (CreditCard) details.get(0);
System.out.println(card);
result :
CreditCard(super=BillingDetails(Id=1, owner=Mehmet Dogan), cardNumber=6145 1233 4577 2360, expDate=2022-05-03, cardKey=673)

Map string properties to JSONB

I've been trying map my string properties to Postgresql's JSONB using JPA. I did read perfect article by Vlad Mihalcea many times and also seen relative questions and problems with similar stuff. BUT I still have this exception org.postgresql.util.PSQLException: ERROR: column "json_property" is of type jsonb but expression is of type character varying every time when I'm trying insert something into my table.
And what even worse is - all these advices in similar questions were useful until I changed my entity class and made him inherits super class. And now situation is like this:
If #TypeDef and #Type on my child class and it works great
But I want use abstraction layer and set annotations, which I noticed above, to my base entity class and after that exception says me 'Hello! It's me again'
My hierarchy is pretty simple and here it is:
Base entity
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
#MappedSuperclass
public abstract class AbstractServiceEntity implements Serializable {
private Integer id;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
Child entity
#Entity
#Table(schema = "ref", name = "test_json_3")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
public class TestJson extends AbstractServiceEntity {
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
private String jsonProperty;
My table
create table ref.test_json_3
(
id serial primary key,
json_property jsonb
)
UPD
I've succesfully inserted record with JPA native query, but I had to unwrap my query into hibernate query. Not sure that it's the most convinient way to manage inserting data into DB. The my question is actual, I still need your help) Example with native query below.
Code snippent with result
#Repository
public class JpaTestRepository {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void insert(TestJson testJson) {
entityManager.createNativeQuery("INSERT INTO test_json_3 (json_property) VALUES (?)")
.unwrap(Query.class)
.setParameter(1, testJson.getJsonProperty(), JsonBinaryType.INSTANCE)
.executeUpdate();
}
Finally I found solution for my problem. Answer is - just use your #Column(columnDefinition = "jsonb") and #Type(type = "jsonb" via getters but not class properties.
entity definition
#Entity
#Table(schema = "ref", name = "test_json_3")
#NoArgsConstructor
#AllArgsConstructor
#Setter
public class TestJson extends AbstractServiceEntity {
private String jsonProperty;
#Type(type = "jsonb")
#Column(columnDefinition = "jsonb")
public String getJsonProperty() {
return jsonProperty;
}
You can try to add #TypeDefs under class TestJson:
#TypeDefs({
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class TestJson extends AbstractServiceEntity {
Alternate solution for mapping String to Jsonb type. Just add the following annotation on your string.
#ColumnTransformer(write = "?::jsonb")
private String jsonProperty;

EclipseLink how to delete orphans entities from the "one side" in a ManyToOne relationship

After searching an answer, I have seen the #PrivateOwner annotation but it doesn't solve my issue.
I'm using EclipseLink
here is the issue:
I have 2 Entities related with a 1:n bidirectional relationship.
A ComponentEntity can be related to one TransferDetailsEntity
A TransfertDetailsEntity is related to one or multiple ComponentEntity.
what I want to achieve is: when I delete the last Component referencing a TransfertDetails, this TransfertDetails should be deleted.
Same if I change the last reference to a TransfertDetails.
in short : As soon as a TransfertDetails is not referenced by any Component, it should be deleted.
As a workaround I call this method :
#Override
public void removeOrphanTransfer() {
for (TransferDetailsEntity transfer : transferDetailsRepository.findAll()) {
if (transfer.getComponents().isEmpty()) {
transferDetailsRepository.delete(transfer);
}
}
}
That works but it's not really efficient since it search through the entire table. and it's quite ugly...
here is the (simplified) code for Entities :
TransfertDetailsEntity:
#Entity
#Table(name = TransferDetailsEntity.TABLE_NAME)
#Getter
#Setter
#NoArgsConstructor
public class TransferDetailsEntity extends AbstractEntity {
[...]
#Id
#Column(name = ID, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
[...]
#OneToMany(mappedBy = "transferDetails")
private List<ComponentEntity> components;
[...]
}
ComponentEntity:
#Entity
#Table(name = ComponentEntity.TABLE_NAME, uniqueConstraints = #UniqueConstraint(name = ComponentEntity.TABLE_NAME
+ AbstractEntity.SEPARATOR + AbstractEntity.CONSTRAINT,
columnNames = { ComponentEntity.COLUMN_NAME_SERIAL, ComponentEntity.COLUMN_NAME_TYPE }))
#Getter
#Setter
#ToString(callSuper = true, exclude = { "parent" })
#NoArgsConstructor
public class ComponentEntity extends AbstractEntity {
[...]
#Id
#Column(name = ID, nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
[...]
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = COLUMN_TRANSFER_DETAILS)
private TransferDetailsEntity transferDetails;
[...]
}
As mentioned earlier, #PrivateOwner on the #OneToMany annotation (in TransfertDetailsEntity) doesn't work...
any help appreciated
There is no automatic JPA or EclipseLink feature that will do this for you, your application will have to handle this.
The easiest I can think of is on removal of a ComponentEntity, get the referenced TransfertDetailsEntity and check its components list to see if it has other ComponentEntity references and remove it if it does not. You should be removing each ComponentEntity reference from the TransfertDetailsEntity.components list when you delete it anyway, so this list should be up to date and not incur any database hits.

Hibernate storing identifiers of another entities not whole entities?

I'm building RESTful service on java using JERSEY and need to implement relationships between entities storing just identifier on another entity not whole. Is there any way to implements it in the hibernate?
I'm using something like this but it is not working.
#Entity
#javax.persistence.Table(name = "manager_user")
public class ManagerUser extends User {
#ManyToOne(targetEntity = ShopAdminUser.class)
private Integer shopAdminUserId;
//...
}
#Entity
#javax.persistence.Table(name = "shop_admin_user")
public class ShopAdminUser extends User {
#Lob
private String contactData;
public String getContactData() {
return contactData;
}
public void setContactData(String contactData) {
this.contactData = contactData;
}
}
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class User {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Integer id;
//...
}
It will be very comfortable for me to implement this.

javax.persistence annotations and inheritance

I have 4 persistent classes which all have the same fields (exactly) the only 3 difference between them is 1) the class name, 2) the table name and 3) the data. i am aware that this might seem strange to some but trust me there is a good reason which i won't go into here.
now, i'm using hibernate annotations to configure my class which should work like so:
#Entity
#Table(name = "store")
public class Store
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
.. and this does work, for a single stand-alone class, however there are many fields to map and i'd like to do it all in one hit for all four similar classes, ie:
public class StoreBase
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
#Entity
#Table(name = "store1")
public class Store1 extends StoreBase
{}
#Entity
#Table(name = "store2")
public class Store2 extends StoreBase
{}
#Entity
#Table(name = "store3")
public class Store3 extends StoreBase
{}
#Entity
#Table(name = "store4")
public class Store4 extends StoreBase
{}
however when attempting this i get the following exception:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: package.entities.Store1
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:672)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:546)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
i'm guessing this is because the super class is not being searched for the identifier?
is there a way to utilise inheritance in this context?
thanks, paul.
#MappedSuperclass
public class StoreBase
See docs for more info.
Have a look at #MappedSuperclass.

Categories