I am using spring boot (verson 2.1.1) to create an application that needs to one-to-many & many-to-one relationship between two model classes with below requirements
The model classes are
#Entity
#Table(name="ORGANIZATIONS")
public class Organization{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
}
#Entity
#Table(name="DEPARTMENTS")
Public class Department{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
//…
}
Requirements
Both organizations and departments should be created by separate respective rest api's.
Through the POST /organizations api we should be able to create an organization without creating departments in the same api call. In fact the api should fail I tried to pass the json element for department as part of the POST /organizations call.
When calling POST /departments I should be able to pass the organization id to associate the newly created department with the organization.
The GET /organizations api call should return the Collection as part of the organization object
The questions are
How do I associate the two model objects ? Do I add #OneToMany in Organization? What attributes do I pass to #OneToMany? Do I need a similar #ManyToOne on the other side - department?
Do I need any special considerations on the REST controllers?
You will need #ManyToOne for persisting in Department only but you most likely will need #OneToMany in Organization for the GET request.
Just make sure, when saving the Department, that you need to:
Fetch from db the organization
Set the fetched organization on the department object
Add the department to the Organization.departments list
Persist the department
For the error handling return a BAD_REQUEST response:
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
Related
I start a project with spring boot, I use a database with 6 tables.
It is a CRUD application .
So , I have my entities/dto/service/controller/repository packages for 5 tables. (6 tables in SQL)
Now, I would like to update a row on the column of table x(SQL) of entity x to another entity y at a specific row.
In my opinion, it should be done at the service layer at create X, but how?
Should I create a xyDTO with data from 2 entities? I am afraid of doing it , the table y it doesn't update automatically.But when create the xyDTO. I don't want this.
How I can update the data of specific DTO x to another DTO y (6th table of SQL), at the same time
I cannot find similar example online. Could anyone help me?
My code:
#Entity
#Table(name = "repo")
public class Repo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
Long id;
#Column(name="stock")
private Long stock;
}
#Entity
#Table(name = "voucher")
public class Voucher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "quantity")
private BigDecimal quantity;
#Column(name = "type")
private boolean type;
}
#Service
public class VoucherService{
#Override
public Voucher dtoToEntity(VoucherDTO dto) {
Voucher voucher = new Voucher();
voucher.setId(dto.getId());
voucher.setDescription(dto.getDescription());
List<VoucherProduct> voucherList = new ArrayList<>();
for (VoucherProductDTOMini inv : dto.getVoucherproducts()) {
VoucherProduct voucherL = voucherProductService.DTOtoEntity(inv);
voucherList.add(voucherL);
}
voucher.setVoucherproducts(voucherList);
return voucher;
}
#Override
public VoucherDTO createVoucher(VoucherDTO voucherDTO) {
Voucher voucher=new Voucher();
voucher=voucherRepository.save(voucher);
VoucherDTO voucherDTOnew=new VoucherDTO(voucher);
return voucherDTOnew;
}
}
I should
check the type of my voucher (true), and I should add on my repo entity at
stock.
In which way can I update the two entities at the same time?
When I add a true voucher, I should add on my repo.stock the data of voucher.quantity.
First, I would like to highlight a few things in your code:
I cannot find the reason to use #Override annotation on those methods in your Service.
In createVoucher method you create completely empty entity, this is not a good thing to do.
DTO stands for Data Transfer Object, and usually in Spring applications it is used to transfer data to or from the service. For example: Controllers. When user makes a Http Request to receive all Vouchers for example, you would like to return VoucherDto with only those fields that you want users to see.
You can have different DTO objects for Getting entity values and Updating them. Because sometimes you want to allow users to update only certain properties.
To answer your question on how to update two entities in a single call. As I understood your question, you want to update different properties in two different entities via a single request. This is possible, though there are different approaches to this.
Create two different DTOs, one for each entity. Create two different Http Requests each would take one DTO and call a method in a service to update each Entity. ex.: VoucherController.updateVoucher -> VoucherService.updateVoucher and RepoController.updateRepo -> RepoService.updateRepo. I personally prefer this as a solution because your entities Voucher and Repo don't have any relation.
Create a single DTO object, containing all fields required to be updated, then in the service method find your Voucher, and Repo and update their fields, then save both entities. This would be a messy approach when you have many entities.
There would be another different approach if you would have a relation between your Repo and Voucher entities, for example a OneToMany.
Let me know whether this answers your question. There is nothing wrong to have many DTO objects and Services, etc.
If you would like to easily generate all the DTO objects, have a look at this: https://github.com/OpenAPITools/openapi-generator
I have created a Spring Boot JPA micro service with a MySQL back end. I have two simple entities in my model, users and skills. These are used to represent the skills base within my organisation. Each user may have many skills and so I have used a one to many join to model this and can successfully assign skills to users. In my database, I can see that a users_skills table has been created by JPA to achieve this.
However, I am now struggling because, for each skill that a user has, an experience level needs to be specified (e.g. basic, advanced, expert) and I am unsure how to achieve this. I'm not sure whether 'levels' should just be an enum type within the Skill entity, or perhaps it should be a separate entity in its own right? Could I configure JPA so that it generates a users_skills_levels table which would represent this three-way relationship? Any advice would be most welcome!
These are my Entity classes: -
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private String email;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Skill> skills = new HashSet<>();
getters and setters
}
#Entity
#Table(name = "skills")
public class Skill {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
getters and setters
}
That's not possible what you try to achieve.
You should create an Entity for the users_skills_levels. E.g. UserSkillLevel This entity will then have a ManyToOne relationship to User and a ManyToOne relationship to Skills plus the attribute level.
The User has a collection of UserSkillLevel and the Skill entity as well.
Please find a more in-depth example here:
https://thoughts-on-java.org/many-relationships-additional-properties/
I have recently started on a project that uses Spring Boot. I am still learning some concepts but some things related to data access are bothering me a bit. Let me use an example.
I have a couple of entities:
#Entity
class Book {
#Id
private Long idBook;
private String title;
#ManyToOne
#JoinColumn(name = "idAuthor")
private Author author;
}
#Entity
class Author {
#Id
private Long idAuthor;
private String name;
#OneToMany(fetch = FetchType.LAZY)
private List<Book> books;
}
For the sake of simplicity lets suppose a Book can have only one Author.
The book repository is a simple interface:
public interface BookRepository extends JpaRepository<Book, Long> {}
I also have a DTO for books:
class BookDTO {
private Long idBook;
private String title;
private idAuthor;
}
When a client want to save a book he will send a json like that:
{
"idBook":328,
"title":"The Martian Chronicles",
"idAuthor":56
}
Everytime someone needs to save a Book he will convert the DTO to entity and fetch the Author before saving:
entityBook.setId(dtoBook.getId());
entityBook.setTitle(dtoBook.getTitle());
entityBook.setAuthor(authorRepository.getById(dtoBook.getIdAuthor()));
bookRepository.save(entityBook);
For me it seems a waste of resources as only the idAuthor need to be saved. This is a simple example. The real life situations I am facing are much more complex and can be frustrating sometimes.
I found a solution using the method EntityManager::getReference.
persisting a new object without having to fetch the associations
Hibernate persist entity without fetching association object. just by id
The problem (if I didn't understand it wrong) is: getting a EntityManager reference (injected via #PersistenceContext) outside the Repositories is not a good practice and the conversion from dto to entity is made on a layer before the call to the Repositories.
Is there an alternative way to accomplish this without accessing the EntityManager on upper layers?
You can try setting the author by :
Author currentAuthor = new Author();
currentAuther.setIdAuthor(dtoBook.getIdAuthor());
entityBook.setAuthor(currentAuthor);
This will not create new Author in case there is an existing one with same Primary key. Please check the Cascade type while setting this
I have the following entities and I would like to give some additional thoughts before I make a final decision.
Post
#Entity
public class Post {
#Id
#GeneratedValue
private long id;
private String author;
private String content;
private String title;
#OneToMany
private List<Comment> comments;
// Standard getters and setters...
}
And the entity, which holds child rows
#Entity
public class Comment {
#Id
#GeneratedValue
private long id;
private String author;
private String content;
#ManyToOne
private Post post;
// Standard getters and setters...
}
A post could have a big number of comments, the most of them of course are not changed during the user session. I would like to find out the best way to save data in the following scenarios:
1. Post data is not changed;
2. Post data is changed.
The standard way is to use the following code
Post saved = postRepository.save(post);
But is this approach the most effective when you have only one comment added or changed to the post? Should the approach be different here, namely remove the one to many relationship between Post and Comment entities and treat them separately? Also, I don't like the idea that a post object, which needs to be updated in the database, may contain a large number of comments, which in turn adds additional load on network.
First to make that work you need a cascade in the post entity else it will just update/save the Post entity
#OneToMany(cascade = CascadeType.ALL ,mappedBy = "post")
private List<Comment> comments;
Also hibernate works by dirty checking your entity.
Hibernate during the merge(update) will dirty check your managed entities and generate one update query just for the entities that you changed, so if you don't touch the Post entity and just update one Comment hibernate will generate one update query.
Also this phrase make no sense.
Also, I don't like the idea that a post object, which needs to be
updated in the database, may contain a large number of comments, which
in turn adds additional load on network.
The OneToMany relationship specified in Post is just for hibernate convenience and it's actually optional.
With that you define what is called a bi-directional relationship.
There is nothing in db except for the fk on Comment referencing Post
I'm trying to use Hibernate Search on two Entities, that do not (and must not) share a relation on object-level, however they're connected by a join table that uses their IDs. (legacy)
These are more or less the two Entities:
#Entity
#Indexed
class Person {
#Id
private long id;
#Field
private String name;
....
}
#Entity
#Indexed
class Address {
#Id
private long id;
#Field
private String street;
#Field
private String zip;
....
}
They are connected by their IDs:
#Entity
class Relation {
#Id
private long id;
private long personId;
private long addressId;
}
The goal I'm trying to achieve is finding similar persons that share a similar address via Hibernate Search. This means I'm searching for attributes from both Person and Address.
I guess the easiest way is to "emulate" an #IndexedEmbedded relation which means denormalizing the data and add "street" and "zip" from Address to a Person document. I stumbled upon Hibernate Search Programmatic API, but I'm not sure if that's the right way to go (and how to go on from to there)..
Would this be the proper way of doing things or am I missing something?
If you cannot add this relationship into the model, you will be pretty much out of luck. You are right that you would have to index the Person and corresponding Address data into the same document (this is what #IndexedEmbedded does really). The normal/best way to customize the Document is via a custom (class) bridge. The problem in your case, however, is that you would need access to the current Hibernate Session within the implementation of the custom bridge.
Unless you are using some approach where this Session for example is bound to a ThreadLocal, there won't be a way for you to load the matching Address data for a given Person within the bridge implementation.