Problem creating endpoint with springboot - java

My task is to create endpoint, the logic is:
User provides input --> nip (one of the variables in Contractor.class)
on the basis of that nip, I must return JSON, which will contain information about the product that is assigned to the contractor with the provided nip.
Example JSON should look like: {"name": "product_name", "quantity": "0", "address": "storage_address"}
I spent lots of time on this problem, but still don't know what logic to implement.
It's over my newbie head;
Product.class:
public class Product extends AbstractEntity {
#Id
#GeneratedValue
private Long id;
private String name;
private long quantity;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "product", fetch = FetchType.EAGER)
private List<Assignment> assignments;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "product")
private List<Place> places;
}
Contractor.class:
public class Contractor extends AbstractEntity {
#Id
#GeneratedValue
private Long id;
private String contractorName;
private int nip;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "contractor")
private List<Assignment> assignments;
}
Assignment.class:
public class Assignment extends AbstractEntity {
#Id
#GeneratedValue
private Long id;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "id_product", referencedColumnName = "id", nullable = false)
private Product product;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "id_contractor", referencedColumnName = "id", nullable = false)
private Contractor contractor;
}
Storage.class:
public class Storage extends AbstractEntity {
#Id
#GeneratedValue
private Long id;
private String address;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "storage")
private List<Place> places;
}
Place.class:
public class Place extends AbstractEntity {
#Id
#GeneratedValue
private Long id;
private Long shelfNumber;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "id_product", referencedColumnName = "id")
private Product product;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "id_storage", referencedColumnName = "id", nullable = false)
private Storage storage;
}
image of ERD Diagram

I do not know what logic do you need precisely. Also I am not sure if that code will fit to rest of your app. But maybe it will help.
The interface which will be returned by repository and by the rest controller:
public interface GetProductResponse {
public String getName();
public int getQuantity();
public String getAddress();
}
The repository where you can write your query:
public interface ProductRepository extends CrudRepository<Product, Long> {
#Query(nativeQuery = true,
value = (
"SELECT product.name AS name, product.quantity AS quantity, storage.address " +
//be sure to name the result columns the variables in GetProductResponse (without the 'get')
"FROM contractor INNER JOIN assignment ON contractor.id = assignment.id_contractor " +
" INNER JOIN product ON product.id = assignment.id " +
" INNER JOIN place ON product.id = place.id_product " +
" INNER JOIN storage ON storage.id = place.id_storage " +
"WHERE contractor.nip = :nip_ "
)
public List<GetProductResponse> getProducts(#Param("nip_")String nip)
}
The rest controller:
#RestController
public class Controller {
#RequestMapping(value = "/getProductsByNip", method = { RequestMethod.POST})
public List<GetProductResponsee> getProductsByNip(#RequestBody String nip) {
return productRepository.getProducts(nip);
}
}
The output will look like:
[
{"name": "product_name1", "quantity": "0", "address": "storage_address1"},
{"name": "product_name2", "quantity": "2", "address": "storage_address2"}
]

Related

Sping boot many-to-many GET request

I have many-to-many relationship in my Spring boot app. But when i try to do get response i always get an empty array;
Here are my classes(I have pasted code without constructor, getters and setters but i have them in my code):
#Entity
#Table(name="orders")
public class Order {
private #Id
#GeneratedValue
Long id;
#OneToOne(cascade = CascadeType.ALL)
private Customer customer;
#OneToMany(mappedBy = "product",fetch = FetchType.LAZY,cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH},orphanRemoval = true)
private Set<ProductOrderDetails> productOrderDetails;
#DateTimeFormat
private Date shippmentDate;
private double totalOrderPrice;
private OrderStatus status;
private String note1;
private String note2;
#Entity
#Table
public class Product {
private #Id
#GeneratedValue
Long id;
private String name;
private String model;
private String color;
private String material;
private double price;
#Transient
private int productQuantity;
#OneToMany(mappedBy = "order",fetch = FetchType.LAZY)
private List<ProductOrderDetails> productOrderDetailsSet;
#Entity
#IdClass(ProductOrderDetails.class)
public class ProductOrderDetails implements Serializable {
#Id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="order_id")
Order order;
#Id
#ManyToOne(cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH})
#JoinColumn(name="product_id")
Product product;
private int quantity;
Here is my OrderController code:
#GetMapping("/{id}")
public Order One(#PathVariable Long id) {
Order order=repository.findById(id).orElseThrow(()->new ObjectNotFoundException(id));
return order;
}
And this is the response that I get:
{
"id": 2,
"customer": {
"id": 1,
"name": "Company",
"address": "Main Street 1",
"city": "Bern",
"state": "Switzerland",
"zip": 58529,
"contactPersonName": "John Smith",
"contactPersonEmail": "test#gmail.com"
},
"productOrderDetails": [],
"shippmentDate": "2020-12-09T23:00:00.000+00:00",
"totalOrderPrice": 3434.0,
"status": "WAITING",
"note1": "note 1",
"note2": "note 2"
}
How do i get an array of productOrderDetails (array of products that are ordered)?
Id prefer if i could use JPA
You have a couple of possibilities to do this:
Using FetchType.EAGER strategy
#Entity
#Table(name="orders")
public class Order {
private #Id
#GeneratedValue
Long id;
#OneToOne(cascade = CascadeType.ALL)
private Customer customer;
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER,cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH},orphanRemoval = true)
private Set<ProductOrderDetails> productOrderDetails;
#DateTimeFormat
private Date shippmentDate;
private double totalOrderPrice;
private OrderStatus status;
private String note1;
private String note2;
}
Using Join fetching in your custom Repository method:
#Query(value = "SELECT o FROM Order o JOIN FETCH o.productOrderDetails")
List<Order> findAllOrders();
So to anyone who had the same problem as i did,
i had a typo error, in the Order class i should've used:
#OneToMany(mappedBy = "order",fetch = FetchType.LAZY,cascade = {CascadeType.PERSIST,CascadeType.MERGE,CascadeType.DETACH},orphanRemoval = true)
private Set<ProductOrderDetails> productOrderDetails;
instead of mappedBy="product".
And if you dont want to have a recursive call when you do a GET request you should add in ProductOrderDetails class #JsonIgnore:
#Id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="order_id")
#JsonIgnore
Order order;
The rest of the code stays unchanged.

Error with #ManyToOne - JPA/Hibernate Detached Entity Passed to Persist

Good Evening,
I am relatively new to using Hibernate, and I am running into the following error:
"message": "org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist:
com.company.project.data.relational.models.ListsItems; nested exception is org.hibernate.PersistentObjectException:
detached entity passed to persist: com.company.project.data.relational.models.ListsItems",
I have a JSON object being sent from the front-end that has a nested object. I am trying to get the the nested items in a separate table in MySQL, with a relationship using the original objects ID.
Here's an example of the JSON:
{
"name":"Test",
"type":"App Id List",
"listItems":
[
{
"id":1,
"name":"Test",
"value":" 1"
},
{
"id":2,
"name":"NEW TEST",
"value":" 2"
}
]
}
Here is my Lists model:
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "lists")
public class Lists implements Serializable, OperationalEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(columnDefinition = "char", nullable = false)
private String guid;
private String name;
private String type;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "listItems", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
private Date created;
private Date updated;
}
And here is my ListsItems model:
#Getter
#Setter
#Entity
#Table(name = "lists_items")
#NoArgsConstructor
public class ListsItems implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String value;
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists listItems;
}
Here is the save function:
#PostMapping(value = "/add")
#PreAuthorize("hasRole('ADMIN')")
public #ResponseBody WebResponse<W> create(#RequestBody W webModel) {
D dbModel = asDbModel(webModel);
dbModel.setGuid(UUID.randomUUID().toString());
return WebResponse.success(createWebModelFromDbModel(getDatabaseEntityRepository().save(dbModel)));
}
Any ideas on what might be causing this error? I've searched a bit but nothing I've tried from any other solutions have worked out.
Thanks in advance!
- Travis W.
The answer was to make the following changes to ListItems:
#JsonIgnore // this import will be from jackson
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists list;
And the following to Lists:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "list", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
I also needed to iterate over the results:
#Override
protected Lists asDbModel(WebLists webModel) {
Lists dbModel = new Lists();
dbModel.setId(webModel.getId());
dbModel.setName(webModel.getName());
dbModel.setType(webModel.getType());
dbModel.setListItems(webModel.getListItems());
for(ListsItems item : webModel.getListItems()) {
item.setList(dbModel);
}
return dbModel;
}

Hibernate unable to map many students to a class

I hae 2 simple entities: Student and Class. I want to POST a student, where I specify the class it belongs to, but I've got stuck in hibernate mapping.
ClassModel.class
#Entity
#Table(name = "class" )
public class ClassModel implements Serializable {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotEmpty
#Size(max = 20)
#Column(name = "name")
private String name;
#Column(name = "tables")
private int tables;
#Column(name = "chairs")
private int chairs;
#Column(name = "teacher")
private String teacher;
(getters + setters)
StudentModel
#Entity
#Table(name = "student")
public class StudentModel implements Serializable {
#Id
#Column(name = "student_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int student_id;
#NotEmpty
#Column(name = "name")
#Size(max = 50)
private String name;
#Column(name = "age")
private int age;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id")
private ClassModel classModel;
(getters + setters)
}
StudentController.java
...
#Autowired
private StudentService studentService;
#Autowired
private ClassService classService;
#PostMapping(value = "/save")
public StudentModel save(#RequestBody StudentModel studentModel){
ClassModel classModel = classService.findById(studentModel.getClassId()).get();
studentModel.setClassModel(classModel);
return studentService.save(studentModel);
}
...
But when I make a request from Postman with the following body:
{
"name": "some name",
"age": 12,
"class_id": 1
}
I get the following error from hibernate:
Column 'class_id' cannot be null
Where is the mistake in my hibernate mapping?
It's how I have made working join in hibernate. Have a look:
TrainingEntity.java
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "animal_id", nullable = false, insertable = false, updatable = false)
private AnimalEntity animalEntity;
#Column(name = "animal_id")
private Integer animalId;
AnimalEntity.java
#Id
private Integer id;
#OneToMany(mappedBy = "animalEntity", fetch = FetchType.LAZY)
private List<TrainingEntity> trainingEntityList = new ArrayList<>();
So here is the join between AnimalEntity and TrainingEntity.
AnimalEntity have a list of TrainingEntities.
The mistake is in this line:
"class_id": 1
You're using column name instead of field name. You would have to replace class_id with classModel, where classModel would be an object. Other solution would be to find ClassModel by id from json and set it as parent to StudentModel.

How to JSON returning child object details using JPA and Springboot

I'm using Springboot + JPA to do a restful backend, I have two entities and I need to get a JSON response with child object details like this:
{
"mensagens": [
{
"id": "2",
"origem_id": "1",
"destino_id": "2",
"assunto": "hello",
"corpo": "hi, how are you?",
"origem": {
"id": "1",
"nome_completo": "Leonard",
"apelido": "leo"
},
"destino": {
"id": "2",
"nome_completo": "Mark",
"apelido": "mark"
}
}
]
}
Can anyone help me?
======================================================================================================================================================
My classes are below:
This is my Entity Contact:
#Entity
#Table(schema="schema")
public class Contact {
#Id
#GeneratedValue
#Column(name = "contact_id")
private long id;
#Column(name = "nome_completo", nullable = true)
#JsonProperty(value = "nome_completo")
private String nomeCompleto;
#Column(name = "apelido", nullable = true)
private String apelido;
#OneToMany(mappedBy = "origemId", cascade = CascadeType.ALL)
private List<Message> msgOrigem;
#OneToMany(mappedBy = "destinoId", cascade = CascadeType.ALL)
private List<Message> msgDestino;
// getters and setters
This is my Entity Message
#Entity
#Table(name = "message", schema = "schema")
public class Message {
#Id
#GeneratedValue
#Column(name = "msg_id")
private long id;
#Column(name = "origem_id")
private long origemId;
#Column(name = "destino_id")
private long destinoId;
#Column(name = "assunto")
private String assunto;
#Column(name = "corpo")
private String corpo;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contactId")
private Contact contact;
// getters and setters
My repository:
#RestResource(exported = false)
public interface MessageRepository extends JpaRepository<Message, Long> {}
My Class Messages:
public class Messages {
private List<Message> mensagens;
public List<Message> getMensagens() {
return mensagens;
}
public void setMensagens(List<Message> mensagens) {
this.mensagens = mensagens;
}
}
my rest controller:
#RestController
#RequestMapping(path = "/sdm/mensageiro/mensagens")
public class ListMessagesController {
#Autowired
private MessageRepository repository;
#GetMapping
public Messages findAllMessages() {
Messages c = new Messages();
c.setMensagens(repository.findAll());
return c;
}
}
This is the class that run the springboot applcation:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I use the postman to retrive from Mysql data and now my result JSON is like below:
{
"mensagens": [
{
"id": 2,
"origemId": 1,
"destinoId": 2,
"assunto": "Hello",
"corpo": "hi, how are you?"
}
]
}
Should update fetch type to EAGER in order to when getting the list persiste, here is things that you need to update:
#OneToMany(mappedBy = "origemId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Message> msgOrigem;
#OneToMany(mappedBy = "destinoId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Message> msgDestino;
And ignore Contact in order not to display contact again because you will have a recursive error.
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "contactId")
#JsonIgnore
private Contact contact;

Spring MVC/Jackson - Nested Entity Deserialization Weirdness

I'm having a weird problem with Jackson serialization - I have a Role entity have a nested Permission entity which, in turn, contains a nested Metadata entity. When these entities are retrieved from a Spring MVC #RestController as a list, Jackson serializes the Permission collection into a JSON array. The problem is that sometimes the element placed in this array is just the id of the Permission rather than a serialized representation of the object.
Role.class:
#Entity
#Table(name = "t_db_roles")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Role.class)
public class Role implements GrantedAuthority {
private final static Logger log = LoggerFactory.getLogger(Permission.class);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "auto_id")
private int id;
#Column(name = "role", length = 50)
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "t_db_role_permissions",
joinColumns = {#JoinColumn(name = "roleid", referencedColumnName = "auto_id")},
inverseJoinColumns = {#JoinColumn(name = "permid", referencedColumnName = "auto_id")}
)
private Set<Permission> permissions;
// getters and setters omitted
}
Permission.class:
#Entity
#Table(name = "t_db_permissions")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Permission.class)
public class Permission implements GrantedAuthority {
private final static Logger log = LoggerFactory.getLogger(Permission.class);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "auto_id")
private int id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy = "permission")
private Metadata metadata;
}
Metadata.class
#Entity
#Table(name = "t_report_data")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Metadata.class)
public class Metadata {
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "file_name")
private String fileName;
#Column(name = "human_name")
private String humanName;
#Column(name = "links_to")
#JsonIgnore
private Integer linksTo;
#Column(name = "is_subreport")
#JsonIgnore
private Boolean isSubreport;
#OneToOne(cascade = javax.persistence.CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "permid")
private Permission permission;
}
The controller:
#RestController
public class RoleRestController {
private final static Logger log = LoggerFactory.getLogger(PermissionRestController.class);
private RoleService roleService;
private MetadataService metadataService;
#Autowired
public void setRoleService(RoleService service) {
this.roleService = service;
}
#Autowired
public void setMetadataService(ReportMetadataService service) { this.metadataService = service; }
#RequestMapping(value = "/admin/roles/", method = RequestMethod.GET)
public List<Role> getRoles() {
return roleService.getRoles();
}
}
I'm fairly sure that the problem is in serialization - echoing the List<Role> to the console works as expected, but here is the JSON returned (note the first element of the permissions array is an integer rather than a JSON object):
{
"id": 10,
"name": "ROLE_TESTER",
"permissions": [
14,
{
"id": 7,
"name": "ViewDailySettlementSummaryGL",
"metadata": {
"id": 41,
"fileName": "acct_summary_gl.rptdesign",
"humanName": "Daily Settlement Summary GL",
"permission": 7
},
"authority": "ViewDailySettlementSummaryGL"
},
{
"id": 6,
"name": "ViewDailySettlementSummary",
"metadata": {
"id": 24,
"fileName": "acct_summary_os.rptdesign",
"humanName": "Daily Settlement Summary",
"permission": 6
},
"authority": "ViewDailySettlementSummary"
}
],
"authority": "ROLE_TESTER"
}
I can work around this by handling Role serialization manually, but since the SpringMVC/Jackson serialization works for other classes in the project it seems like there must be a problem in these classes that i'm overlooking. Any ideas?

Categories