I have a question about how to avoid circular references and stackoverflows.
I have a User object and another News Object (with a User variable). I need a Comments object (that already has a News variable), but I also need that it has a reference to the User that has created the Comment.
If I create a User variable inside my Comment object I will have circular references and stackoverflows, so I think that I should only incluide a variable like userid in my Comment object.
So it could be that I'm right in my thinking or that I'm doing something wrong to get the stackoverflow errors. What would you do and why? If you can help, that will be great. Thanks.
This is the User...
#Entity
#Table(name = "users")
#PasswordMatch(message = "{register.repeatpassword.mismatch}")
public class SiteUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "email", unique = true)
#Email(message = "{register.email.invalid}")
#NotBlank(message = "{register.email.invalid}")
private String email;
#Transient
#Size(min = 5, max = 15, message = "{register.password.size}")
private String plainPassword;
#Column(name = "password", length = 60)
private String password;
#Column(name = "enabled")
private Boolean enabled = false;
#NotNull
#Column(name = "firstname", length = 20)
#Size(min = 2, max = 20, message = "{register.firstname.size}")
private String firstname;
#NotNull
#Column(name = "surname", length = 25)
#Size(min = 2, max = 25, message = "{register.surname.size}")
private String surname;
#Transient
private String repeatPassword;
#Column(name = "role", length = 20)
private String role;
public SiteUser() {
}
Here comes the StatusUpdate(you can call it piece of news or article). It has a site user that is the one who has created that article.
#Entity
#Table(name = "status_update")
public class StatusUpdate {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min=5, max=255, message="{addstatus.title.size}")
#Column(name = "title")
private String title;
#Size(min=5, max=5000, message="{addstatus.text.size}")
#Column(name = "text")
private String text;
#Column(name = "added")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss")
private Date added;
#OneToOne(targetEntity = SiteUser.class)
#JoinColumn(name="user_id")
private SiteUser siteUser;
#PrePersist
protected void onCreate() {
if (added == null) {
added = new Date();
}
}
public StatusUpdate() {
}
And the Comment which can be done by any registered user, right? As you will notice the Comment has no User object to avoid circular references. And that is the question. How can avoid circular references if Autowired a User
#Entity
#Table(name = "comments")
public class Comment {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "statusupdateid")
private StatusUpdate statusUpdate;
#Column(name = "commenttext")
private String commenttext;
#Column(name = "commentdate")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern = "yyyy/MM/dd hh:mm:ss")
private Date commentdate;
#Column(name = "userid")
private Long userid;
public Comment() {
}
Related
public class EmployeeEntity {
#Id
#Column(name = "id")
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#Length(min = 2, max = 30)
#Column(name = "name")
private String name;
#Length(min = 2, max = 30)
#Column(name = "last_name")
private String lastName;
#Column(name = "email", nullable = false, unique = true)
#Length(max = 50)
private String email;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "employee_id")
private Set<EmployeeRoleEntity> roles;}
This is my Employee class and as you can see inside Employee, I have a set of EmployeeRoleEntity
public class EmployeeRoleEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "role_name")
#Enumerated(EnumType.STRING)
private RoleEntityEnum role;
#ManyToOne
#JoinColumn(name = "employee_id")
#ToString.Exclude
#EqualsAndHashCode.Exclude
private EmployeeEntity employee;
I was trying to filter my employees depends in their role. I created a method like this on my Jpa repository;
List<EmployeeEntity> findByRoles_RoleContainingIgnoreCase( String role);
But it doesn't work and Im so confused to what to do. How can I solve this problem?
Finally I found the answer;
List<EmployeeEntity> findByRoles_Role( RoleEntityEnum role);
This solved my problem. I thought at first the problem was the method but appearently the problem was parameter.
Trying to run a delete query on a many-one relationship. But sometime it's stuck for a while when the count of row delete is more then ~50.
Repository:
#Repository
public interface TransitItemRepository extends JpaRepository<TransitItemsMapping, UUID> {
#Modifying
#Transactional
#Query(value="delete from TransitItemsMapping t where t.grouping_form_id=:groupingFormId",nativeQuery = true)
void deleteByGroupingFormId(#Param("groupingFormId") UUID groupingFormId);
}
Domain:TransitItemsMapping.java
#Data
#Entity
#Table(name = "TransitItemsMapping")
public class TransitItemsMapping implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
#GeneratedValue(generator = "uuid")
#Column(name = "transit_Item_id",unique = true, nullable = false)
private UUID transitItemId;
#ToString.Exclude
#JsonManagedReference
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "grouping_form_id")
//#OnDelete(action = OnDeleteAction.CASCADE)
private GroupingForm groupingForm;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(referencedColumnName = "dim_Item_ID",name = "item_id")
private Item item;
#Column(name ="item_relationship_id", insertable = false,updatable = false)
private String itemRelationshipId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "item_relationship_id",referencedColumnName = "dim_item_relationship_id")
private VendorFactoryItem vendorFactoryItem;
#Column(name = "edam_id")
private String edamId;
#Column(name = "model_number")
private String modelNumber;
#Column(name = "description")
private String description;
#Column(name = "packaging_details")
private String packagingDetails;
#Column(name = "packaging_method")
private String packagingMethod;
#Column(name = "is_side_stack")
private String isSideStack;
#Column(name = "quantity")
private Integer quantity;
#Column(name = "dimensions")
private String dimensions;
#Column(name = "product_net_weight")
private String productNetWeight;
#Column(name = "plastic_bag_ind")
private String plasticBagInd;
#Column(name = "insertion_order")
private Integer insertionOrder;
#Column(name = "comments")
private String comments;
#Column(name = "item_unique_id")
private String itemUniqueId;
#Column(name = "itm_pak_qty")
private Integer itemPackQuantity;
}
GroupingForm.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
#Entity
#Table(name = "GroupingForm")
public class GroupingForm implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
#GeneratedValue(generator = "uuid")
#Column(name = "grouping_form_id",unique = true, nullable = false)
private UUID groupingFormId;
#Column(name = "grouping_form_name")
private String groupingFormName;
#Column(name = "vid")
private String vid;
#Column(name = "vendor_name")
private String vendorName;
#Column(name = "hovbu")
private String hovbu;
#Column(name = "fid")
private String fid;
#Column(name = "factory_name")
private String factoryName;
#Column(name = "item_count")
private Integer itemCount;
#CreationTimestamp
#Column(name = "creation_date")
private Timestamp creationDate;
#Column(name = "created_by")
private String createdBy;
#UpdateTimestamp
#Column(name = "modified_date")
private Timestamp modifiedDate;
#Column(name = "modified_by")
private String modifiedBy;
#Column(name = "product_engineer")
private String productEngineer;
#Column(name = "status")
private String status;
#Column(name = "sourcing_type")
private String sourcingType;
#Column(name = "total_comments")
private Integer totalComments;
#Column(name = "factory_name_chinese")
private String factoryNameChinese;
#Column(name = "grouping_form_type")
private String groupingFormType;//to save as Product/transit/Product_transit
#Column(name = "ref_id")
private String refId;
#JsonBackReference
#OneToMany(mappedBy = "groupingForm", cascade = CascadeType.ALL)
private List<ProductItemsMapping> productItems = new ArrayList<>();
#JsonBackReference
#OneToMany(mappedBy = "groupingForm", cascade = CascadeType.ALL)
private List<TransitItemsMapping> transitItems = new ArrayList<>();
#Column(name = "pdf_status")
private String pdfStatus;
public GroupingForm(UUID groupingFormId,String groupingFormName, String vid, String vendorName, String hovbu,
String fid, String factoryName, String status, String sourcingType, Integer totalComments,
Date creationDate, String createdBy, Date modifiedDate, String modifiedBy, String productEngineer,
Integer itemCount, String groupingFormType, String refId, String factoryNameChinese) {
this.groupingFormId = groupingFormId;
this.groupingFormName = groupingFormName;
this.vid = vid;
this.vendorName = vendorName;
this.hovbu = hovbu;
this.fid = fid;
this.factoryName = factoryName;
this.status = status;
this.sourcingType = sourcingType;
this.totalComments = totalComments;
this.creationDate = creationDate!=null?new Timestamp(creationDate.getTime()):null;
this.createdBy = createdBy;
this.modifiedDate = modifiedDate!=null?new Timestamp(modifiedDate.getTime()):null;
this.modifiedBy = modifiedBy;
this.productEngineer = productEngineer;
this.itemCount = itemCount;
this.groupingFormType = groupingFormType;
this.refId = refId;
this.factoryNameChinese = factoryNameChinese;
}
}
Service: methods which already annotated with #Transactional
private void updateTransitItem(GroupingCardsDto groupingCardsDto, GroupingForm groupingForm) {
transitItemRepository.deleteByGroupingFormId(groupingCardsDto.getGroupingFormDto().getGroupingFormId());
groupingFormService.saveTransitItems(groupingCardsDto.getGroupingFormDto(), groupingForm);
}
when I am running eclipse in debug mode then my breakpoint is stuck in delete method. I am using
PostgreSQL 9.6.24 on x86_64-pc-linux-gnu, compiled by Debian clang version 12.0.1, 64-bit
version, and for pool connection Hikari-CP-3.2.0.
And If I let my debug running after long time (~45min) I am getting below error.
marked as broken because of SQLSTATE(08006), ErrorCode(0)\norg.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
Thanks in advance.
There are two possible reasons for this.
Either your delete statement actually takes a really long time, or it is stuck on a lock.
45 min, is certainly a lot for simple delete and could only be expected when you are working on huge amounts of data, like many millions of rows. Use the explain plan to validate that the expected indexes are used.
I consider locks the more likely reason for the problem. You'll need to check what locks are present and where they are coming from. This wiki page about lock monitoring in PostgreSQL seems to be a good starting point.
Entity fetched using Hibernate has all fields set to NULL. How can I avoid this?
This is my Jpa Repository.
#Repository
public interface PolicyRepository extends JpaRepository<Entity, Long>{
Entity findByEntSeqNo(#Param("entSeqNo") long entSeqNo);
}
When debugged I can see that fetched data are inside a field name $$_hibernate_interceptor
This is my entity class
#Entity
#Table(name = "ENTITY_TABLE")
#Lazy(value = false)
public class Entity extends AuditModel {
#Id
#Column(name = "ENT_SEQ_NO")
#GeneratedValue(generator="entitySeq")
#SequenceGenerator(name="entitySeq",sequenceName="\"ENT_SEQ_NO_seq\"", allocationSize=1)
private long entSeqNo;
#Column(name = "ENT_CUSTOMER_CODE")
#NotEmpty(message = "Customer Code is Required.")
private String entCustomerCode;
#Column(name = "ENT_CLAS_CODE")
#NotEmpty(message = "Class Code is Required.")
private String polcClasCode;
#Column(name = "ENT_PROD_CODE")
#NotEmpty(message = "Product Code is Required.")
private String entProdCode;
#Column(name = "ENT_BRANCH_CODE")
#NotEmpty(message = "Branch Code is Required.")
private String entBranchCode;
#Column(name = "ENT_YYY_NO")
#NotEmpty(message = "Yyy Number is Required.")
private String entYyylNo;
#Column(name = "ENT_XX_NO")
private String entXxNo;
#Column(name = "ENT_XXX_NO")
private String entXxxNo;
#Column(name = "ENT_COMMENCEMENT_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date entCommencementDate;
#Version
#NotNull(message = "Version is Required.")
#Column(name = "VERSION")
private int version;
#Column(name = "ENT_EXPIRY_DATE")
#NotNull(message = "Entity Expiry Date is Required.")
#Temporal(TemporalType.TIMESTAMP)
private Date entExpiryDate;
#Column(name = "ENT_DELIVERY_MODE")
private String entDeliveryMode;
#Column(name = "ENT_COLLECTED_BY")
private String entCollectedBy;
#Column(name = "ENT_COLLECTED_USER_NIC")
private String entCollectedUserNic;
#Column(name = "ENT_COLLECTED_USER_MOBILE")
private String entCollectedUserMobile;
#Column(name = "ENT_COLLECTED_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date entCollectedDate;
#Column(name = "ENT_POSTED_DATE")
#Temporal(TemporalType.TIMESTAMP)
private Date entPostedDate;
#Column(name = "ENT_POSTED_REF_NO")
private String entPostedRefNo;
#Column(name = "ENT_LANG_CODE")
private String entLangCode;
#Column(name = "ENT_PP1_ID", insertable = false, updatable = false)
private Long entPp1Id;
#Column(name = "ENT_STATUS")
private Short entStatus;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ENT_PP1_ID", referencedColumnName = "ID")
private Pp1 entPp1;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ENT_EPP1_ID", referencedColumnName = "ID")
private Epp1 entEpp1;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xxx")
private Collection<TaskLog> taskLogColletion;
}
I'm asking this here since I couldn't find the exact cause for this behavior. All the Jpa operations are working fine and this happens only when I try to pick the details from the database. I also tried getOne method and the same problem occurs.
You do not need the method : Entity findByEntSeqNo(#Param("entSeqNo") long entSeqNo); in your jpa interface because entSeqNo is an id of your entity.
So whene you want to get one Entity by id you can just call policyRepository.getOne(the value of entSeqNo) provided by JpaRepository
Or you can use policyRepository.findById(the value of entSeqNo) provided by CrudRepository whitch is basically a superinterface of JpaRepository
Ps: Do no overwrite these methods in you interface.
This is the first entity:
#Entity
#Table(name = "photographers")
public class Photographer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#Column(name = "first_name")
private String firstName;
#NotNull
#Size(min = 2, max = 50)
#Column(name = "last_name")
private String lastName;
#Pattern(regexp = "^\\+[0-9]{1,3}/[0-9]{8,10}$")
private String phone;
#NotNull
#OneToOne(optional = false)
#JoinColumn(name = "primary_camera_id", referencedColumnName = "id")
private BasicCamera primaryCamera;
#NotNull
#OneToOne(optional = false)
#JoinColumn(name = "secondary_camera_id", referencedColumnName = "id")
private BasicCamera secondaryCamera;
#OneToMany(mappedBy = "owner")
private Set<Lens> lenses;
#OneToMany(mappedBy = "owner")
private Set<Accessory> accessories;
and this is the other(lenses) :
#Entity
#Table(name = "lenses")
public class Lens {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String make;
#Column(name = "focal_length")
private Integer focalLength;
#Column(name = "max_aperture", precision = 1)
private Double maxAperture;
#Column(name = "compatible_with")
private String compatibleWith;
#ManyToOne
#JoinColumn(name = "owner_id", referencedColumnName = "id")
private Photographer owner;
Firstly i have lenses without owners. Then i want to create a photographer with everything and set its lenses to exiting ones i do this with this code:
for (Lens lens : lensSet) {
lens.setOwner(photographer);
}
photographer.setPrimaryCamera(primaryCamera);
photographer.setSecondaryCamera(secondaryCamera);
photographer.setLenses(lensSet);
and when i save the photographer i want the lenses owner to be set to the created one. I tried cascad.MERGE on the OneToMany realation in the photographer entity but did't work and in the database all lenses owners are still nulls
I figured it out : i need to save the lenses AFTER i save the photographer:
for (Lens lens : lensSet) {
lens.setOwner(photographer);
}
photographer.setPrimaryCamera(primaryCamera);
photographer.setSecondaryCamera(secondaryCamera);
photographer.setLenses(lensSet);
this.photographerRepository.save(photographer);
this.lensRepository.save(lensSet);
I have the following sql query:
select * from wash_history w where w.wash_id in
(select wash.id from wash where wash.car_wash_id = 17)
and w.time between '2017-07-13' and '2017-07-22'
And I want to create this query inside interface that extends CrudRepository. The method is
Page<WashHistory> findWashHistoryByTimeBetweenOrderByTimeDesc(Date dateFrom,
Date dateTo,
Pageable pageable);
But this method always returns an empty list, I think is it due to Java date and SQL TimeStamp? If you want some more details I can add them to question. My class:
public class WashHistory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
// #Max(value=?) #Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
#Column(name = "wash_price")
private Double washPrice;
#Size(max = 2147483647)
#Column(name = "car_wash_name")
private String carWashName;
#Basic(optional = false)
#NotNull
#Column(name = "time")
#Temporal(TemporalType.TIMESTAMP)
private Date time;
#Size(max = 2147483647)
#Column(name = "status")
private String status;
#Column(name = "bonuses")
private Integer bonuses=0;
#JoinColumn(name = "wash_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Wash washId;}
ANd wash class
public class Wash implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Size(max = 2147483647)
#Column(name = "status")
private String status;
#Column(name = "using_bonuses")
private Boolean usingBonuses=false;
#Basic(optional = false)
#NotNull
#Column(name = "last_status_time")
#Temporal(TemporalType.TIMESTAMP)
private Date lastStatusTime;
#OneToMany(mappedBy = "washId", fetch = FetchType.LAZY)
private Set<WashHistory> washHistorySet;
#OneToOne(mappedBy = "washId", fetch = FetchType.LAZY)
private WashComment washCommentSet;
#JoinColumn(name = "car_wash_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private CarWash carWashId;}
My pageble
public class LimitAndOffsetPageable extends PageRequest {
private int page;
private int size;
public LimitAndOffsetPageable(int page, int size){
super(page,size);
this.page=page;
this.size=size;
}
#Override
public int getOffset(){
return this.page;
}
}
I pass to findWashHistoryByTimeBetweenOrderByTimeDesc(new LimitAndOffsetPageable (0,100)) (offset = 0 , limit = 100)
Your problem is caused by using bad naming conventions: Your entities should look like following: (removed redundant lines - if you want to add constraints like not null, add them in #Column)
#Entity
public class WashHistory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "wash_price")
private Double washPrice;
#Column(name = "car_wash_name")
private String carWashName;
#Column(name = "time")
#Temporal(TemporalType.TIMESTAMP)
private Date time;
private String status;
private Integer bonuses=0;
#JoinColumn(name = "wash_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Wash wash;
}
Now, the method could be:
Page<WashHistory> findAllByWashIdIdAndTimeBetween(Integer id, Date start, Date end, Pageable pageable)
Sort property should be put into pageable object.