I have the following entity and I'm trying to use ExampleMatcher for simple queries:
#Entity(name="UserAccount")
#Table(name = "useraccount", catalog = "useraccount")
#Data
public class UserAccount implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String username;
#Column(nullable = false, unique = true)
private String mail;
private String password;
private boolean isEnabled;
private Timestamp credentialExpire;
private boolean isAccountNonLocked;
private boolean isSuspended;
private Timestamp accountExpire;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_privileges", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "privilege_id", referencedColumnName = "id", nullable = false)})
private Set<Privilege> privileges= new HashSet<>();
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#JoinTable(name = "user_to_organizations", catalog = "useraccount",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)},
inverseJoinColumns = {#JoinColumn(name = "organization_id", referencedColumnName = "id", nullable = false)})
private Set<Organization> organizations= new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
#Setter(AccessLevel.NONE)
private UserRegister register;
#OneToMany(mappedBy ="tokenId.user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
private Set<SecureToken> tokens = new HashSet<>();
#OneToOne(mappedBy ="user", cascade = {CascadeType.REMOVE, CascadeType.MERGE, CascadeType.REFRESH}, fetch=FetchType.LAZY)
#PrimaryKeyJoinColumn
private UserLogin login;
//all the methods omitted from brevity
}
I create the Example matcher as follows:
UserAccount account= new UserAccount();
account.setUsername("John");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example<UserAccount> regExample = Example.of(account, matcher);
List<UserAccount> out = repository.findAll(regExample);
Consider that a user with username "John" exists, but the output is always empty, no matter what parameter I fill.
Edit: this helps to find the solution: Are there any possible ways to ignore all paths of JPA Example Matcher. There is no way to automatically ignore primitive fields when not used?
Edit: Notice that I want to find all the UserAccount containing the specified strings in the selected fields. With other entities the configuration of ExampleMatcher works.
Related
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.
Anyone have any ideas on how I could do postmapping for the Many-to-Many relationship? Getting the data works, but this is what I'm having trouble with
I tried using the "guide" but unfortunately I don't understand it very well yet
Here is my entities:
Album
#Entity
public class Album implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long id;
String name;
String artist;
String cover;
#ManyToMany(fetch = FetchType.LAZY, cascade =
{
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
})
#JoinTable( name = "user_albums",
joinColumns = #JoinColumn(name = "album_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "user_id", nullable = false)
)
#JsonBackReference
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<User> users = new HashSet<>();
User:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 20)
private String username;
#NotBlank
#Size(max = 50)
#Email
private String email;
#NotBlank
#Size(max = 120)
private String password;
#ManyToMany(fetch = FetchType.LAZY, cascade =
{
CascadeType.DETACH,
CascadeType.MERGE,
CascadeType.REFRESH,
CascadeType.PERSIST
})
#JoinTable( name = "user_albums",
joinColumns = #JoinColumn(name = "user_id", nullable = false),
inverseJoinColumns = #JoinColumn(name = "album_id", nullable = false)
)
#OnDelete(action = OnDeleteAction.CASCADE)
private Set<Album> albums = new HashSet<>();
I tried it this way but it didn't work
#PostMapping("/users/mal/{userId}/album")
public ResponseEntity<Album> addAlbum(#PathVariable(value = "userId") Long userId, #RequestBody Album albumRequest, User userRequest) {
Album newMal = userRepo.findById(userId).map(user -> {
long albumId = userRequest.getId();
user.addAlbum(albumRequest);
return albumRepo.save(albumRequest);
}).orElseThrow(() -> new RuntimeException("Not found USER with id = " + userId));
return new ResponseEntity<>(newMal, HttpStatus.CREATED);
I am implementing many to many relationship between Users and Permissions and I cannot conclude where am I wrong, it is not working (Unable to map collection rs.raf.demo.model.User.permissions):
#Data
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column
private String username;
#Column
private String password;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID")
)
private List<Permission> permissions = new ArrayList<>();
}
=================================================================================
#Data
#Entity
public class Permission {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "permission_id")
private Long userId;
private String type;
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<User> users = new ArrayList<>();
}
I implemented Users and Permissions with looking in this example, this is working (and I cannot see difference with Users and Permission):
#Data
#Entity
#Table(name = "STUD")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String lastName;
private String firstName;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID")
)
private List<Course> courses = new ArrayList<>();
}
================================================================================
#Data
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
#ManyToMany
#JoinTable(
name = "STUDENTS_COURSES",
joinColumns = #JoinColumn(name = "COURSE_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID")
)
#JsonIgnore
private List<Student> students = new ArrayList<>();
public void addStudent(Student student) {
students.add(student);
student.getCourses().add(this);
}
public void removeStudent(Student student) {
students.remove(student);
student.getCourses().remove(this);
}
}
JPA uses (and creates in your case probably) intermediate join table for many to many relationship and you cannot use different reference column name from name you specified in entity.
So you need to set:
in User class
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID")
)
in Permission
#ManyToMany
#JoinTable(
name = "USERS_PERMISSIONS",
joinColumns = #JoinColumn(name = "PERMISSION_ID", referencedColumnName = "PERMISSION_ID"),
inverseJoinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
)
And same for other tables.
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!
I have an entity "Employee" and a #Embeddable class RecycleBin. Now whenever a user deletes anything in the system the the entity class record is duplicated. I know it is because of the cascadeType.Merge i added in the RecycleBin class but if I remove cascadeType.Merge then the system crashes on delete operation giving a server error of "save transient before flushing". I have been stuck in this since two days and i am new to spring. If someone could please help me.
This is my Employee class.
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Long version;
private String fullName;
private String fatherName;
private String identityCardNo;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String placeOfBirth;
private String nationality;
private String email;
private String phoneNumber;
#Column(columnDefinition = "TEXT")
private String permanentResAddr;
#Column(columnDefinition = "TEXT")
private String presentResAddr;
#Enumerated(EnumType.STRING)
private MaritalStatus martialStatus;
#Enumerated(EnumType.STRING)
private Gender gender;
private String spouseName;
private String spouseOccupation;
private String spouseOrganization;
private String spouseDesignation;
private String numOfDependents;
private String languagesKnown;
private String salary;
private String bonus;
private String allowance;
private String stipend;
#ElementCollection(fetch = FetchType.EAGER)
#Column(name = "jobrole")
#Enumerated(EnumType.STRING)
private List<JobRole> jobRoles;
#Enumerated(EnumType.STRING)
private JobType jobType;
#ElementCollection(targetClass = Privilege.class, fetch = FetchType.LAZY)
#Column(name = "privilege")
#Enumerated(EnumType.STRING)
private List<Privilege> privileges;
// dep auto del
#OneToMany(mappedBy = "substituteTeacher")
private List<TimetableSubstitute> timetableSubstitutes;
// dep manul del
#OneToMany(mappedBy = "createdBy")
private List<SchemeOfWork> createdSchemeOfWorks;
// dep association del
#OneToMany(mappedBy = "createdBy")
private List<CommentBank> createdComments;
// dep manul del
#OneToMany(mappedBy = "teacher")
private List<BatchSubject> teacherSubjects;
// dep auto del
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "employee_campus", joinColumns = { #JoinColumn(name = "employee", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "branch", referencedColumnName = "id") })
private List<Campus> schoolCampuses;
// dep auto del
#OneToMany(mappedBy = "employee")
private List<CampusEmployeeAttendance> employeeBranchAttendanceList;
// dep auto del
#OneToMany(mappedBy = "employee")
private List<EmployeeAttendance> employeeDayAttendanceList;
// dep association del
#OneToMany(mappedBy = "createdByTeacher")
private List<ProfileForm> teacherCreateProfileForms;
// dep auto del
#ManyToMany(mappedBy = "employees")
private List<Announcement> announcements;
// dep auto del
#OneToMany(mappedBy = "employee")
private List<Notification> notification;
// dep auto del
#OneToOne
private UserLoginAccount employeeLoginAccount;
// dep manul del
#OneToMany(mappedBy = "batchTeacher")
private List<Batch> teacherBatches;
// dep manul del
#OneToOne(mappedBy = "schoolEmployee")
private Parent parent;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "employee_qualifications", joinColumns = #JoinColumn(name = "emp", referencedColumnName = "id"))
private List<Qualification> qualifications;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "employee_pre_employment", joinColumns = #JoinColumn(name = "emp", referencedColumnName = "id"))
private List<PreEmployment> preEmployments;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "employee_memberships", joinColumns = #JoinColumn(name = "emp", referencedColumnName = "id"))
private List<EmpMembership> memberships;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "employee_certificates", joinColumns = #JoinColumn(name = "emp", referencedColumnName = "id"))
private List<EmpCertificate> certificates;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "employee_references", joinColumns = #JoinColumn(name = "emp", referencedColumnName = "id"))
private List<EmpReference> references;
// dep auto del
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY)
private List<FileLibrary> empImages;
#Embedded
private RecycleBin recycleBin;
And this is my RecycleBin class.
#Embeddable
public class RecycleBin {
#Temporal(TemporalType.TIMESTAMP)
private Date deleteDate;
#ManyToOne(cascade= CascadeType.MERGE)
private Employee deletedBy;
#Enumerated(EnumType.STRING)
private RecycleBinStatus recycleBinStatus;