Our site uses Spring (5.2.8.RELEASE), Hibernate (5.4.19.Final), Tomcat (8.5.x), and SQL Server (2014). Recently, we noticed that the performance is bad in case of deleting a simple record. We found out that it is caused by duplicate foreign keys that were generated by Hibernate. When there is a new table or field, we let Hibernate generate needed new tables or keys (primary key or foreign key) by setting
hibernate.hbm2ddl.auto=update
This is only a one-time deal. After generating new tables or new keys, we prohibit Hibernate from updating any tables or keys by setting
hibernate.hbm2ddl.auto=none
However, hibernate.hbm2ddl.auto=update generates duplicate foreign keys on existing tables. For example, the site allows people to leave comments and upvote/downvote comments. Upvote/downvote table has references to Comment and Account tables, recording which comment was upvoted/downvoted and who did the vote.
Here is the code about the relationship between Vote and Account in Java. In CommentVote.java
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "account")
private Account account;
In Account.java:
#OneToMany(mappedBy = "account", targetEntity = CommentVote.class,
fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<CommentVote> commentVotes;
Here is what is shown in SQL Management Studio:
In the above picture, foreign key FK17o46... and FKmgsaf... are duplicates. For example, the foreign keys on the Account field:
If we delete the two duplicate keys and set hibernate.hbm2ddl.auto=update, then the they will be generated again. How can we prevent Hibernate from generating duplicate foreign keys if they already exist on tables while still letting it generating keys only for NEW tables or NEW fields?
Related
I am completely new to working with databases and a beginner to Spring Boot as well really so apologies for any wrong terminology/fuzzy logic. There are some similar questions to this on here but I have not found exactly the answer to what I was looking for so I decided to post. Ramble over.
I am reading an article about joining tables in Spring Boot. They have a teacher class and a course class and it is a one-to-many relationship. They don't have the full classes written out but it says that you can go into Teacher class and do:
#OneToMany
#JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private List<Course> courses;
or go to Course class and do:
#ManyToOne
#JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;
What threw me off is that the parameters given to JoinColumn were the same in both cases. Assuming that both tables have something called ID, how does Spring know which one to use? Does it start by looking at both tables, looking for teacher_id. Then, after finding that it switches to the other table to get the ID?
Actually this is in the methodology of One-To-Many mappings in relational databases.
To achieve 1-N relation you only need to have:
Table One -> primary_key
Table Many -> primary_key, fk_one_primary_key
With the above configuration, given a table One entity, you can get all it's Many relations using the primaryKey-foreignKey join, and vice versa.
Now, in the code you've shared, the two parameters are described as:
ID -> Primary key column of table One / Teacher
TEACHED_ID -> foreign key column of table Many / Course
As I am new to Spring boot. I am not at all clear about mappings. By using #Onetomany mapping in one entity and #manytoOne mapping at other entities. Using the controller I have to write REST API functions to insert multiple users at a time inside an array or set. Can anyone please suggest some websites or provide some existing codes?
The #OneToMany and #ManyToOne mappings can be used according to your use-case, whether you need bi-directional mappping or not. For a simple example consider the following :
#Entity
#Table(name="ENTITY_A")
public class EntityA{
//...
#OneToMany(mappedBy="EntityA")
private Set<EntityB> entityBItems;
// getters and setters
}
#Entity
#Table(name="ENTITY_B")
public class EntityB{
//...
#ManyToOne
#JoinColumn(name="entityA_id", nullable=false)
private EntityA entityA;
public EntityB() {}
// getters and setters
}
What you need to look out for is the owning side of the relation indicated by the mappedBy . The owning entity can be used to persist and get the data from the database. But from the description in your question I cannot understand whether you actually need to use mappings at all as you just have to insert multiple users into a table without any relations to another entity. It will be more helpful if you could explain more about your use case and provide code samples for furthur analysis.
For details about the mappings article or article .
Official doc .
MappedBy signals hibernate that the owner of key (relationship) is on the other side.
This means that although you link 2 tables together, only 1 of those tables has a foreign key constraint to the other one.
MappedBy allows you to still link from the table not containing the constraint to the other table.
If you still want use #JoinColumn on both the Entities you can use #JsonManagedReference and #JsonBackReference from com.fasterxml.jackson
To save the multiple records at same time you can use yourRepository.saveAll(entities)
I want to have an entity that have a many to one relationship with another entity, but with generated foreign key using JPA (no foreign key in the database), is it possible?
I know there is a solution using one to many and many to one, but I want to only have a many to one, because I only want it to be a unidirectional
You can have OneToMany and ManyToOne, both in unidirectional and bidirectional way. Obviously, when you have a many-to-one relation from one side, you will have a one-to-many from the opposite side.
Also, you should note that only one foreign key in the many-side to one-side can handle this relation.
If you use #JoinColumn(name="some_column_name") just below one of the #OneToMany or #ManyToOne annotations, the hbm2ddl should be able to create the proper foreign key in your table.
However, try not to rely on hbm2ddl and maintain the database schema yourself.
I have the following relation and the foreign key is always empty in the audit table after the new revision:
#ManyToOne
#Audited(targetAuditMode=RelationshipTargetAuditMode.NOT_AUDITED)
#JoinColumn(name="mail_iid")
#private Mail mail;
...
#OneToMany(cascade=Cascade.ALL, orphan = true, fetch= fetchType.LAZY)
#JoinColumn(name="mail_iid")
private List<Attachments> attachments;
After the insertion of a new register, the original table have the iid but not the revision one.
Somebody knows about this issue.
There is only one way for this to happen, which is not managing bidirectional relationships properly.
I suspect you are never calling Attachments#setMail to assign the newly created Mail entity to the Attachments entities and instead simply add the Attachments entity to the collection that your Mail entity cascades.
This type of one-sided maintenance of bidirectional relationships is wrong and can lead to really incorrect results, particularly if entity instances are being inspected from the 1LC and are never being refreshed from the database; which is precisely why you're seeing the audit table with null in your mail_iid field.
Your code should make sure that both sides of the relationship get set properly
// setup bidirectional mappings
attachments.setMail( mail );
mail.getAttachments().add( attachments );
When you do it this way, you'll end up with mail_iid being populated in your audit table as you would have expected and also avoids any issues when traversing cached instances of an entity's object graph that is already loaded in the 1LC.
In my entity, I have defined a #ManyToOne(optional = false, cascade = CascadeType.PERSIST) to my other entity. The relationship works fine.
Now I am using Postgre DB where I have created my tables and defined foreign keys between columns in my tables. In my DB, those keys work. When I use my Spring Boot application with it, Spring for some reason creates hashed foreign keys (fk1sishw42l6qx85h5f3pckl6d0) instead using the ones that I have created. At least I think, it uses those.
Why is that so?
How to avoid that?
Hope this solution works, as it worked for me.
#ManyToOne
#JoinColumn(name="employeeID")
#ForeignKey(name='your_existing_constraint_name')
private employee employee;