How to retrieve data based on inverseColumn data using CrudRepository in springboot? - spring

I have two tables i.e. users and events. Users table will be filled when new user will sign up. Later same user can create calendar events. so events table will be filled and users_events will keep mapping of events based on user.
I would like to find all events based on logged in userId. so here is query, it should return data based on it.
select * from events where eventid in (select eventId from users_event where id_user=x ). Here is my Users and Event Entity class.
User.java
#Entity
#Table(name = "users")
public class User {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "family_name", nullable = false)
private String familyName;
#Column(name = "e_mail", nullable = false)
private String email;
#Column(name = "phone", nullable = false)
private String phone;
#Column(name = "language", nullable = false)
private String language;
#Column(name = "id_picture")
private String pictureId;
#Column(name = "login", nullable = false)
private String login;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "enabled")
private Boolean enabled;
//getter and setter
Event.java
#Entity
#Table(name = "events")
public class Event {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "eventId", nullable = false)
private Long eventId;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "startAt", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date startAt;
#Column(name = "endAt", nullable = true)
#Temporal(TemporalType.TIMESTAMP)
private Date endAt;
#Column(name = "isFullDay", nullable = false)
private Boolean isFullDay;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "users_event", joinColumns = { #JoinColumn(name = "id_event", referencedColumnName = "eventId") }, inverseJoinColumns = { #JoinColumn(name = "id_user", table = "users", referencedColumnName = "id") })
private Set<User> user = new HashSet<User>();
/getter and setter
EventRepo.java
public interface EventRepo extends CrudRepository<Event, Long> {
Event findByUser(Set<User> user);
}
I am trying to implement something, which can give me output of this query.
select * from events where eventid in (select eventId from users_event where id_user=x )
here is my implementation.any input please?
#RequestMapping(value = "/events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents() {
logger.debug("get event list");
User x=new User();
x.setId(1);
Set<User> user= new HashSet();
user.add(x);
return (List<Event>) eventRepo.findByUser(user);
}

Just add a following method to your EventRepo:
List<Event> findAllByUserId(Long userId);
And modify your controller to something like this:
#RequestMapping(value = "/events", method = RequestMethod.GET)
public List<Event> getEvents() {
return eventRepo.findAllByUserId(1L);
}

Related

Spring specification query for one to many join and sum of field is grater then given value

I am creating filter using joining this three entity seeker_job_application,seeker_profile and seeker_experience. where I want achieve result as below query.
In filter I want to find out seeker_profile whose total_moth of experience should be grater then or equal to given value i.e 20, one seeker_profile has multiple experience so I need to group by profile and sum of their experience and then compare with given value. is it possible to do this using spring specification?
How to check that seeker total month of experience is grater then or equal to given value?
Relation between table is
seeker_job_application 1<-->1 seeker_profile 1<---->* seeker_experience
Want to achieve query like this
select r.sja_id,r.sp_id,r.name,r.company_name,r.total_month from (
select sja.id as sja_id , sp.id as sp_id , sp.`name`,se.company_name,sum(se.total_month) as total_month
from seeker_job_application sja
INNER JOIN seeker_profile sp on sp.id = sja.seeker_id
INNER JOIN seeker_experience se on se.seeker_id = sp.id
where job_id =1 group by sp.id ) as r where r.total_month > 20;
#Entity
#Table(name = "seeker_job_application")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SeekerJobApplication implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "seeker_id", nullable = false)
private Long seekerId;
#NotNull
#Column(name = "job_id", nullable = false)
private Long jobId;
#Column(name = "apply_date")
private Instant applyDate;
#Column(name = "profile_viewed")
private Boolean profileViewed;
#Column(name = "on_hold")
private Boolean onHold;
#Column(name = "interview_schedule")
private Boolean interviewSchedule;
#Column(name = "rejected")
private Boolean rejected;
#Column(name = "selected")
private Boolean selected;
#Column(name = "prefered_location_id")
private Long preferedLocationId;
#Column(name = "work_preference")
private String workPreference;
#Column(name = "resume_file_path")
private String resumeFilePath;
#Column(name = "status")
private String status;
#ManyToOne
#JoinColumn(name="seeker_id",referencedColumnName = "id", insertable = false, updatable = false)
private SeekerProfile seekerProfile;
#Data
#Entity
#Table(name = "seeker_profile")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SeekerProfile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "mobile_number", nullable = false)
private String mobileNumber;
#Column(name = "password")
private String password;
#Column(name = "email", nullable = false)
private String email;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "address_line_1")
private String addressLine1;
#Column(name = "address_line_2")
private String addressLine2;
#Column(name = "city")
private String city;
#Column(name = "postcode")
private String postcode;
#Column(name = "state")
private String state;
#Column(name = "country")
private String country;
#Column(name = "website")
private String website;
#Column(name = "linkedin")
private String linkedin;
#Column(name = "facebook")
private String facebook;
#Column(name = "gender")
private String gender;
#Column(name = "dob")
private String dob;
#Column(name = "resume")
private String resume;
#Column(name = "wfh")
private String wfh;
#Column(name = "profile_completed")
private String profileCompleted;
#OneToOne
#JoinColumn(unique = true)
private Location preferedLocation;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "seeker_skill", joinColumns = { #JoinColumn(name = "seeker_id") }, inverseJoinColumns = { #JoinColumn(name = "skill_id") })
private Set<Skill> skills;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name="seeker_id",referencedColumnName = "id", insertable = false, updatable = false)
private Set<SeekerExperience> seekerExperiences;
#OneToMany
#JoinColumn(name="seeker_id",referencedColumnName = "id", insertable = false, updatable = false)
private Set<SeekerEducation> seekerEducation;
#Entity
#Table(name = "seeker_experience")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class SeekerExperience implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "seeker_id", nullable = false)
private Long seekerId;
#NotNull
#Column(name = "job_title", nullable = false)
private String jobTitle;
#NotNull
#Column(name = "company_name", nullable = false)
private String companyName;
#NotNull
#Column(name = "start_date", nullable = false)
private String startDate;
#NotNull
#Column(name = "end_date", nullable = false)
private String endDate;
#Column(name = "total_month")
private Integer totalMonth;
#Column(name = "location")
private String location;
#Column(name = "role_description")
private String roleDescription;
Specification<SeekerJobApplication> specification = Specification.where(null);
specification = specification.and((root, query, cb) -> {
Join<SeekerJobApplication, SeekerProfile> seekerProfile=root.join(SeekerJobApplication_.seekerProfile);
Join<SeekerProfile, SeekerExperience> seekerExperience = seekerProfile.join(SeekerProfile_.seekerExperiences);
query.having(cb.greaterThanOrEqualTo(cb.sum(seekerExperience.get(SeekerExperience_.totalMonth)), criteria.getTotalExperience().getEquals()));
query.getRestriction();
});
This will give me result as below query
select sja.* from seeker_job_application sja
INNER JOIN seeker_profile sp on sja.seeker_id = sp.id
INNER JOIN seeker_experience se on se.seeker_id = sp.id
where sja.job_id = 1
GROUP BY sp.id
having sum(se.total_month) > 20

Spring Data persisting Phantom Child with Null value - not null property references a null or transient value

I have the following Entities in my Project:
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "purchaseId" }))
public class Purchase {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseId;
#Column(unique = true, nullable = false, length = 15)
private String purchaseNo;
#Column(nullable = false, length = 15)
private String batchCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "supplier.supplierId", foreignKey = #ForeignKey(name = "FK_purchase_supplier"), nullable = false)
private Supplier supplier;
#Column(nullable = false)
private LocalDate purchaseDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "purchaseId", nullable = false)
private List<PurchaseItem> purchaseItems;
private Double totalAmount;
#ManyToOne
#JoinColumn(name = "userId", nullable = false, foreignKey = #ForeignKey(name = "FK_invoice_purchases"))
private User staff;
#Column(length = 100)
private String remarks;
#Column(nullable = false, updatable = false)
#CreationTimestamp
private LocalDateTime createdAt;
private boolean isDeleted = false;
}
#Getter
#Setter
#Entity
#Table(uniqueConstraints = #UniqueConstraint(columnNames = {"purchaseItemId"}))
public class PurchaseItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long purchaseItemId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "purchaseId", insertable = false, updatable = false, foreignKey = #ForeignKey(name="FK_purchase_item"))
private Purchase purchase;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name="FK_product_item"), nullable = false)
private Product product;
private Double itemAmount;
#Column(nullable = false)
private Double quantity;
private Double itemTotalAmount;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
#PrimaryKeyJoinColumn(foreignKey = #ForeignKey(name = "FK_purchacase_item_batch"))
private PurchaseProductBatch productPurchaseBatch;
public void setPurchaseProductBatch() {
PurchaseProductBatch productPurchaseBatch = new PurchaseProductBatch();
productPurchaseBatch.setProduct(this.product);
productPurchaseBatch.setQuantity(this.quantity);
productPurchaseBatch.setPurchaseItem(this);
this.productPurchaseBatch = productPurchaseBatch;
}
}
#Getter
#Setter
#Entity
#Table()
public class PurchaseProductBatch{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long productBatchId;
#ManyToOne(cascade = CascadeType.DETACH)
#JoinColumn(name = "productId", foreignKey = #ForeignKey(name = "FK_product_purch"))
private Product product;
private Double quantity;
#OneToOne(fetch = FetchType.EAGER)
#MapsId
private PurchaseItem purchaseItem;
private boolean isDeleted = false;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "productBatchId", foreignKey = #ForeignKey(name = "FK_purchase_batch_qty"))
private Set<InvoicePurchaseBatchQuantity> invoicePurchaseBatchQuantities;
}
During Purchase Insert, everything works fine. However, if I update the Purchase record in the database and add new PurchaseItem entry, I encounter the issue below:
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : com.be.entity.PurchaseItem.product; nested
I have debugged my application and I see that there is a Product instance inside all of the PurchaseItem. When I commented out the PurchaseProductBatch inside PurchaseItem, everything works fine so I conclude that it is the causing the issue. However, I don't understand how and why JPA seems to create phantom PurchaseItem Records with no value.
Also, if I only update an existing PurchaseItem entry in Purchase, I don't encounter any issues.

Why hibernate is throwing constraintViolationException?

Order Entity
#Entity
#Table(name = "Order",
indexes = {
#Index(name = "ORDER_X1", columnList = "REFERENCE_ID,SOURCE_ID"),
#Index(name = "ORDER_X2", columnList = "TYPE,STATUS")
}
)
#DiscriminatorColumn(name="PROCESSOR_TYPE", discriminatorType=DiscriminatorType.STRING, length = 80)
#SequenceGenerator(name="orderSeq", sequenceName="ORDER_SEQ")
#Inheritance(strategy= InheritanceType.JOINED)
public abstract class OrderEntity implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE, generator="orderSeq")
private Long id;
#ManyToMany(cascade={CascadeType.MERGE})
#JoinTable(
name = "FILE_ORDER_MAP",
joinColumns = {#JoinColumn(name = "ORDER_ID")},
inverseJoinColumns = {#JoinColumn(name = "FILE_ID")}
)
private Set<TransferFile> transferFiles = new HashSet<>();
#Column(name = "TYPE")
#Enumerated(EnumType.STRING)
private OrderType type;
#Column(name = "AMOUNT", precision = 12, scale = 2)
private LcMoney amount;
#Column(name = "STATUS")
#Enumerated(EnumType.STRING)
private OrderStatus reconStatus;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "STATUS_D", nullable = false)
#LcDateTimeUtc()
private DateTime reconStatusDate;
#Column(name = "REFERENCE_ID")
private Long referenceId;
#Column(name = "SOURCE_ID")
private Long sourceId;
#Column(name = "ACCOUNT_ID")
private Long accountId;
#Column(name = "PROCESSOR_TYPE", insertable = false, updatable = false)
#Enumerated(EnumType.STRING)
private OrderProcessorType processorType;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "TX_EXECUTION_D")
#LcDateTimeUtc()
private DateTime executedDate;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "CREATE_D")
#LcDateTimeUtc()
private DateTime createDate;
#Column(name = "IS_ON_DEMAND")
#Type(type = "yes_no")
private boolean isOnDemand;
#ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.PERSIST})
#JoinColumn(name="PAYER_ID", nullable=true)
private Payer payer;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID")
private List<OrderTransaction> orderTransactions;
#OneToMany(cascade = {CascadeType.ALL})
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID",
foreignKey = #ForeignKey(name = "FK_ORDER")
)
private List<MatchResult> matchResults;
#Version
#Column(name = "VERSION")
private Integer version;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "externalSourceId", column = #Column(name = "TRANS_EXT_SRC_ID")),
#AttributeOverride(name = "externalId", column = #Column(name = "TRANS_EXT_REF_ID"))
})
private LcExternalIdEntity transExtId;
#PreUpdate
#PrePersist
public void beforePersist() {
if (reconStatusDate != null) {
reconStatusDate = reconStatusDate.withZone(DateTimeZone.UTC);
}
if (executedDate != null) {
executedDate = executedDate.withZone(DateTimeZone.UTC);
}
if (createDate != null) {
createDate = createDate.withZone(DateTimeZone.UTC);
}
}
// getters and setters
}
//controller method
public Response processFile(){
// separate trasaction
service.readFileAndCreateOrders(); // read files and create orders in new status
List<Order> newOrders = service.getNewOrders();
for( Order order: newOrders){
service.processOrder(order); // separate transaction
}
}
#Transaction
void processOrder(OrderEntity order){
matchResultJpaRepository.save(orderEntity.id);
log.info("Saving matchId={} for order={}", match.getId(), order.getId());
// create new transaction and add to order
OrderTransaction transaction = createNewTransaction(order);
order.getTransactions().add(transaction);
order.setStatus("PROCESSED");
log.info("Saving Order id={}, Type={}, Status={} ", order.getId(), order.getType(), order.getStatus());
orderRepository.save(order);
}
I am seeing this below error.
ORA-01407: cannot update ("PAYMENTS"."MATCH_RESULT"."ORDER_ID") to NULL
This endpoing is not exposed to user. There is a batch job which invokes this endpoint.
This code has been there for atleast a year and this is the first time i am seeing this.
This happened only once and for only one call. I am seeing both the logs printed. I am puzzled why I am seeing above error complaining about NULL order id. From the logs, we can confirm that the order id is definitely not null.
Any idea why this is happening? What can be done to fix this?

Spring JpaRepository manyToMany bidirectional should save instead of update

if got a language table and a system table with a many-to-many relationship:
Language:
#JsonAutoDetect
#Entity
#Table(name = "language")
public class Language implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "language_id", nullable = false)
private int languageId;
#Column(name = "language_name", nullable = false)
private String languageName;
#Column(name = "language_isocode", nullable = false)
private String languageIsoCode;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "system_language", joinColumns = {#JoinColumn(name = "language_id", updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "system_id", updatable = false)}, uniqueConstraints = {
#UniqueConstraint(columnNames = {
"language_id",
"system_id"
})})
private List<System> systems;
public Language() {
}
// GETTER & SETTERS
// ....
}
System
#JsonAutoDetect
#Entity
#Table(name = "system")
public class System implements Serializable {
#Id
#Column(name = "system_id", nullable = false)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer systemId;
#Column(name = "system_name", nullable = false, unique = true)
private String systemName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id", nullable = false)
private University university;
#JoinColumn(name = "calender_id", nullable = false)
#OneToOne(fetch = FetchType.EAGER)
private Calendar calender;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<SystemUserRole> systemUserRoleList;
#OneToMany(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Role> roleList;
#OneToOne(mappedBy = "system")
#LazyCollection(LazyCollectionOption.FALSE)
private CsmUserEntity csmUserEntity;
#ManyToMany(mappedBy = "systems")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Language> languages;
public System() {
}
// GETTER & SETTERS
// ....
}
When im writing a first dataset (systemId=1, language_id=20) into the table, everything works fine. But when i try to write a second dataset with the same language_id but with other system_id (systemId=2, language_id=20), then the existing dataset gets updated. But i want to have a new dataset instead. What can i do?
Thanks in advance!

one to many mapping in hibernate for a messaging service

I have two JPA entities: User and Message.
Each Message has one sender and one receiver of type User. And on the other side each User has two sets of type Message: inbox and outbox.
Message:
#Entity
#Table(name = "messages")
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seq")
#SequenceGenerator(name = "seq", sequenceName = "MESSAGES_SEQ")
#Column(name = "ID", insertable = false, updatable = false)
private int id;
#ManyToOne
#JoinColumn(name = "SENDER")
private User sender;
#ManyToOne
#JoinColumn(name = "RECEIVER")
private User receiver;
private String subject, content;
private Date sdate;
//getters and setters
}
All the properties which not being mapped with an annotation has he same name as the columns in database and are automatically mapped by JPA.
User:
#Entity
#Table(name = "users")
public class User {
#Column(name = "USERNAME")
private String username;
#Column(name = "PASSWORD")
private String pass;
#Column(name = "EMAIL")
private String email;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "seq")
#SequenceGenerator(name = "seq", sequenceName = "USERS_SEQ")
#Column(name = "ID", insertable = false, updatable = false)
private int id;
#OneToMany(mappedBy = "uploader")
private Set<Book> books;
#OneToMany(mappedBy = "receiver")
private Set<Message> inbox;
#OneToMany(mappedBy = "sender")
private Set<Message> outbox;
//getters and setters
}
The problem is, when I select an User from Oracle database, then the inbox property is empty. How is this caused and how can I solve it?

Resources