Below is my scenario.
I have two entities.
1) Client
2) Project
Relationship among them is One client provides Many Projects. I have created both the entities in Hibernate as follows,
#Entity
#Table(name="client")
public class Client extends BaseEntity<Long> {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long clientId;
......
......
}
#Entity
#Table(name="Project")
public class Project extends BaseEntity<Long>{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long projectId;
.......
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "clientId")
private Client client;
.......
}
My Service Layer looks like,
#RestController
#RequestMapping("/project")
public class ProjectController {
#Autowired
private ProjectService projectService;
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ResponseEntity<String> add(#RequestBody Project project, BindingResult result) throws JsonProcessingException {
........
projectService.saveProject(project);
....
}
}
My JSON while requesting the server is,
{
"name": "Project 1",
"description": "client Project 1",
"startDate": "2012-07-21 12:11:12",
"endDate": "2017-07-21 12:11:12",
"totalPlannedReleases": 20,
"projectKey": "MAQ",
"avatar":"Not Available",
"client": {"clientId":1}
}
My client (Parent entity) is already persisted in database. I have to reference the existing clientId to the new Project which I am inserting.
I am using Jackson as Json library
Is there any way so that Client entity can be mapped/fetched automatically while inserting Project?
What does 'automatically' mean?
If Project is the owning side of the client - project relationship (which it probably should be as per your requirement), you can call project.setClient(entityManager.getReference(Client.class, id)) (or project.setClient(session.load(Client.class, id)) if you are using Hibernate session) to avoid loading the entire state of the Client entity from the database.
Related
On execution of my spring boot application with MySQL as data source, it fails with below error message
Table 'schema.users_seq' doesn't exist
I have an #Entity class Users with an AUTO_INC field Id
#Entity
#Table(appliesTo = "users")
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="uid")
private long id;
}
Below is my User controller class
#RestController
#RequestMapping("/cm/api/user")
public class UserController {
#Autowired
UsersRepository usersRepository;
#GetMapping("/{username}")
public void getUser(#PathVariable("username") String userName) {
}
#PostMapping("/add")
public void addNewUser(#RequestBody Users users) {
usersRepository.save(users);
}
}
There are some other articles on the same issue, but it all ended with the question if the class has AUTO_INC field.
Your app tries to generate a long ID for your Users entity by retrieving a value from the users_seq. You need to change the #GeneratedValue(strategy = GenerationType.AUTO) according to your DDL structure. As you didn't include that in your post, I can only assume how you mapped it, so I can only recommend that you read this article by Hibernate maintainer Vlad Mihalcea about why you should not use the AUTO JPA GenerationType with MySQL and Hibernate.
Please be patient with me. tried my best to explain with sample easy code.
Two Entities - Shop and Product.
Relationship - A Shop can have many Product.
I return a Shop object, it keeps printing like this -
{
"shopId": 1,
"shopName": "S1",
"productList": [
{
"productId": 100,
"productName": "MOBILE",
"shop": {
"shopId": 1,
"shopName": "S1",
"productList": [
{
"productId": 100,
"productName": "MOBILE",
"shop": {
Before i start with the actual issue, i did solve the Cyclic issue partially but arrived at a new problem. I stopped it with the help of #JsonIgnore
Basically when i print my parent(Shop) json object i stopped the cyclic response by using #JsonIgnore in child (Product) class field.
#JsonIgnore
private Shop shop
So, now
API 1 =
#GetMapping("/getShopById")
public Shop getShopById(){
return shopRepo.findById(1L).get();
}
GIVES ME OUTPUT - (Which is perfect as i avoid printing Shop back);
{
"shopId": 1,
"shopName": "S1",
"productList": [
{
"productId": 100,
"productName": "MOBILE"
},
{
"productId": 101,
"productName": "EARPHONE"
}
]
}
But now anytime i want to fetch the Shop from a Product object and send the response i get an error, which is because of the #JsonIgnore i guess, which basically is completely stopping the serialization of the field from Product object.
API 2 =
#GetMapping("/getShopFromTheProductId")
public Shop getShopFromProductId() {
Shop s = productRepo.findById(100L).get().getShop();
return s;
}
GIVES ME ERROR -
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.doubt.StackOverFlow.Shop$HibernateProxy$YEW0qvzw["hibernateLazyInitializer"])
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty
So to summarize how can i ignore printing/getting the Parent back from Child until and unless i require it explicitly ?
PROBABLE SOLUTION 1 - remove the getter for Shop (private Shop getShop()) from Product entity . But this is not a solution for me as i will never be able to track back to the parent when i may need it in business logic.
MY classes -
Controller -
#RestController
public class MainController {
#Autowired
private ShopRepo shopRepo;
#Autowired
private ProductRepo productRepo;
#GetMapping("/getShopById")
public Shop getShopById(){
return shopRepo.findById(1L).get();
}
#GetMapping("/getShopFromTheProductId")
public Shop getShopFromProductId() {
Shop s = productRepo.findById(100L).get().getShop();
return s;
}
}
Shop Entity -
#Entity
#Table(name = "SHOP")
public class Shop {
#Id
#Column(name = "SHOP_ID")
private Long shopId;
#Column(name = "SHOP_NAME")
private String shopName;
#OneToMany(fetch = FetchType.LAZY,targetEntity = Product.class, mappedBy = "shop")
private List<Product> productList;
........
all the getters and setters
Product Entity -
#Entity
#Table(name = "PRODUCT")
public class Product {
#Id
#Column(name = "PRODUCT_ID")
private Long productId;
#Column(name = "PRODUCT_NAME")
private String productName;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SHOP_ID")
#JsonIgnore
private Shop shop;
........
all getters and setters
To avoid the cyclic problem Use #JsonManagedReference, #JsonBackReference as below.
Add #JsonManagedReference on Parent class, shop entity.
#JsonManagedReference
#OneToMany(fetch = FetchType.LAZY,targetEntity =
Product.class, mappedBy = "shop")
private List<Product> productList;
Add #JsonBackReference on child class as below.
#JsonBackReference
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SHOP_ID")
#JsonIgnore
private Shop shop;
So i have come to terms with this problem.
Firstly this is wrong sending a domain object directly as a response. Not at all the best practice.
Best practice is to have a RequestShopDTO Object and Similarly and ResponseShopDTO. We should have DTO with getters and setters same as the domain object, in this case Shop.
Rest API should receive RequestShopDTO object.
Use a factory class/ adapter classes to convert the RequestShopDTO to a Shop Domain object and forward it to the Business layer.
Similarly we should convert the Response Shop domain object to ResponseShopDTO object and send it as response.
we should have like BaseRequest class, extended by something like CreateRequest, UpdateRequest, GetRequest etc... where properties common to all get requests are in GetRequest which is then extended by more specific request classes such as RequestShopDTO.
Similarly we can have a abstract Adapter class like this RequestDtoToDomainBaseAdapter that gets extended by something like
ShopDtoToShopDomainAdapter.
reference - inor's answer - Design Pattern to model Request and Response Objects for Webservices
P.S. - DTO - Data Transfer Object
How to bind my new object user with an role object in my spring boot application when I receive an request post with a json/application that has all data for the new user?
What is the best approach (inform the role in the json)? If yes, how must be the json concerned the role information?
I will try to explain myself. First, I am sorry, I am not a native English speaker.
I want to create a new object user mapping the json received from a HTTP request post. The problem is that I have an internal object from my model named role. Roles are always either a common or an admin. Then I want to reference an already instantiated role object.
So, I want to know how to indicate the role from my user. You should consider that the model can not be modified because some internal team restriction. I don't know if the correct restful approach is send the role information in the json. How would you do this task?
My code
Class Controller
#RestController
public class UserController {
#RequestMapping(method=RequestMethod.POST, value="/users", produces = "application/json")
public #ResponseBody ResponseEntity<EntityUser> create(#Validated #RequestBody EntityUser user)
{
return new ResponseEntity<>(userService.add(user), HttpStatus.CREATED);
}
}
The Service class
#Service
public class UserService {
public EntityUser add(EntityUser user)
{
if (userRepository.findByName(user.getName()) == null)
return userRepository.save(user);
return null;
}
}
My plain object EntityUser (the json is mapped to it).
#Entity
public class EntityUser {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="user_sequence")
private long id;
#Column(name = "name")
#NotNull
private String name;
#Column(name = "email", unique = true)
#NotNull
private String email;
#ManyToOne(fetch=FetchType.EAGER)
// #NotNull
private EntityRole role;
...
}
and finally my EntityRole class
#Entity
public class EntityRole {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="role_sequence")
private long id;
#NotNull
private String label;
#NotNull
private String permission;
...
}
(***Edited*:**I am looking for the JSON representation of the solution provided here: Spring REST multiple #RequestBody parameters, possible?)
I have the following entity Account which has a member variable of another entity called Customer
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id=0;
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
...
}
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id=0;
private String customerId;
public Customer(){}
public Customer(String customerId){
this.customerId = customerId;
}
public long getId(){
return id;
}
public String getCustomerId(){
return customerId;
}
public void setCustomerId(String customerId){
this.customerId = customerId;
}
}
I need to post a JSON object representation of an Account. Here is the Controller method:
#Autowired
private AccountRepository accounts;
#RequestMapping(value="/accounts/account",method=RequestMethod.POST)
public #ResponseBody Account addAccount(#RequestBody Account account){
return accounts.save(account);
}
AccountRepository is an interface that extends CrudRepository
I have an existing customer with the URI http://localhost:8084/customers/customer/1
I have attempted to post the following JSON objects and received a 400 Bad Request response.
{"customer":"1"}
{"customer":"http://localhost:8084/customers/customer/1"}
{"customer":{"href":"http://localhost:8084/customers/customer/1"}}
Just to make sure that an Account can be created if correct data is received, I modified the controller method as per the following code, which created the account when I post to http://localhost:8084/accounts/account
#RequestMapping(value="/accounts/account",method=RequestMethod.POST)
public #ResponseBody Account addAccount() throws Exception{
Customer customer = customers.findOne(1);
Account account = new Account();
account.setCustomer(customer);
return accounts.save(account);
}
So my question is, how do I format a JSON object so as to Create an Entity whose member is an existing Entity?
I found out that the correct format is to provide the URI of the referenced entity, i.e in this case it should be
{"customer":"http://localhost:8084/customers/customer/1"}
However, this only worked after I added the data-rest dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>{spri}</version>
</dependency>
Without this dependency, the request fails with a JsonMappingException
I have the follow problem
I have a basic configuration of spring data rest (Nothing fancy, nothing custom).
Using spring-data-rest-webmvc 2.0.0 RELEASE and spring-data-jpa 1.5.0 RELEASE
Class A
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id
private String name;
#ManyToMany
private List<B> b;
// getters setters
}
Class B
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id
private String nameb;
#ManyToMany(mappedBy = "b")
private List<A> a;
// getters setters
}
Repository A
#Repository
#RestResource(rel = "a", path = "a")
public interface ARepository extends PagingAndSortingRepository<A, Integer> {
}
Repository B
#Repository
#RestResource(rel = "b", path = "b")
public interface BRepository extends PagingAndSortingRepository<B, Integer> {
}
When I save an entity works fine, but I don't know how to save a relationship
e.g. save an "A" inside a "B" using http
This is the last thing I try from this answer https://stackoverflow.com/a/13031580/651948
POST http://localhost:8080/api/a
{
"name": "Name of A",
"b": {
"rel": "b",
"href": "http://localhost:8080/api/b/1"
}
}
I get an 201 http code but doesn't save the entity.
Did someone tried this already?
Try just using the URL.
POST http://localhost:8080/api/a
Content-Type: application/json
{
"name" : "Name of A",
"b": "http://localhost:8080/api/b/1"
}
or, in your case, it's probably
"b" : ["http://localhost:8080/api/b/1"]
because A.b is a list and hence you submit an array. Did not test this, though.
This should be the valid way since Spring 2.0 (see Spring Data Rest 2.0.0.RELEASE Breaks Code Working Previously With RC1) and it works for me well.