JPA repository OneToMany mapping with #Query condition on child column - java

JobName id is primary and is mapped with JobStatus jobid
public class JobName implements Serializable {
private static final long serialVersionUID = 1;
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "NAME")
private String name;
#Column(name = "SCHEDULEDTIME")
private String scheduledTime;
#Column(name = "THRESHOLD")
private Integer threshold;
#Column(name = "TYPE")
private String type;
#OneToMany(mappedBy = "jobName", fetch = FetchType.EAGER)
private List<JobStatus> jobStatus;
}
public class JobStatus implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Integer statusId;
#Column(name = "STARTDATE")
private Timestamp startDate;
#Column(name = "ENDDATE")
private Timestamp endDate;
#Column(name = "STATUS")
private String status;
#Column(name = "REMARKS")
private String remarks;
#ManyToOne
#JoinColumn(name = "JOBID")
#JsonIgnore
private JobName jobName;
}
#Repository
public interface JobRepository extends JpaRepository<JobName, Integer> {
#Query("select j from JobName j join j.jobStatus s where s.startDate> :fromDate")
public List<JobName> findJobNameBylastTime(#Param("fromDate") Timestamp fromDate);
}
I want to fetch all records from jobstatus table where startDate is greater than the date sent.
I want response in below json format, I am getting jobName list size correct based on date passed but all jobStatus records present in DB are fetched.
{
"id":"",
"jobName": "",
"scheduledTime": "",
"threshold": "",
"type": "",
"jobStatus": [
{
"statusId":"",
"startDate": "",
"endDate": "",
"status": "",
"remarks": ""
},
{
"statusId":"",
"startDate": "",
"endDate": "",
"status": "",
"remarks": ""
}
]
}
]

Worked by adding FETCH in query
#Repository
public interface JobRepository extends JpaRepository<JobName, Integer> {
#Query("select j from JobName j join fetch j.jobStatus s where s.startDate > :fromDate")
public List<JobName> findJobNameBylastTime(#Param("fromDate") Timestamp fromDate);
}

First, it seems like there is an error in your query, it should be s.startDate > :fromDate instead of s.jobStatus
Then, if you want to query JobStatus entites then you should use a JobStatusRepository as this :
#Repository
public interface JobStatusRepository extends JpaRepository<JobStatus, Integer> {
public List<JobName> findJobNameByCreationDateGreaterThan(Timestamp fromDate);
}
You shouldn't need a query as the method name is enough, you might want to add a ...Distinct... to avoid duplicates.

Related

Spring Boot JPA one-to-many bidirectional foreign key is null

I have two entities Account and Order where one account can have one or more orders. I want to be able to log into an Account and then have order(s) for that account. Whenever I post an order for an existing account everything gets filled in properly but "account_id"(the foreign key). I would like to know why "account_id" is null and what I can do to address this problem, thanks.
Here is the Account entity:
#Entity
#Table(name="account")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="first_name")
private String firstName;
#Column(name="last_name")
private String lastName;
#Column(name="email")
private String email;
#Column(name="user_name")
private String userName;
#Column(name="password")
private String password;
#Column(name="address")
private String address;
#Column(name="city")
private String city;
#Column(name="state")
private String state;
#Column(name="country")
private String country;
#Column(name="zipcode")
private String zipcode;
#Column(name="credit_card_number")
private String creditCardNumber;
#Column(name="credit_card_code")
private int creditCardCode;
#Column(name="credit_card_name")
private String creditCardName;
#Column(name="credit_card_expiration_month")
private int creditCardExpirationMonth;
#Column(name="credit_card_expiration_year")
private int creditCardExpirationYear;
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
private List<Order> orders;
/* constructor */
/* Getters and setters */
Here is the Order entity:
#Entity
#Table(name="orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
#Column(name="total_price")
private int totalPrice;
#Column(name="total_quantity")
private int totalQuantity;
#Column(name="date_created")
#CreationTimestamp
private Date dateCreated;
#Column(name="shipping_address")
private String shippingAddress;
#ManyToOne
#JoinColumn(name = "account_id",nullable = false, insertable = false, updatable = false)
private Account account;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "order")
private Set<OrderItem> orderItems = new HashSet<>();
/* constructor */
/* Getters and setters */
Here is the Order Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
Page<Order> findByAccountId(#Param("id") Long id, Pageable pageable);
}
Here is the Order Controller
#RestController
#RequestMapping("/api")
public class OrderController {
#Autowired
private OrderRepository orderRepository;
#PostMapping("/save-order")
public Order saveOrder(#RequestBody Order order) {
return orderRepository.save(order);
}
}
Here is the postman post for order
URL: "http://localhost:8080/api/save-order"
{
"totalPrice": 10.17,
"totalQuantity": 3,
"dateCreated": "2022-09-05",
"shippingAddress": "123 Road, London, United Kingdom",
"account": [{
"id": 1,
"firstName": "Winston",
"lastName": "Smith",
"email": "smith22#gmail.com",
"userName": "wilsonsmith22",
"password": "mypassword22",
"address": "123 Road",
"city": "London",
"state": "London Region",
"country": "United Kingdom",
"zipcode": "220220",
"creditCardNumber": "4422336644885500",
"creditCardCode": 234,
"creditCardName": "Winston Smith",
"creditCardExpirationMonth": 5,
"creditCardExpirationYear": 2025
}]
}
You have :
#ManyToOne
#JoinColumn(name = "account_id",nullable = false, insertable = false, updatable = false)
private Account account;
#Column(name = "account_id")
private long accountId;
Both fields account & accountId have the same name, which souldn't happen I guess.

Spring Boot GET method returns duplicated values with StackOverFlow error

I'm building a RESTful API GET method with Spring Boot to get return of a Bill entity as JSON from database. The return is not expected as it has many duplicated values and a StackOverFlowError.
[{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":{"id":1,"date":"2022-05-20","time":"16:48:06","total":330000.0,"billDetails":[{"billMenuItemID":{"billId":1,"menuItemId":1},"bill":
//continues for eternity
Hibernate log:
Hibernate:
select
bill0_.bill_id as bill_id1_0_,
bill0_.date as date2_0_,
bill0_.time as time3_0_,
bill0_.total as total4_0_
from
bill bill0_
Hibernate:
select
billdetail0_.bill_id as bill_id1_1_0_,
billdetail0_.menu_item_id as menu_ite2_1_0_,
billdetail0_.bill_id as bill_id1_1_1_,
billdetail0_.menu_item_id as menu_ite2_1_1_,
billdetail0_.quantity as quantity3_1_1_,
billdetail0_.subtotal as subtotal4_1_1_,
menuitem1_.menu_item_id as menu_ite1_2_2_,
menuitem1_.description as descript2_2_2_,
menuitem1_.img_url as img_url3_2_2_,
menuitem1_.name as name4_2_2_,
menuitem1_.price as price5_2_2_,
menuitem1_.status as status6_2_2_,
menuitem1_.type as type7_2_2_
from
bill_detail billdetail0_
inner join
menu_item menuitem1_
on billdetail0_.menu_item_id=menuitem1_.menu_item_id
where
billdetail0_.bill_id=?
How can I get a return of a Bill like this:
{
"billId": 1,
"date": 2022-05-20,
"time": 16:48:06,
"total": 330000,
"billDetails": [
{
"menuItem": {
"id": 1,
"name": Rice,
// other attributes of MenuItem
},
"quantity": 2
"subtotal": 90000
},
{
"menuItem": {
"id": 2
"name": Wine
// other attributes of MenuItem
},
"quantity": 4
"subtotal": 240000
}
]
}
This is my classes and related functions
Class Bill
#Entity(name = "bill")
#Table(name = "bill")
public class Bill {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "bill_id")
private Long id;
private LocalDate date;
private LocalTime time;
private Double total;
#OneToMany(mappedBy = "bill", cascade = CascadeType.ALL)
private List<BillDetail> billDetails = new ArrayList<>();
Class MenuItem
#Entity
#Table(name = "menuItem",
uniqueConstraints = {
#UniqueConstraint(name = "menu_item_name_unique", columnNames = "name")
}
)
public class MenuItem {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "menu_item_id")
private Long id;
private ItemType type;
private String name;
private String description;
private String imgUrl;
private Double price;
private MenuItemStatus status = MenuItemStatus.ENABLED;
#OneToMany(mappedBy = "menuItem", cascade = CascadeType.ALL)
private List<BillDetail> billDetails = new ArrayList<>();
Class BillDetail
#Entity
#Table(name = "bill_detail")
public class BillDetail {
#EmbeddedId
private BillMenuItemID billMenuItemID = new BillMenuItemID();
#ManyToOne
#MapsId("billId")
#JoinColumn(name = "bill_id")
private Bill bill;
#ManyToOne
#MapsId("menuItemId")
#JoinColumn(name = "menu_item_id")
private MenuItem menuItem;
#Column
private Long quantity;
#Column
private Double subtotal;
GET method
#GetMapping
public List<Bill> getMenuItems() {
return billService.getBills();
}
public List<Bill> getBills() {
return billRepository.findAll();
}
public interface BillRepository extends JpaRepository<Bill, Long> {
}
Database
database
In the class MenuItem you should add the annotation #JsonIgnore to prevent an infinite loop in the JSON format returned; a bill has a BillDetails , a BillDetails has a MenuItem , a MenuItem Has a BillDetails , every BillDetail has a List of MenuItem ...
#Entity
#Table(name = "menuItem",
uniqueConstraints = {
#UniqueConstraint(name = "menu_item_name_unique", columnNames = "name")
}
)
public class MenuItem {
#Id
#GeneratedValue(
strategy = GenerationType.IDENTITY
)
#Column(name = "menu_item_id")
private Long id;
private ItemType type;
private String name;
private String description;
private String imgUrl;
private Double price;
private MenuItemStatus status = MenuItemStatus.ENABLED;
// ADD JSON IGNORE ANNOTATION HERE :
#JsonIgnore
#OneToMany(mappedBy = "menuItem", cascade = CascadeType.ALL)
private List<BillDetail> billDetails = new ArrayList<>();

extended class doesn't load in frontend

my Entity is:
#Data
#Entity
#Table(name = "CORE_BOOK")
public class Book extends BaseEntity<Integer>{
#Column(name = "TITLE")
private String title;
#Column(name = "AUTHOR")
private String author;
#Column(name = "ISBN_NUMBER")
private String isbnNumber;
#Column(name = "price")
private Long price;
#Column(name = "LANGUAGE")
private String language;
#Column(name = "COVER_PHOTO_URL")
private String coverPhotoUrl;
}
and BaseEntity
#MappedSuperclass
public class BaseEntity<T extends Serializable> implements Serializable {
private static final long serialVersionUID = 95231478632145632L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private T id;
}
then when i breakpoint on findAll in controller i can see id in array List
#GetMapping("/list")
#ResponseBody
public List<Book> findAll() {
return bookRepository.findAll();
}
but in generated-request.http or in frontend (react table) , extended property (id) doesn't load
GET http://localhost:8080/rest/books/list
and the result as follows:
{
"title": "test",
"author": "SMH",
"isbnNumber": "513548",
"price": 150000,
"language": "English",
"coverPhotoUrl": "../images.jpg"
}
please help me how i can see extended property in frontend

how to display DTO correctly in Spring boot

I have two Entities in my spring boot application. I am working with dtos and displaying my dto in the end. But I am getting the wrong output from my getRequest. My first entity is MeetingSetting which can have multiple MeetingTimes and inside MeetingTime I have meetingName as a foreign key. I want to display meetingTime like this:
{
"id": 1,
"date": "2021-06-31",
"startTime": "15:30",
"endTime": "16:30",
"meetingName": "Test"
}
But I am getting instead this one:
{
"id": 1,
"date": "2021-06-31",
"startTime": "15:30",
"endTime": "16:30",
"meetingName": {
"id": 1,
"meetingName": "Tewasddweewrst2",
"meetingUrl": null,
"meetingPw": ""
}
}
Could someone take a look at my code and tell me what I am doing wrong?
MeetingSetting Entity::
#Entity
#Table(name = "meeting_settings")
#Setter
#Getter
public class MeetingsSetting implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name", unique = true)
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meetingName", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<MeetingTime> meetingTime = new HashSet<>();
}
MeetingSettingDTO:
#Getter
#Setter
public class MeetingSettingDTO {
private Long id;
#NotNull
private String meetingName;
#NotNull
private String meetingUrl;
#NotNull
private String meetingPw;
#JsonIgnore
private Set<MeetingTime> meetingTime;
}
MeetingTimeEntity:
#Entity
#Table(name = "meeting_times")
#Getter
#Setter
public class MeetingTime implements Serializable {
#JsonIgnore
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "meeting_name" , referencedColumnName = "meeting_name")
private MeetingsSetting meetingName;
}
MeetingTimeDTO:
#Getter
#Setter
public class MeetingTimeDTO {
private Long id;
#NotNull
private String date;
#NotNull
private String startTime;
#NotNull
private String endTime;
private MeetingSettingDTO meetingName;
}
In my service I am first getting MeetingTime as an entity from my repository then converting it to DTO and returning it for my controller:
#Service
public class MeetingTimeService {
ModelMapper modelMapper = new ModelMapper();
#Autowired
MeetingTimeRepository meetingTimeRepository;
public List<MeetingTimeDTO> findAllMeetingTimes(){
List<MeetingTime> meetingTimeList = meetingTimeRepository.findAll();
return meetingTimeList.stream()
.map(this::convertToDto)
.collect(Collectors.toList());
}
private MeetingTimeDTO convertToDto(MeetingTime meetingTime) {
MeetingTimeDTO meetingTimeDTO = modelMapper.map(meetingTime, MeetingTimeDTO.class);
return meetingTimeDTO;
}
}
Controller:
#GetMapping(value = "/" )
public List<MeetingTimeDTO> getAllTimes() {
return meetingTimeService.findAllMeetingTimes();
}
In MeetingTimeDTO:
private MeetingSettingDTO meetingName;
The type needs to be changed to:
private string meetingName;
I have not worked with ModelMapper so cannot help you with how to map a specific field from the related object but this answer here seems to provide the info needed.

Jpa join with where clause in first an second tables

I have a problem with JPA and two tables
Entity Language
public class Language {
#NotEmpty
#Id
#Column(name = "code", nullable = false, length = 5)
private String code;
#Column(nullable = false)
private Boolean active;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "language", cascade = CascadeType.MERGE, orphanRemoval = true)
private List<LanguageTranslation> languageTranslation;
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
Entity LanguageTranslation
public class LanguageTranslation {
#EmbeddedId
private LanguageTranslationId languageTranslationId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("languageCode")
#JoinColumn(name = "language_code", nullable = false)
#JsonIgnore
private Language language;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("languageLanguageCode")
#JoinColumn(name = "language_language_code", nullable = false)
#JsonIgnore
private Language languageLanguage;
#Column(nullable = false, length = 255)
#NotEmpty
private String name;
public LanguageTranslation(LanguageTranslationId languageTranslationId, #NotEmpty String name)
{
super();
this.languageTranslationId = languageTranslationId;
this.name = name;
}
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
ID
#Embeddable
public class LanguageTranslationId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "language_code")
private String languageCode;
#Column(name = "language_language_code")
private String languageLanguageCode;
#JsonPOJOBuilder(withPrefix = "")
public static class Builder {
// Lombok will add constructor, setters, build method
}
}
Query
#Query(value = "SELECT l FROM Language l " +
"JOIN LanguageTranslation lt ON l.code = lt.languageTranslationId.languageCode " +
"WHERE lt.languageTranslationId.languageLanguageCode = ?2 " +
"AND l.code = ?1 AND l.active = true")
Language findByCodeByTranslationAndActiveTrue(String id, String translation);
The problem is this WHERE lt.languageTranslationId.languageLanguageCode = ?2 it's useless. JPA doesn't execute this clause
Whith id = en_EN and translation = en_EN I have this
{
"code": "en_EN",
"languageTranslation": [
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "en_EN"
},
"name": "English"
},
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "es_ES"
},
"name": "Ingles"
}
]
}
And normally I would have to have this
{
"code": "en_EN",
"languageTranslation": [
{
"languageTranslationId": {
"languageCode": "en_EN",
"languageLanguageCode": "en_EN"
},
"name": "English"
}
]
}

Categories

Resources