I'm trying to do my first JEE application with rest api and hibernate. I dont know how to do it in proper way. I mean that I have entity User
#Entity
#XmlRootElement
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String username;
private String password;
private String email;
private LocalDateTime lastDateOfLogin;
#OneToMany(fetch=FetchType.LAZY)
private List<Post> posts = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<Comment> comments = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<Like> likes = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<User> followers = new ArrayList<>();
#OneToMany(fetch=FetchType.LAZY)
private List<User> following = new ArrayList<>();
and I have UserResource method to get one user
#GET
#Path("{id}")
public User getUser(#PathParam("id") long id)
{
return userService.getOne(id);
}
Now my problem is that when I'm trying to get one user I'm getting exception org.hibernate.LazyInitializationException - of course I know why but question is how to do this get it in proper way. In this #GET I don't need this OneToMany collections because when I want E.g user posts I will call user/1/posts url and I will receive all user posts.
How to develop this kind of applications? Should I delete relations from user entity and just search in database posts of user when I need them? Or maybe there is another solution for it?
If that particular endpoint is only interested in the basic details about the user and none of the associations your database model has to other objects in your system, you need to somehow prevent the serialization process from looking at those attributes.
If it is a field that you want to always be ignored, you could annotate it with #XmlTransient, but this is a decision that is determined at build time and cannot be modified at runtime. For situations where you need to dynamically influence the serialization step, you can either look at these two articles:
How to Conditionally serialize with JAXB or Jackson
Jacksons JsonView or MOXy's external mapping-files
Another alternative would be to modify your service to return a class instance for that specific view that only contains the attributes you want to be marshalled into the output XML. This can easily be accomplished with a JPA select new query like:
SELECT new com.company.app.BasicUser(u.userName, other attributes) FROM User u
WHERE u.id = :id
Now your XML marshalling would be based on BasicUser and not your domain entity User, where that BasicUser doesn't have any of the associations or attributes that you don't wish to have serialized for that specific view.
Related
I have entities that represent my database - User, Recipe and Tag.
For data manipulation I use DTO. So UserDTO, RecipeDTO, TagDTO. When I define a relationship between entities, I use its basic User, Recipe, Tag form, but when I define these relationships in a DTO class, I use its DTO form.
For example:
DTO Class looks like this
public class UserDTO{
private String name;
private String email
private List<RecipeDTO>
}
public class RecipeDTO{
private String title;
private String description;
private UserDTO user;
}
I know how to map a DTO to an entity so that I can perform operations (CRUD) on the data in the database.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle);
recipe.setDescription(recipeDTO.getDescription);
}
But the RecipeDTO also has a UserDTO in it, which I also need to map to an entity. How do I do this?
So I am trying to achieve a mapping inside the mapping .... (??)
I can think of the following solution.
Create method that converts UserDTO to User:
private User convertUser(UserDTO userDTO){
User user = new User();
user.setName(userDTO.getName());
user.setEmail(userDTO.getEmail());
}
And then use it while mapping RecipeDTO to Recipe.
private Recipe convertToEntity(RecipeDTO recipeDTO){
Recipe recipe = new Recipe();
recipe.setTitle(recipeDTO.getTitle());
recipe.setDescription(recipeDTO.getDescription());
//Convert UserDTO
recipe.setUser(convertUser(recipeDTO.getUser()));
}
I'm not sure if this is the right solution, as there will be more and more mappings as the code gets bigger.
The approach you described is not wrong and will work, but doing it that way will indeed involve a lot of hard work.
The way this is usually done in the industry is by letting a library do that work for you.
The two most popular mapping libraries for java are:
https://mapstruct.org/ (which uses annotation processing at compile time and auto-generates basically the same mapping code as in your example)
and
http://modelmapper.org/ (which uses black magic and reflection)
They are both easy to setup/learn and either will do the job (including mapping nested objects as in your example), so take a look at the “getting started“ section and pick the one you find more intuitive to use.
My personal recommendation would be to pick Mapstruct, as it has way fewer gotchas, generates clean human-readable code and avoids using reflection.
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 a POJO called User which is also being used for inserting documents in MongoDb.
#Data
#Document(collection = Constants.COLLECTION_USERS)
public class User {
public ObjectId _id;
public String userID;
public String email;
public String name;
public String sex;
public String dob;
public String photo;
//have more variables
}
I have a simple application where a user registers by giving in a subset of data listed in the User class. The signature of the register method in my controller is as follows.
public GenericResponse registerUser(#RequestBody User userRegistrationRequest)
It can be noticed that I am using the same POJO for the registration request. Until now everything is fine. I can persist the data user object just fine.
This registration API is just used to persist a small set of a user's data. There would be other information as well in the MongoDb document, which would be accessed/persisted from some other APIs.
Suppose a user has registered with the basic information and also has persisted other information via APIs other than the registration one.
How would I make an API which can just get me some selective data from the User document again using the same User Pojo? If I call the repository to give data for a specific userID, it will give me the whole document mapped to the User class. I don't want my API to give all the information stored in the document.
One approach is to make another POJO with the details I want, and map the information selectively using a Converter. But, I want to avoid this approach, as I want to use the same class.
Second approach: Modify the Mongo query to return data selectively as given in the docs. But here I would have to specify all the fields I want in the result set. This would again be a length query.
Is there a better way to filter out data from the object?
How would I make an API which can just get me some selective data from the User document again using the same User Pojo?
How would I go off-road with a car I would like to take me girl to the restaurant at the evening? I would not - if I would have the same car for everything I would look stupid next to the restaurant, coming out in a suite or I would stuck in a swamp.
The biggest Java advantage is object creation time - you should not be afraid of it. Just create another model for registration, another as DTO for saving data, another for front-end presentation etc
Never mix responsibility of objects. You will finish with something like
#Entity
class ThePileOfShit {
#Id
private Long id;
#my.audit.framework.Id
private String anotherId;
#JsonIgnore
// just a front-end flag ignore
private boolean flag;
#Column
// not a field but getter because of any-weird-issue-you-want-to-put-here
public String getWeirdStuff() { ... }
// Useless converters
public ModelA getAsModelA() { ... }
public ModelB getAsModelB() { ... }
// etc
// etc
}
Four frameworks, five technologies - nobody knows what's going on.
If you are afraid of converting stuff use ModelMapper or another tool but keep your POJOs as simple as possible
You can use Gson's #Expose annotation only on the fields you want to return in the API.
To serialize the data, use:
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String json = gson.toJson(userData);
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);
A class Dog is using a private field owner to keep the owner username kept in class User (which implements UserDetails):
#Document
public class Dog {
#Id
private ObjectId id;
private String owner;
}
#Document
public class User implements UserDetails {
#Id
private ObjectId id;
private String username;
}
These documents are persisted using spring-data-mongodb and exposed using spring-data-rest. User is authenticated using Spring Security, so it is available as #AuthenticationPrincipal.
It is requested that REST can only access to the dog the user owns. Is it possible to tweak spring-data-rest to return only a subset of the documents from collection dogs, the ones with 'owner' field is the same as the username in the User instance returned by the authentication principal ?
What I did in such a scenario was implementing custom RepositoryInvoker.
The invoker was a proxy for the one created by Spring Data Rest. The idea was to intercept invokeFindAll (or other method that should filter by the ownership) and delegate to the the query method that implements the filtering.
Note the I was using Spring Data JPA and Specfications to implement the filtering predicate. I believe similar approach should by feasible with Mongo.