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
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.
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;
}
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.
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;
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?