How get 2 SELECT in 1 query with SPRING DATA - java

I have 3 entities :
#Entity
#Table(name = "copy")
public class Copy {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "format")
private String format;
#Column(name = "status")
private String status;
#ManyToOne
#JoinColumn(name = "book_id")
private Book book;
#ManyToOne
#JoinColumn(name = "library_id")
private Library library;
#Entity
#Table(name = "book")
public class Book implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "title")
private String title;
#Column(name = "pub_date")
private Date pubDate;
#Column(name = "page")
private int page;
#Column(name = "synopsis")
private String synopsis;
//TODO Image à gérer
#Column(name = "cover")
private String cover;
#ManyToOne
#JoinColumn(name = "categorie_id")
private Categorie categorie;
#ManyToOne
#JoinColumn(name = "author_id")
private Author author;
#OneToMany(mappedBy = "book")
List<Copy> copyList = new ArrayList<>();
#Entity
#Table(name = "library")
public class Library implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column
private Long id;
#Column(name = "nom")
private String nom;
#Column(name = "adress")
private String adress;
#Column(name = "phone_num")
private String phoneNum;
#Column(name = "email")
private String email;
#OneToMany(mappedBy = "library")
private List<Copy> copyList = new ArrayList<>();
I would like to recover the number of copies of a book according to its format and its libraries. However I cannot figure out how to retrieve a list of copies and the total number depending on the format and its library. How can I do. I wrote this request but I can't get what I want.
My request :
#Query("SELECT DISTINCT c, COUNT(c.format) FROM Copy c WHERE c.book.id = :id")
List<Copy> getCopyById(#Param("id") Long id);

first you need to create class to handel query result (copy,total)
public class CopyWithTotal{
Copy c;
int total;
CopyWithTotal(Copy c, int total){
this.c = c;
this.total = total;
}
}
then you should constratc this class in the query
#Query("SELECT new packgeTo.CopyWithTotal(DISTINCT c, COUNT(c.format)) FROM Copy c WHERE c.book.id = :id group by c")
List<CopyWithTotal> getCopyById(#Param("id") Long id);
whenever you use aggregation function like count all selected column shoud apper in the group by

Related

Reading nested collection QueryDSL

I am using queryDSL to fetch the inner collection of data and failed to do so. My entities are
#Entity
#Table(name = "countries")
#Setter
#Getter
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long countryId;
#Column(name = "name")
private String name;
#Column(name = "data")
private String data;
#OneToOne(mappedBy = "country")
private State stateJoin;
}
#Entity
#Table(name = "states")
#Setter
#Getter
public class State {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long stateId;
#Column(name = "name")
private String name;
#Column(name = "count")
private String count;
#Column(name = "co_id")
private Long countryId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "co_id", referencedColumnName = "id", updatable = false, insertable = false)
private Country country;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "state_id", referencedColumnName = "id")
private Set<Town> towns;
}
#Entity
#Table(name = "towns")
#Setter
#Getter
public class Town {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "town_id")
private Long townId;
#Column(name = "name")
private String name;
#Column(name = "people_count")
private String peopleCount;
#Column(name = "st_id")
private Long stateId;
}
and in querydsl i am trying below query and always failing to retrieve inner collection towns
List<Expression> childGroup = new ArrayList<>();
query.from(countries);
childGroup.addAll(Arrays.asList(countries.name, countries.data, countries.stateJoin));
query.join(countries.stateJoin, State);
childGroup.add(Projections.bean(State.class, name, count, town)
.as((Path) countries.stateJoin));
OR
childGroup.add(Projections.bean(State.class, name, count, list(town)).as((Path) countries.stateJoin));
query.select(fields);
query.where();
final Map<? extends Number, ? extends Object> t =
(Map<? extends Number, ? extends Object>)
query
.limit(pageRequest.getPageSize())
.offset(pageRequest.getOffset())
.distinct()
.transform(
GroupBy.groupBy(groupByPath)
.as(Projections.bean(Countris.class, childGroup.toArray(new Expression[0]))));
while executing the above line exactly i am getting always SQLSyntax error as i see the underlying SQL is with
. as towns
Can some one help me how to read the nested collection formed by JPA join?

JPA : How to handle mapping with a table that has relationship with two other tables?

I have three tables, table A (product), table B (invoice) and table C (invoices_info) which contains two columns referencing invoice_id and product_id. How can i insert a new entry (a new invoice) while inserting the products to the appropriate table and inserting the invoice info to its table also ?
Here are the entity classes :
Product
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "family_id")
private long familyId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_category")
private String productCategory;
#Column(name = "product_quantity")
private int productQuantity;
//getters and setters
}
Invoice
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private Long id;
#Column(name = "provider_id")
private Long providerId;
#Column(name = "total")
private int invoiceTotal;
#Column(name = "date")
private Date invoiceDate;
//getters and setters
}
InvoiceInfo
#Entity
#Table(name = "invoice_info")
public class InvoiceInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private long id;
#Column(name = "product_id")
private long productId;
#Column(name = "invoice_id")
private long invoiceId;
//getters and setters
}
InvoiceInfo should be join table, Define relationship on entities Product & Invoice using annotations #OneToMany, #ManyToOne based on your requirement.
You have to create relationships between your entities by using a set of annotations like: #ManyToOne, #OneToMany, #ManyToMany or #OneToOne... and other annotations if needed.
In your case I am not really sure you need an InvoiceInfo table, as the Invoice table can (or should) already contains the list of products.
I would suggest you the following relationships:
Product
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "family_id")
private long familyId;
#Column(name = "product_name")
private String productName;
#Column(name = "product_category")
private String productCategory;
#Column(name = "product_quantity")
private int productQuantity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "invoice_id", referencedColumnName = "id")
private Invoice invoice;
//getters and setters
}
Invoice
#Entity
#Table(name = "invoice")
public class Invoice {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private Long id;
#Column(name = "provider_id")
private Long providerId;
#Column(name = "total")
private int invoiceTotal;
#Column(name = "date")
private Date invoiceDate;
#OneToMany(mappedBy = "product")
private List<Product> products;
//getters and setters
}
As your table InvoiceInfo no longer exists, you just have to insert you data in two table like this:
Invoice invoice = invoiceRepository.save(invoice);
Product product = new Product();
// Set the other properties
product.setInvoice(invoice);
productRepository.save(product);

javax.persistence.EntityNotFoundException: Unable to find kg.library.spring.library_spring.entity.Author with id 10000001

I'm new to Spring and I'm probably making the dumbest mistake, but I can't solve this problem for more than 2 hours. According to the video tutorial, I did Pagination, I did it exactly like his, but he did not have relationships between entities. I think the error is in a one-to-one relationship between Author and Book entity. Can you please help?
I wanted to add pagination because I have more than a million records in my table, after adding pagination I got this error.
Book Entity:
#Entity
#Table(name = "books")
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "publishing_year")
private Integer publishingYear;
#Column(name = "sell_cost")
private BigDecimal sellCost;
#Column(name = "rent_cost")
private BigDecimal rentCost;
#Column(name = "amount")
private Integer amount;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "author_id")
private Author author;
//getter and setters
Author Entity:
#Entity
#Table(name = "author")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "fullname")
private String fullname;
#Column(name = "birthday")
private Date birthday;
#OneToOne(mappedBy = "author")
private Book book;
//getters and setters
BookServiceImpl class:
#Service
public class BookServiceImpl implements BookService
{
#Autowired
private BookRepository bookRepository;
#Override
public Page<Book> findPaginated(int pageN, int pageSize,String sortField,String sortDirection) {
Sort sort = sortDirection.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortField).ascending() :
Sort.by(sortField).descending();
Pageable pageable = PageRequest.of(pageN-1,pageSize,sort);
return bookRepository.findAll(pageable);
}
}
LibrarianController class:
#GetMapping("/books/{pageN}")
public String getAllBooks(#PathVariable (value = "pageN") int pageN,Model model,
#RequestParam("sortField") String sortField,
#RequestParam("sortDir") String sortDir){
int pageSize = 25;
Page<Book> page = bookService.findPaginated(pageN,pageSize,sortField,sortDir);
List<Book> books = page.getContent();
model.addAttribute("currentPage",pageN);
model.addAttribute("totalPages",page.getTotalPages());
model.addAttribute("totalItems", page.getTotalElements());
model.addAttribute("books",books);
model.addAttribute("sortField",sortField);
model.addAttribute("sortDir",sortDir);
model.addAttribute("reverseSortDir",sortDir.equals("asc") ? "desc" : "asc");
return "librarian/show-all-books";
}
It seems that you have a Book record that refers to an Author with id 10000001 that does not exit in Author table.
Try these changes. I hope that each book has only one author.
Book.java:
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "author_id")
private Author author;
Author.java:
#OneToMany(mappedBy = "author")
private List<Book> books=new ArrayList<>();

Modifying Data in a Many-to-One Relationship java spring boot

I'm having trouble updating my data in a Many-to-One relationship
Product Model:
#Entity
#Getter
#Setter
#Table(name = "Products")
public class ProductModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long product_id;
#Column(name = "ProductName")
#Lob
private String productName;
#Column(name = "ShortName")
private String shortName;
#Column(name = "Price")
private int price;
#Column(name = "Slider")
private Boolean slider;
#Column(name = "SpecialOffer")
private Boolean specialOffer;
#Column(name = "NewPrice")
private int newPrice;
#Column(name = "ShortDesc")
private String shortDesc;
#Column(name = "FullDescription")
#Lob
private String fullDescription;
#Column(name = "Image")
#Lob
private String image;
#ManyToOne
#JoinColumn(name="category_id")
private CategoryModel categoryModel;
}
Category Model:
#Table(name = "Category")
public class CategoryModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long category_id;
#Column(name = "CategoryName")
private String cateGoryName;
#Column(name = "Image")
#Lob
private String image;
}
Service:
Add Product:
public ResponseEntity<ProductModel> addProduct(#PathVariable Long id,
#RequestBody ProductModel productDetails){
ProductModel productModel = categoryRespository.findById(id).map(category -> {
productDetails.setCategoryModel(category);
return productRepository.save(productDetails);
}).orElseThrow(() -> new RuntimeException("Fail "));
return new ResponseEntity<>(productModel,HttpStatus.CREATED);}
Update Product:
public ResponseEntity<ProductModel> updateProduct(#PathVariable long id, long categoryId, #RequestBody ProductModel productDetails) {
ProductModel updateProduct = productRepository.findById(id).orElseThrow(() -> new RuntimeException("Fail " + id));
updateProduct.setFullDescription(productDetails.getFullDescription());
updateProduct.setImage(productDetails.getImage());
updateProduct.setNewPrice(productDetails.getNewPrice());
updateProduct.setPrice(productDetails.getPrice());
updateProduct.setShortDesc(productDetails.getShortDesc());
updateProduct.setShortName(productDetails.getShortName());
updateProduct.setProductName(productDetails.getProductName());
updateProduct.setSlider(productDetails.getSlider());
updateProduct.setSpecialOffer(productDetails.getSpecialOffer());
productRepository.save(updateProduct);
return ResponseEntity.ok(updateProduct);
}
Add product working fine, adds a product according to the category. Update product also works fine without modifying the categories.The problem is that I cannot correctly assign the modified category to the model.

Spring Boot Data JPA detached entity passed to persist on #OneToMany and #OneToOne

Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).

Categories