How can create an entity table using Hibernate that will foreign keys in it a primary key. Consider it to a joining table (with few other attributes) between two tables having many to many relationship between them.
Any sample code?
Any real reason not to use a PK? If the entity has other attributes I would say you are better off with one.
If you really dont want one you can use JPA's #EmbeddedId using a compound key
private UserSessionId id
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "userId", column = #Column(name = "user_id", nullable = false)),
#AttributeOverride(name = "sessionId", column = #Column(name = "session_id", nullable = false)) })
public UserSessionId getId() {
return this.id;
}
public void setId(UserSessionId id) {
this.id = id;
}
Then you can define a separate entity UserSessionId that contains both FK's, at least this is how Hibernate Tools generates compound key relationships
If there are no extra fields involved in the join (ie. foreign keys only). You can use the following technique, no java domain object is required, hibernate will figure it all out for you.
http://www.mkyong.com/hibernate/hibernate-many-to-many-relationship-example-annotation/
You can still have other columns in your table such as creation_date, but their values will need to be updated at the database level.
There are very good examples of composed PK in the JSR document, take a look in JSR 317 in link for persistence-2_0-final-spec.pdf, 2.4 Primary Keys and Entity Identity section have great examples.
Related
I'm struggling to find what feels like a good design for a Spring Boot CRUD REST API app that involves several OneToMany relationships w/ join tables. For example, consider this DB structure in MySQL which allows one "Recipe" to be associated with several "Recipe Categories":
create table recipes
(
id int auto_increment primary key,
name varchar(255)
);
create table recipe_categories
(
id int auto_increment primary key,
name varchar(64) not null
);
create table recipe_category_associations
(
id int auto_increment primary key,
recipe_category_id int not null,
recipe_id int not null,
constraint recipe_category_associations_recipe_categories_id_fk
foreign key (recipe_category_id) references recipe_categories (id)
on update cascade on delete cascade,
constraint recipe_category_associations_recipes_id_fk
foreign key (recipe_id) references recipes (id)
on update cascade on delete cascade
);
On the Java side, I'm representing the structures as JPA entities:
#Entity
#Table(name = "recipes")
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<RecipeCategoryAssociation> recipeCategoryAssociations;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_categories")
public class RecipeCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "name", nullable = false)
private String name;
// ... setter/getters ...
}
#Entity
#Table(name = "recipe_category_associations")
public class RecipeCategoryAssociation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_category_id", nullable = false)
private RecipeCategory recipeCategory;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "recipe_id", nullable = false)
#JsonBackReference
private Recipe recipe;
// ... setter/getters ...
}
This works OK, but my hang-up is that to persist/save a new Recipe via REST JSON API, the caller needs to know about the join table recipe_category_associations. For example a PUT request w/ this payload could add a new Recipe to the DB associating it with the "category foo" recipe category:
{
"name": "Chicken soup",
"recipeCategoryAssociations": [{
"recipeCategory": {
"id": 123,
"name": "category foo"
}
}]
}
Using this in the controller:
#PutMapping(path = PATH, produces = "application/json")
#Transactional
public #ResponseBody Recipe addNewRecipe(#RequestBody Recipe recipe) {
return recipeRepository.save(recipe);
}
To me, the inclusion of "recipeCategoryAssocations" key in the JSON payload feels weird. From the client POV, it doesn't really need to know the mechanism creating this association is a join table. Really, it just wants to set a list of recipe category ids like:
{
"name": "Chicken soup",
"recipeCategories": [123, 456, ...]
}
Any tips how best to accomplish this in nice way? It'd be nice if I can keep the REST implementation super clean (e.g., like I have now with one recipeRepository.save(recipe); call). Thanks in advance.
When writing software we expect requirement to change. Therefore we want to make sure our code will be flexible and easy to evolve.
Coupling our server response with our DB structure makes our code very rigid. If a client needs a new field or if we want to arrange the DB schema differently everything will change.
There are several different approaches to designing your software correctly. A common approach is called "Clean Architecture" and is outlined in a book by this title by the great Uncle Bob. The Book itself outlines the approach in high level but there are many example projects online to see what it means in action.
For example this article by my favourite Java blog:
[baeldung.com/spring-boot-clean-architecture][1]
If you are looking for something simpler, you can follow the ["3-Tier Architecture"][2] (not really an architecture in my mind). Separate your code in to 3 layer:
Controller/Resource/Client
Service/BusinessLogic
Repository/DataAccess
Each layer will use a different data object. the business logic layer will have the object in it's purest form without constraints regarding who will want to read it and where it is stored and will be mapped/converted to the objects in the other layers as needed.
So in your case you might have 3 (or more) different objects:
RecipeDTO
Recipe
model.Recipe (and model.RecipeCategoryAssociation etc.)
Make sure that the Business level object only have fields that makes sense from a business logic. The code in each layer will use the objects that are relevant to that layer. When a rest controller class for example calls the business logic server it will need to convert the DTO object to the Business level object for example. Very important to maintain this separation between layers
I am trying to understand the one-to-many mapping in Hibernate with a small example. I have a Product with a set of Part's. Here are my entity classes:
Part.java
#Entity
public class Part {
#Id
#GeneratedValue
int id;
String partName;
//Setters & Getters
}
Product.java
#Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
#Id
public String getSerialNumber() {
return serialNumber;
}
#OneToMany
#JoinColumn(name = "PRODUCT_ID")
public Set<Part> getParts() {
return parts;
}
// Setter methods
}
Then I tried to save some parts and products in my database and observed below queries generated by hibernate:
Hibernate: insert into Product (serialNumber) values (?)
Hibernate: insert into Part (partName, id) values (?, ?)
Hibernate: update Part set PRODUCT_ID=? where id=?
Here to add a record in Part table, hibernate generates 2 DML operations - insert and update. If a single insert command is sufficient to add a record in table then why hibernate uses both insert and update in this case? Please explain.
I know this is crazy old but I had the same problem and Google brought me here, so after fixing it I figured I should post an answer.
Hibernate will switch the insert/update approach to straight inserts if you make the join column not nullable and not updatable, which I assume in your case it is neither anyways:
#JoinColumn(name = "PRODUCT_ID", nullable = false, updatable = false)
If Part as composite element list then only two query will come. Please check and revert.
If its not a composite element , hibernate try to insert individual as a separate query and it will try to create relationship between them.
In earlier case hibernate will insert with relationship key.
**Hibernate: insert into Product (serialNumber) values (?)
Hibernate: insert into Part (partName, id) values (?, ?)**
In these two queries hibernate is simply inserting a record into the database.
At that stage hibernate is not creating any relationship between the two entities.
Hibernate: update Part set PRODUCT_ID=? where id=?
Now after making entity tables,hibernate is going to make a relationship between the two
by using the above third query...
The association is uni-directional, so Product is the owning side (because it's the only side).
Make the association bidirectional and make Part the association owner. That way you will avoid redundant updates because the foreign key values will be specified as part of insert statements for Part.
My spring boot project uses an existing database, I have a new model entity/table in my project that must have a foreign key constraint with an existing table in the database.
I've tried to find solution online but all the answers are for the case where both the tables are present as entities in that project and using some #ManyToOne, #OneToMany annotations.
I can't define those annotations because I don't have the reference table as an entity or model in my project.
Let's say I have class like:
#Entity(name = "user")
public class User {
#Id
#GeneratedValue
private long userId;
private long departmentId;
I want to put a foreign key contraint on the departmentId column to reference to id column of the existing department table that isn't defined as a model or entity in my project.
Thanks
Just do it as normal
example
#Column(name = "department_id")
private Department departmentId;
You can later access it Department.departmentId. Hope this helps.
Try it like this
#ManyToOne
#JoinColumn(name="(column name of current entity)", referencedColumnName="(column name in target entity)")
private Department departmentId;
you can skip the referencedColumnName if the column name is same in both the entities
Is it possible to Greate DDL using JPA with bidirectional mapping and without foreign key? If can, is it best practice?
#Entity
class Book{
int id;
String title;
#OneToMany(mappedBy="book")
Set<BookDetail> book_detail;
}
#Entity
class BookDetail{
int id;
String name;
String description;
#ManyToOne
Book book;
}
Yes. It is possible using a join table. It will have foreign keys of course.
#Entity
class Book{
#OneToMany
List<BookDetail> bookDetail;
}
#Entity
class BookDetail{
#ManyToOne(fetch = FetchType.LAZY)
Book book;
}
what is #JoinColumn and how it is used in Hibernate
You can't do it without at least one foreign key, since a DB needs to establish some connection between two entities - BookDetail and Book. It is possible to create two tables for these entities without a real foreign key by using plain integer attribute in BookDetail which will be storing a value of Book's id. But don't do that!
With a foreign key your DBMS generates constraints so it's known about the relationship and it prevents some unsafe deletions and insertions, so each BookDetail's row references existing Books one.
Without real foreign key you c accidentally remove a Book and you BookItem's
I have two entities :
#Entity
#Table(name="Registration")
public class Registration{
#Id
private UUID uuid;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, fetch = FetchType.LAZY)
#JoinColumn(name="registration", nullable = false)
private List<Payment> payment;
}
#Entity
#Table(name="Payment")
public class Payment {
#Id
private UUID uuid;
/*#ManyToOne(targetEntity = Registration.class) <-- MappingException: Repeated column in mapping for entity
private Registration registration;*/
}
This entities create two tables :
TABLE `registration` (
`uuid` binary(16) NOT NULL,
PRIMARY KEY (`uuid`))
TABLE `payment` (
`uuid` binary(16) NOT NULL,
`registration` binary(16) NOT NULL,
PRIMARY KEY (`uuid`),
CONSTRAINT `FK_jgemihcy9uethvoe3l7mx2bih` FOREIGN KEY (`registration`) REFERENCES `registration` (`uuid`))
I'm using Rest Service. I can access to
registration.payment
but not
payment.registration
why ? I need a relation oneToMany bidirectionnal ?
Yes, you need to add the payment.registration #ManyToOne relationship if you use it in your code.
Take into account that JPA allows you to map a SQL database model to an object oriented one. Once you have the mapping between your objects and your database, you always work at the object level. That's why, although you have the relationship in the database, your Payment object doesn't know anything about it unless you map it to an attribute.
Of course it applies when you are using you data model objects or performing JPQL or Criteria queries. If you use native queries you have access to the database model as it is.