I am trying to build a simple SpringBoot and Hibernate app using DAO and DTO pattern.
I am trying to save a list of users to the database.
When I am using User class it works fine, but when I am trying to use DTO CreateUserDto class I am getting the following error:
"Unknown entity: com.app.sportapp.dto.CreateUserDto; nested exception is org.hibernate.MappingException: Unknown entity: com.app.sportapp.dto.CreateUserDto"
There is a SingleTable inheritance where Player class and Coach class inherit User class.
User.java
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
#Entity(name = "Users")
#ApiModel(description = "All details about user")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "User_Type", discriminatorType= DiscriminatorType.STRING)
public class User implements Seriaalizable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String username;
private String email;
private String password;
private String contactNumber;
}
Player.java
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Getter
#Setter
#Entity(name = "Players")
#DiscriminatorValue(value = "player")
#DiscriminatorOptions(force=true)
public class Player extends User {...}
Coach.java
#Entity(name = "Coaches")
#DiscriminatorValue(value = "coach")
#DiscriminatorOptions(force=true)
public class Coach extends User{
}
And here are DTO's:
CreateUserDto.java
public class CreateUserDto {...}
PlayerDto.java
public class PlayerDto extends CreateUserDto{...}
CoachDto.java
public class CoachDto extends CreateUserDto{
}
As I am very new to DAO and DTO pattern from error I am getting I assume that it is expected to have a model with #Entity called CreateUser so same name as DTO CreateUserDto? Or can I have the example what I did to have a User model and create a new CreateUserDto?
Thanks!
The error happens because you are treating a DTO as an entity.
Remove the JPA annotations from the DTOs and don't use those classes for connecting to the db.
You will convert the results from your queries from entities to DTO and vice-versa.
I would also suggest to have a look at Mapstruct for the creation of DTO. This will probably make it easier to separate the entities from the DTOs.
Related
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)
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 !
#Entity
#Table(name = "DISC")
#NoArgsConstructor
#AllArgsConstructor
#Data
#Builder
#ToString
#EqualsAndHashCode(of = { "discId" })
public class Disc implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#ReturnInsert
#Column(name = "DISC_ID")
private Long discId;
..
}
and
#Repository
public interface DiscRepository extends JpaRepository<Disc, Long> {
...
}
but when I save using saveAndFlush() I have this error:
org.springframework.orm.jpa.JpaSystemException: ids for this class must be manually assigned before calling save():
Looks like you don't set the discId field, as simple as that.
If you want to delegate this to the framework use #GeneratedValue (and set strategy which corresponds to your DB). The framework will handle the ids generation for you.
Is there possibility to add new column in subclass of entity? Something like:
Base class:
#Entity
#Table(name = "users")
public class User {
#Column
private String login;
}
Subclass:
#Entity
#Table(name = "users")
public class UserWithField extends User {
#Column
private String field;
}
I don't want to change base class (User) at all. Would it work somehow?
Just create new class for new entity. Generally,In hibernate inheritance is used when you have two or more related tables.
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.