Troubles in annotation mapping hibernate with Spring MVC - java

Can someone please explain to me how to declare annotations on entities correctly?
There are two tables in the database. One table has a foreign key to a id of another one (#ManyToOne binding), both IDs are of Integer. And I created two entities to represent them in code.
How can I map these entities to DB? I had realized recently that Hibernate refers to classes. I mean that in #JoinColumn("???") I have to write the name of a column in DB, isn't it? What if the ID field's name in the entity is the same as in the DB? Thanks everyone in advance! Regards.
#Entity
#Table(name = "stat")
public class Statistic {
#Id
#Column( name = "statisticId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statisticsId;
...............
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn (name="userId") //<------------ The same name.
private User userStat;
//getters and setters + constr + overriding of ToString()
}
#Entity
#Table(name = "usser")
public class User {
#Id
#Column(name = "userId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;`enter code here`
................
#OneToOne(mappedBy = "userStat")
private Statistic statisticUser;
}

Related

JPA not saving foreign key in one-to-many relation

I have 2 tables with one-to-many relation on the owner class (Person) and many-to-one on the child class (Email)
My problem is that in the child class' foreign key is (person_id) is always null when I want to save my Person object. I tried different things using other questions' answers, but no luck.
I would like to solve this in an annotation approach, if it is possible.
Person Class:
#Entity
#Table(name="PERSON")
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PERSON")
#SequenceGenerator(name="SEQ_PERSON", sequenceName="SEQ_PERSON", allocationSize=1)
#Column(name = "person_id")
private Long personId;
#OneToMany(mappedBy="person", cascade=CascadeType.ALL)
private List<Email> email;
// getters and setters
}
Email class:
#Entity
#Table(name="EMAIL")
public class Email{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_EMAIL")
#SequenceGenerator(name="SEQ_EMAIL", sequenceName="SEQ_EMAIL", allocationSize=1)
#Column(name = "email_id")
private Long emailId;
#ManyToOne
#JoinColumn(name="person_id", referencedColumnName="person_id", insertable = true)
private Person person;
// getters and setters
}
I get no exception / errors when I use this.
When I change the JoinColumn to #JoinColumn(name="person_id", referencedColumnName="person_id", nullable = false, updatable = false, insertable = true) then I get this error: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.test.Email.person
I tried to change the Person's email setter like this, nothing changed:
public synchronized void setEmail(List<Email> email) {
this.email=email;
for(Email em: email) {
em.setPerson(this);
}
}
source
I have a Person object, with 2 emails (as a test object to save, every column is filled, except the FK in Email table), do I have to set the FK everytime manually? (it doesn't look good, if I have multiple one-to-many variables)
Edit: I tried this Which is working, but my problem with that if I have a very deep data structure with a lot of One-To-Many relations, I have to implement this to every variable and then save.. So, is there a better solution with pure annotations / getters-setters ?

JPA one to one mapping creates multiple query when child entity is not found

I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository

OneToOne relationship, keep only foreign key

I am trying to establish a OneToOne relationship between two entities (PartnerDetails and JWTData. How ever, I only want to store the primary key of PartnerDetails entity in JWTData, not the whole object, like this.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "partnerId")
private JWTData jwtData;
}
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne(targetEntity = PartnerDetails.class)
#JoinColumn(name = "partner_id", foreignKey = #ForeignKey(name = "fk_jwt_partnerdetails_partnerid"))
private String partnerId;
#NotBlank
private String secret;
}
But after fetching the JWTData using repository, Hibernate cannot convert the String to a PartnerDetails. Can this be done using any other way?
If you just add PartnerDetails to JWTData then JPA will know to use only the id. JPA is an Object Oriented framework so you should reference objects unless you specifically want a field. JPA handles the details for you. Note that in this configuration JWTData in the "owning" entity because of the mappedBy annotation, therefore only setting the partnerDetails field in a JWTData instance will persist the relationship to the database. The jwtData field in PartnerDetails is for query results only and makes for a Bidirectional instead of a Unidirectional mapping. Also, because of this, having a CascadeType setting generally only makes sense on the owning entity since it is the one handling the database updates and deletes.
When playing around with JPA be sure to turn on the SQL output so that you know what is actually happening.
#Entity
#Data
#Table(name = "partner_details")
public class PartnerDetails {
#Id
#Column(name = "partner_id")
private String partnerId;
#OneToOne(mappedBy = "partnerDetails")
private JWTData jwtData;
#Entity
#Data
#Table(name = "jwt_data")
#NoArgsConstructor
public class JWTData {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// even though it looks like the entire class it's only saving the id to the database.
#OneToOne
private PartnerDetails partnerDetails;

Same ID from sequence for 2 tables - JPA

I have 2 entities that use the same sequence as the primary key, how do I map?
Example:
#Entity
#Table("employeT")
public class Employe(){
#SequenceGenerator(name = "generator_id", sequenceName = "seq_id")
#GeneratedValue(generator = "generator_id")
#colunm(name = "id")
private Integer id;
#colunm(name = "nameEmp")
private String name;
#JoinColumn(name = "id")
private Computer computer;
}
#Entity
#Table("computerT")
public class Computer(){
#SequenceGenerator(name = "generator_id", sequenceName = "seq_id")
#GeneratedValue(generator = "generator_id")
#colunm(name = "id")
private Integer id;
#colunm(name="name_computer")
private String nameComputer;
}
I need save employe and computer with same id, generated by Employe save.
There are three things to do with your code to work the way to want to.
Add #OneToOne annotation to indicate that Employee and Computer are in relation.
Delete information about #SequenceGenerator from your Computer entity and add #Id annotation
Add #MapsId annotation. [More info]
So it would look something like this :
#Entity
#Table("employeT")
public class Employe(){
#Id
private Integer id;
#Colunm(name = "nameEmp")
private String name;
#OneToOne
#JoinColumn(name = "computer_id")
#MapsId
private Computer computer;
}
Why?
#OneToOne annotation indicates relation between entities.
#SequenceGenerator is redudant since we "copy" id from Computer entity.
#Id annotation is mandatory to indicate that this field is our primary key.
Last but not least, #MapsId annotation do the magic, where it 'borrows' id from relation.
More info in the link I attached earlier.

can someone please explain me #MapsId in hibernate?

Can someone please explain to me #MapsId in hibernate? I'm having a hard time understanding it.
It would be great if one could explain it with an example and in what kind of use cases is it most applicable?
Here is a nice explanation from Object DB.
Designates a ManyToOne or OneToOne relationship attribute that provides the mapping for an EmbeddedId primary key, an attribute within an EmbeddedId primary key, or a simple primary key of the parent entity. The value element specifies the attribute within a composite key to which the relationship attribute corresponds. If the entity's primary key is of the same Java type as the primary key of the entity referenced by the relationship, the value attribute is not specified.
// parent entity has simple primary key
#Entity
public class Employee {
#Id long empId;
String name;
...
}
// dependent entity uses EmbeddedId for composite key
#Embeddable
public class DependentId {
String name;
long empid; // corresponds to primary key type of Employee
}
#Entity
public class Dependent {
#EmbeddedId DependentId id;
...
#MapsId("empid") // maps the empid attribute of embedded id
#ManyToOne Employee emp;
}
Read the API Docs here.
I found this note also useful: #MapsId in hibernate annotation maps a column with another table's column.
It can be used also to share the same primary key between 2 tables.
Example:
#Entity
#Table(name = "TRANSACTION_CANCEL")
public class CancelledTransaction {
#Id
private Long id; // the value in this pk will be the same as the
// transaction line from transaction table to which
// this cancelled transaction is related
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_TRANSACTION", nullable = false)
#MapsId
private Transaction transaction;
....
}
#Entity
#Table(name = "TRANSACTION")
#SequenceGenerator(name = "SQ_TRAN_ID", sequenceName = "SQ_TRAN_ID")
public class Transaction {
#Id
#GeneratedValue(generator = "SQ_TRAN_ID", strategy = GenerationType.SEQUENCE)
#Column(name = "ID_TRANSACTION", nullable = false)
private Long id;
...
}
IMHO, the best way to think about #MapsId is when you need to map a composite key in a n:m entity.
For instance, a customer can have one or more consultant and a consultant can have one or more customer:
And your entites would be something like this (pseudo Java code):
#Entity
public class Customer {
#Id
private Integer id;
private String name;
}
#Entity
public class Consultant {
#Id
private Integer id;
private String name;
#OneToMany
private List<CustomerByConsultant> customerByConsultants = new ArrayList<>();
public void add(CustomerByConsultant cbc) {
cbc.setConsultant(this);
this.customerByConsultant.add(cbc);
}
}
#Embeddable
public class CustomerByConsultantPk implements Serializable {
private Integer customerId;
private Integer consultantId;
}
#Entity
public class CustomerByConsultant{
#EmbeddedId
private CustomerByConsultantPk id = new CustomerByConsultantPk();
#MapsId("customerId")
#JoinColumn(insertable = false, updatable = false)
private Customer customer;
#MapsId("consultantId")
#JoinColumn(insertable = false, updatable = false)
private Consultant consultant;
}
Mapping this way, JPA automagically inserts Customer and Consultant ids in the EmbeddableId whenever you save a consultant. So you don't need to manually create the CustomerByConsultantPk.
As he explained Vladimir in his tutorial, The best way to map a #OneToOne relationship is to use #MapsId. This way, you don’t even need a bidirectional association since you can always fetch the Child entity by using the Parent entity identifier.
MapsId lets you use the same primary key between two different entities/tables. Note: when you use MapsId, the CASCADE.ALL flag becomes useless, and you will need to make sure that your entities are saved manually.

Categories