One to many relationship in springboot - java

I have a file named data.sql which contains SQL queries 'INSERT INTO'.
I have table User which model is:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user")
#Column(name = "vacations")
private Set<Vacation> vacations = new HashSet<>();
And I have model Vacation where is:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "begin_date")
private LocalDateTime beginDateOfVacation;
#NotNull
#Column(name = "end_date")
private LocalDateTime endDateOfVacation;
#NotEmpty
#Column(name = "type")
private String typeOfVacation;
#NotEmpty
#Column(name = "reason")
private String reasonOfVacation;
#ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
And in my data.sql I am trying to insert into vacation User with existing ID.
It "passing" threw compiler, but on localhost I can see only this:
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.springproject27.springproject.user.User["vacations"])
It is H2 Database Engine, the query which I try to pass is:
INSERT INTO VACATION(ID,BEGIN_DATE,END_DATE,TYPE,REASON,USER_ID) VALUES
(22,'2012-09-17 18:47:52.69','2012-09-20 18:47:52.69','Unpaid leave','Sick',10);

As you see its happening because of JSON recursion when you are trying to serialize your data because of bi-directional relationship between User entity & Vacation entity. Preferred method to use #JsonIgnoreProperties (if you are using Jackson 2.0+ version) to break the recursion during serialization.
NOTE: Other way to break the JSON recursion is using JsonBackReference and JsonManagedReference in combiation. But I prefer #JsonIgnoreProperties due to no data loss during serialization.
If you are using Lombok, only use #Getter & #Setter annotation not #ToString (or #Data which has #ToString included), as it will cause the same recursion issue.
class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
#OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "user")
#Column(name = "vacations")
#JsonIgnoreProperties("user")
private Set<Vacation> vacations = new HashSet<>();
}
class Vacation{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Column(name = "begin_date")
private LocalDateTime beginDateOfVacation;
#NotNull
#Column(name = "end_date")
private LocalDateTime endDateOfVacation;
#NotEmpty
#Column(name = "type")
private String typeOfVacation;
#NotEmpty
#Column(name = "reason")
private String reasonOfVacation;
#ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonIgnoreProperties("vacations")
private User user;
}

Related

Error accessing field [private int in Springboot data JPA?

This is entity class , I simply want to save this entity but when I am trying to hit the api I am getting error as given below , I am pretty much sure it is due some mapping issue , any sort of help is appreciated :-
#Entity
#Table(name = "productpurchasedata")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "customerid")
private int customerid;
#Column(name = "name")
private String name;
#Column(name = "mailid")
private String mailid;
#Column(name = "address")
private String address;
#Column(name = "city")
private String city;
#Column(name = "state")
private String state;
#Column(name = "postalcode")
private String postalcode;
#Column(name = "mobileno")
private long mobileno;
#ManyToOne(targetEntity = Test.class, fetch = FetchType.LAZY,optional = false)
#JoinColumns({ #JoinColumn(name = "customerid", referencedColumnName = "customerid", insertable=false, updatable=false) })
private PurchaseDetails purchasedetails;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = Test.class)
#JoinColumn(name = "customerid")
private List<ProductPurchased> productpurchased;
#ManyToOne(targetEntity = Test.class, fetch = FetchType.LAZY,optional = false)
#JoinColumns({ #JoinColumn(name = "customerid", referencedColumnName = "customerid", insertable=false, updatable=false) })
private PaymentHistory paymenthistory;
Error:
"message": "Error accessing field [private int com.bhushan.spring.files.excel.model.Test.customerid] by reflection for persistent property [com.bhushan.spring.files.excel.model.Test#customerid] : com.bhushan.spring.files.excel.model.PaymentHistory#7659b9; nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private int com.bhushan.spring.files.excel.model.Test.customerid] by reflection for persistent property [com.bhushan.spring.files.excel.model.Test#customerid] : com.bhushan.spring.files.excel.model.PaymentHistory#7659b9",
You relationship look like wrong I guess. Because one user have many payment history and purchase details not other way round.
Many user doesn't have same same history.
#ManyToOne
private PurchaseDetails purchasedetails;
#ManyToOne
private PaymentHistory paymenthistory

Spring boot JPA related entity clarification

I have an entity like Process, which will be created by , updated by one user. When I try to apply the filter. I have created the foreign key relationship in the database. Now, when I use the JPA Specification to apply dynamic filter, I am getting exception as
No property CREATED found for type Process!
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id = null;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name = null;
#Column(name = "CREATED_BY", updatable = false)
#JsonProperty("createdBy")
private Long createdBy = null;
#Column(name = "updatedBy", nullable = true)
#JsonProperty("updatedBy")
private Long updatedBy = null;
}
Hence, I Added the entity relationship mapping in the process entity as given below,
Now, I am getting below error. I am new to JPA and hibernate, the relation mapping is very confusing, kindly help.
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id = null;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name = null;
#Column(name = "CREATED_BY", updatable = false)
#JsonProperty("createdBy")
private Long createdBy = null;
#Column(name = "updatedBy", nullable = true)
#JsonProperty("updatedBy")
private Long updatedBy = null;
//newly added below properties so that there will be no error when fetching data
#OneToOne(targetEntity = UserDetails.class, fetch = FetchType.LAZY, mappedBy = "id")
private UserDetails CREATED;
#OneToOne(targetEntity = UserDetails.class, fetch = FetchType.LAZY, mappedBy = "id")
private UserDetails UPDATED;
}
Now, I am getting the below error
Referenced property not a (One|Many)ToOne: com.app.users.details.domain.UserDetails.id in mappedBy of com.app.scenarios.domain.Process.CREATED
Kindly let me know what i am doing wrong. I have a process which can be created by a user and can be updated by a user. In DB, I am having a foreign key relationship for process and userdetails entity.
EDIT
Code to get the filtered data from DB using JPA Specification
Page<process> result = this.processDao.findAll(getprocessGridData(processSearchCondition.getprocessName()), pageRequest);
private static Specification<process> getprocessGridData(String processName) {
return (Specification<process>) (root, query, criteriaBuilder) -> (
criteriaBuilder.like(root.get("name"), processName)
);
}
I guess what you actually want is this:
#Table(name = "process")
#Entity
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROCESS_ID")
#JsonProperty("id")
private Long id;
#NotNull
#Column(name = "NAME")
#JsonProperty("name")
private String name;
#OneToOne(fetch = FetchType.LAZY)
#jOINColumn(name = "CREATED_BY", updatable = false)
private UserDetails createdBy;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "UPDATED_BY", nullable = true)
private UserDetails updatedBy;
}

Spring Data JPA/Hibernate LazyInitializationException

I am new to hibernate/spring data JPA and occuring some problems when dealing with Foreign Keys.
I have the two table:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String role;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "job_id", referencedColumnName = "id")
private Job job;
}
#Entity
public class Job{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#OneToMany(
mappedBy = "job",
cascade = CascadeType.PERSIST,
fetch = FetchType.EAGER
)
private Set<User> users = new HashSet<>();
}
I am trying to save one Job (job.Id) at the Users table. But if I am trying to peform a get(repository.getOne(id)) (via ...extends JPARepository<User,Long> I am getting the following error:
org.hibernate.LazyInitializationException: could not initialize proxy [backend.entity.User#193] - no Session
Does anyone has a Tip how to fix it? I have read a lot on stackoverflow, but could not find out how to fix that.

Hibernate unexpected deletes when finding entities

We have a Java ee application running on JBoss 6.4 GA using JPA and Hibernate with the following entities:
#Entity
#SequenceGenerator(name = "sequence", sequenceName="SEQ_CAMPAIGNS_ID",allocationSize = 1)
#Table(name = "CAMPAIGN")
public class CampaignEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "IS_ACTIVE", nullable = false)
private boolean active;
#Column(name = "START_DATE", nullable = false)
private Date startDate;
#Column(name = "END_DATE", nullable = false)
private Date endDate;
#Column(name = "LEGAL_ENTITY_ID", nullable = false)
private Integer legalEntityId;
#Column(name = "DEPARTMENT", nullable = false)
#Enumerated(value = EnumType.STRING)
private Department department;
#Column(name = "CATEGORY", nullable = false)
#Enumerated(value = EnumType.STRING)
private Category category;
#Embedded
CampaignConditionsEntity campaignConditions;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "campaign", orphanRemoval = true)
#OrderBy
private List<CodeEntity> campaignCodes;
public CampaignEntity() {
}
And the following CampaignConditionsEntity:
#Embeddable
public class CampaignConditionsEntity implements Serializable {
private static final String CAMPAIGN_ID = "CAMPAIGN_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CAMPAIGN_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CAMPAIGN_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
And the following CodeEntity:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#SequenceGenerator(name = "sequence", sequenceName = "SEQ_CODES_ID", allocationSize = 1)
public abstract class CodeEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
#Column(name = "ID", nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CAMPAIGN_ID")
private CampaignEntity campaign;
#OneToOne(mappedBy = "code", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false, orphanRemoval = true)
private DiscountEntity discount;
#Column(name = "MAX_USAGES", nullable = false)
private Integer maxUsages;
#Column(name = "UNLIMITED_USAGES", nullable = false)
private boolean unlimitedUsages;
#Column(name = "NEGATIVE_SH", nullable = false)
private boolean negativeSH;
#Column(name = "UNIQUE_BUYER", nullable = false)
private boolean uniqueBuyer;
#Column(name = "START_DATE")
private Date startDate;
#Column(name = "END_DATE")
private Date endDate;
#Embedded
private CodeConditionsEntity codeConditions;
public CodeEntity() {
}
This is the CodeConditionsEntity:
#Embeddable
public class CodeConditionsEntity implements Serializable {
private static final String CODE_ID = "CODE_ID";
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_TRIP_TYPE", joinColumns = #JoinColumn(name = CODE_ID))
private Set<TripTypeConditionEntity> tripTypeConditions;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "CODE_COND_CARRIERS", joinColumns = #JoinColumn(name = CODE_ID))
private Set<CarrierConditionEntity> carrierConditions;
This is the CarrierConditionEntity:
#Embeddable
public class CarrierConditionEntity implements Serializable {
#Column(name = "CARRIER", nullable = false, length = 3)
private String carrierCode;
#Column(name = "IS_INCLUDED", nullable = false)
private boolean included;
The problem is that in the logs we are finding unexpected deletes when the only operation that we are doing are finds of particular campaign entities.
In the production logs we find the following deletes
Hibernate: delete from CODE_COND_CARRIERS where CODE_ID=? and CARRIER=? and IS_INCLUDED=?
do you have any suggestion?
thanks
I have some suggestions :)
Be aware of what is a Persistence Context (EntityManager instance in JPA terminology / Session in Hibernate one), the entity lifecycle and transaction scope (unit of work)
Do not mutate entity state if you don't expect the changes to be reflected in database, or at least detach the entity before mutating it.
Mark your transaction as "readOnly" if you only fetch data in the related unit of work. (beware that if you have many "Transactional" methods joining the same physical transaction, the flag is set by the surrounding one and cannot be overridden by inner logical transactions). That way the EntityManager won't be flushed at the end of the transaction and pending changes won't be persisted to the database.
You can track the method triggering the unexpected deletion using an EntityListener on the related entity and printing the current strackTrace (new Throwable().printStackTrace()/ log(new Throwable()) in the PreRemove method
I found where was the problem:
The problem was that the Entities didn't have the equals() and the hashcode() implemented. Also there were entities that have a #PostLoad that modified the entity after loading it from database. Then in this situation Hibernate though that there was a change in those entities that didn't have the equals and the hashcode, and then it delete all of them and inserted again in the database (to have the same entities before the query)
Adding the equals and hashcode methods and deleting postload removed the unexpected deletes and inserts from the logs.
regards

How to config Spring Security with JPA?

I need to add Spring Security into my project. What is the right way to do it? I have to entities User and UserRole and DAO and Services for them. I use EntityManager to access data. I read, that I just need to write implementation for UserDetails, but I don't know how to do it correctly. Here my code:
User.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty
private Integer id;
#Column(name = "username", length = 20, nullable = false)
#JsonProperty
private String username;
#Column(name = "password", nullable = false)
#JsonProperty
private String password;
#Column(name = "enabled", nullable = false)
#JsonProperty
private boolean enabled;
#Column(name = "email", nullable = false)
#JsonProperty
private String email;
#OneToMany(mappedBy = "user", cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Set<UserRole> userRoles;
//getters and setters
UserRole.java
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty
private Integer id;
#ManyToOne(optional = false)
#JoinColumn(name = "user_ID", referencedColumnName = "id", foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT))
private User user;
#Column(name="role")
#JsonProperty
private String role;
//getters and setters
What should I do?
I had wrote a blog post about exactly what you are looking for. see this post and I am pretty sure it will answer your question:
https://giannisapi.wordpress.com/2011/09/21/spring-3-spring-security-implementing-custom-userdetails-with-hibernate/
In the Service layer of UserDetails below, pay attention that it implements UserDetailsService from org.springframework.security.core.userdetails.UserDetailsService.
and also :
he loadUserByUsername methods return the result of the assembler.buildUserFromUserEntity . Simply put, what this method of the assembler does is to to construct a org.springframework.security.core.userdetails.User object from the given UserEntity DTO. The code of the Assembler class is given below:

Categories