java.sql.SQLException: Field 'id' doesn't have a default value with Many to Many relation - spring

I am trying to do Many to many relation. I have UserEntity and NoteEntity.
Note can be shared to many users, and user can have many notes from other users.
public class NoteEntity {
#Id
#Column(name = "id_note", nullable = false)
private String id;
#ManyToMany(mappedBy = "sharedToUserNotes")
private Set<UserEntity> receivers = new HashSet<>();
}
public class UserEntity implements UserDetails {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
#Column(name = "id_user")
private String id;
#ManyToMany(
cascade = {
CascadeType.MERGE, CascadeType.PERSIST
})
#JoinTable(name = "shared_notes",
joinColumns = { #JoinColumn(name = "id_user") },
inverseJoinColumns = { #JoinColumn(name = "id_note") })
private Set<NoteEntity> sharedToUserNotes = new HashSet<>();
and when I am trying add note to set of notes and next save it:
public void addNoteToSharedToUserNotes(ShareForm shareForm) throws ValueNotFoundException{
NoteEntity note = noteService.getNoteById(shareForm.getIdNote());
UserEntity user = userService.getUserByLogin(shareForm.getUserLogin());
user.getSharedToUserNotes().add(note);
userEntityRepository.saveAndFlush(user);
}
. I'm getting error
java.sql.SQLException: Field 'id' doesn't have a default value
I think it is about additional table "shared_notes", becouse it has columns like: id, id_note, id_user and I can not find how to set value of that id.

Related

Spring data rest ManyToMany mapping PUT/update operation is not replacing the nested object

I started to learn spring data rest. I'm doing PUT operation and it's not working for the nested objects for ManyToMany relationship, whereas it works fine for OneToMany relation.
Entities structures:
#Table(name="CONFIG_DTLS",schema = "app_txn")
#Entity
public class Config {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "NAME", nullable = false, length = 75)
private String name;
/*Unable to replace the data in the MBR_CONFIG_MAPPING table in the put operation.
When the control comes to #HandleBeforeSave annotated method in PUT operation,
the request data contains the existing Member info instead of the one which i'm passing in the PUT request body */
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},fetch = FetchType.EAGER)
#JoinTable(schema = "app_txn", name = "MBR_CONFIG_MAPPING",
joinColumns ={#JoinColumn(name="CONFIG_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name="MBR_ID",referencedColumnName = "ID")}
)
private Set<Member> members;
//able to replace the notifications completely in PUT operation
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "CONFIG_ID",referencedColumnName = "ID")
private Set<Notification> notifications;
}
Member.java
#Table(name="MBR_DTLS",schema = "app_txn")
#Entity
public class Member {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name = "OTHER_MBR_DATA", updatable = false)
private String otherMbrData;
}
Notification.java
#Table(name="NOTIFICATIONS",schema = "app_txn")
#Entity
public class Notification {
#Id
#GenericGenerator(name = "UUIDGenerator", strategy = "uuid2")
#GeneratedValue(generator = "UUIDGenerator")
#Column(name = "ID", updatable = false, nullable = false)
private UUID id;
#Column(name="LEVEL")
private String level;
#Column(name="EMAIL")
private String email;
}
Interfaces:
#RepositoryRestResource(collectionResourceRel = "configs", path="configs")
public interface ConfigRepo extends PagingAndSortingRepository<Config,UUID> {
}
#RepositoryRestResource(exported=false) // don't want to users to manipulate it directly.
public interface MemberRepo extends PagingAndSortingRepository<Member,Object> {
}
Here I don't want to add or modify anything in the MBR_DTLS table as it is loaded by another backend process. I want to update only the mapping details MBR_CONFIG_MAPPING table whenever user does the PUT/update operation. POST/create operation is working fine. Please share your thoughts on how to fix this and if you have any questions add it in the comment section.
PS: I referred some links online but that does not help much - Spring Data REST - PUT request does not work properly since v.2.5.7

Spring boot domain class required for mapping table

I m new to Spring Boot. I have a table (Team) that has resources, am storing in a separate table (Resources) and have team_resource mapping table (with fields teamid, resourceid). My question is should I have a domain class for the mapping_table too ?
When I m inserting a new team (POST) with resources I create entry in all 3 tables. I m using facade/dao pattern for writing/ reading to the DB. I have to handle when the team is modified/ deleted. Should I have a domain class for the mapping_table?
There are multiple approaches you can handle it
Approach 1
Define #ManyToMany between your Team and Resources entity like this:
In Team Entity
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "resources",
joinColumns = { #JoinColumn(name = "id") },
inverseJoinColumns = { #JoinColumn(name = "id") })
private Set<Resources> resources= new HashSet<>();
In your resources entity:
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "resources")
private Set<Team> teams= new HashSet<>();
Approach 2
#Entity
#Table(name = "team_resources")
public class TeamResources implements Serializable {
#EmbeddedId
private TeamResourcesId id;
#ManyToOne
#JoinColumn(name = "team_id", referencedColumnName = "id", insertable = false, updatable = false)
private Team team;
#ManyToOne
#JoinColumn(name = "resources_id", referencedColumnName = "id", insertable = false, updatable = false)
private Resources resources;
public TeamResources (Team u, Resources r) {
// create primary key
this.id = new TeamResourcesId (u.getUserId(), q.getQuestionId());
// initialize attributes
this.user = u;
this.question = q;
}
#Embeddable
public static class TeamResourcesId implements Serializable {
#Column(name = "team_id")
protected Long teamId;
#Column(name = "resources_id")
protected Long resourcesId;
public TeamResourcesId () {
}
public TeamResourcesId (Long teamId, Long resourcesId) {
this.teamId= teamId;
this.resourcesId= resourcesId;
}
//Getter , setter. equal and hash
}
so to answer your question, follow second approach and its good to not define bidirectional approach as it can lead to some run time problem if not handled properly.

Delete Operation on Embedded Object Spring JPA

I Have below Entities :
#Entity(name = "USRGRP_MAP")
public class UserGroupMapping {
#Id
#Column(name = "USRGRP_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GRP_MAP_SEQ")
#SequenceGenerator(sequenceName = "usrgrp_map_seq",allocationSize = 1,name = "USER_GRP_MAP_SEQ")
private Long mappingId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")
private User user;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "USR_GRP_ID", referencedColumnName = "USR_GRP_ID")
private UserGroup group;
#Column(name = "USR_USRGRP_ACT")
private String userGroupAct;
getter/setters
}
#Entity(name = "USER")
public class User {
#Id
#Column(name = "USER_ID")
private Long userId;
#Column(name = "LOGIN_ID")
private String userName;
getter/setters
}
#Entity(name = "USR_GRP")
public class UserGroup {
#Id
#Column(name = "USR_GRP_ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_GRP_SEQ")
#SequenceGenerator(sequenceName = "usr_grp_seq",allocationSize = 1,name = "USER_GRP_SEQ")
private long groupId;
#Column(name = "GRP_NM")
private String groupName;
#Column(name = "GRP_DESC")
private String groupDesc;
getter/setters
}
UserGroupMapping contains has many to one relationship with both user and group.
Now I want to do CRUD operation on UserGroupMapping for that I have created repository as below:
public interface UserGroupMappingRepository extends JpaRepository<UserGroupMapping, Long> {
List<UserGroupMapping> findByGroup(UserGroup group);
List<UserGroupMapping> findByUser(User user);
}
Now I want to write delete operation(for particular user and group) on UserGroupMapping without deleting any entry in USER and USR_GRP table , Just need to remove entry from USRGRP_MAP table.
I am trying to achieve it using native query:
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
Facing Exception Invalid SQL grammar, although query work fine in sql developer.
Below is my service class:
#Service
public class UserGroupMappingServiceImpl implements UserGroupMappingService{
#Autowired
private UserGroupMappingRepository repository;
#Override
public void deleteUserGroupMapping(Long userId, Long groupId) {
repository.deleteUserGroupMappingByUserAndGroup(userId,groupId);
}
}
Could anyone suggest correct way to delete entry from UserGroupMapping without deleting user and group ?
Below is USRGRP_MAP table:
USRGRP_ID USER_ID USR_USRGRP_ID USR_USRGRP_ACT
------------- ---------- ------------- -
41 306106 41 Y
14 108527 14 Y
8 295597 8 N
10 296518 10 Y
11 295597 11 Y
Thanks in advance .
Try to change
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
To this:
#Modifying
#Query(value = "delete from USR_USRGRP_MAP where user_id = :userId and usr_grp_id = :groupId",nativeQuery = true)
void deleteUserGroupMappingByUserAndGroup(#Param("userId") Long userId, #Param("groupId") Long groupId);
Cheers
~Emil

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!

how to delete or save a many to many relationship in hibernate & spring

i have a many to many relationship between 2 tables.
below are the two tables with mappings.
StaffSearchCriteria is used to search staffs having skills selected.
this search criteria is persisted in DB so that we can again lookup it later.
the issue i am facing is that i am not able to properly save this data.
i am not understanding the "cascade" part of the mapping.
due to which, if i do " Cascade.ALL ", the data is saved properly, but when i delete the search criteria, then it also deletes the Skill entries associated with it, which is wrong.
i just want that if i delete Skill, StaffSearchCriteria entry should not get deleted and similarly for the Skill;
Only the selected data should be deleted and its entry in the mapping table.
the other table should not be affected by that action.
StaffSearchCriteria
#Entity
#Table(name = "staff_search_criteria")
#NamedQueries({
#NamedQuery(name = "StaffSearchCriteria.findAll", query = "SELECT s FROM StaffSearchCriteria s")})
public class StaffSearchCriteria implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#Basic(optional = false)
#NotNull
#Column(name = "version")
private long version;
#Lob
#Size(max = 2147483647)
#Column(name = "description")
private String description;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 200)
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "staffSearchCriteriaCollection", cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
private Collection<Skill> skillCollection;
==================================================
Skill
#Entity
#Table(name = "skill")
#NamedQueries({
#NamedQuery(name = "Skill.findAll", query = "SELECT s FROM Skill s")})
public class Skill implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#Basic(optional = false)
#NotNull
#Column(name = "version")
private long version;
#Lob
#Size(max = 2147483647)
#Column(name = "description")
private String description;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "name")
private String name;
#JoinTable(name = "mission_skill", joinColumns = {
#JoinColumn(name = "skill_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "mission_skills_id", referencedColumnName = "id")})
#ManyToMany(fetch = FetchType.LAZY)
private Collection<Mission> missionCollection;
#JoinTable(name = "staff_search_criteria_skill", joinColumns = {
#JoinColumn(name = "skill_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "staff_search_criteria_skills_id", referencedColumnName = "id")})
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.LAZY)
private Collection<StaffSearchCriteria> staffSearchCriteriaCollection;
Save method
public StaffSearchCriteria saveStaffSearchCriteria(StaffSearchCriteria staffSearchCriteria) {
logger.info(" [StaffSearchCriteriaDAOImpl] saveStaffSearchCriteria method called. - staffSearchCriteria = " + staffSearchCriteria);
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(staffSearchCriteria);
return staffSearchCriteria;
}
delete method
public void deleteStaffSearchCriteria(Long id) {
logger.info(" [StaffSearchCriteriaDAOImpl] deleteStaffSearchCriteria method called. - id = " + id);
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("FROM StaffSearchCriteria ssc where ssc.id = " + id);
if(null != query.uniqueResult()){
StaffSearchCriteria staffSearchCriteria = (StaffSearchCriteria)query.uniqueResult();
session.delete(staffSearchCriteria);
}
}
Please help me here.What am i doing wrong?
Finally i solved it. what i did was as follows.
1. In controller, i found out which skills were removed from previous saved data.
2. passed that list of Skill as well as the StaffSearchCriteria to the service save method.
3. in Service, i iterated over each skill to be removed and removed the staffSearchCriteria object from it and saved it.
4. then passed the staff search criteria to dao and used saveOrUpdate method.
Below are the code snippets.
1.Controller
List<Skill> skillList2 = new ArrayList<Skill>();
if(null != request.getParameterValues("skillCollection")){
for(String skillId : request.getParameterValues("skillCollection")){
if((!skillId.equals(null)) && skillId.length() > 0){
Skill skill = skillService.findSkillById(Long.parseLong(skillId));
// skill will be lazily initialized :(
// initialize it
skill.setStaffSearchCriteriaCollection(staffSearchCriteriaService.getAllStaffSearchCriteriaBySkillId(skill.getId()));
// set staff search criteria in each skill. because it is the owner
if(null != skill.getStaffSearchCriteriaCollection()){
skill.getStaffSearchCriteriaCollection().add(staffSearchCriteria);
}else{
List<StaffSearchCriteria> staffSearchCriteriaList = new ArrayList<StaffSearchCriteria>();
staffSearchCriteriaList.add(staffSearchCriteria);
skill.setStaffSearchCriteriaCollection(staffSearchCriteriaList);
}
skillList2.add(skill);
}
}
}
staffSearchCriteria.setSkillCollection(skillList2);
// Remove OLD skills also. plz. :)
List<Skill> skillList3 = null;
if(null != staffSearchCriteria && staffSearchCriteria.getId() != null && staffSearchCriteria.getId() > 0){
// this skillList3 will contain only those which are removed.
skillList3 = skillService.getAllSkillByStaffSearchCriteriaId(staffSearchCriteria.getId());
skillList3.removeAll(skillList2);
}
// now set staffSearchCriteriacollection and then pass it.
List<Skill> removedskillList = new ArrayList<Skill>();
if(null != skillList3){
for(Skill skill : skillList3){
skill.setStaffSearchCriteriaCollection(staffSearchCriteriaService.getAllStaffSearchCriteriaBySkillId(skill.getId()));
removedskillList.add(skill);
}
}
// now pass to service and save these skills after removing this staff search criteria from them.
staffSearchCriteria = staffSearchCriteriaService.saveStaffSearchCriteria(staffSearchCriteria, removedskillList);
2.Service
if(null != removedskillList && removedskillList.size() > 0){
for(Skill skill : removedskillList){
skill.getStaffSearchCriteriaCollection().remove(staffSearchCriteria);
skillDAO.saveSkill(skill);
}
}
return staffSearchCriteriaDAO.saveStaffSearchCriteria(staffSearchCriteria);
3.DAO
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(staffSearchCriteria);
4.Entity Class - Skill
#JoinTable(name = "staff_search_criteria_skill", joinColumns = {
#JoinColumn(name = "skill_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "staff_search_criteria_skills_id", referencedColumnName = "id")})
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
private Collection<StaffSearchCriteria> staffSearchCriteriaCollection = new ArrayList<StaffSearchCriteria>();
5.Entity Class - StaffSearchCriteria
#ManyToMany(mappedBy = "staffSearchCriteriaCollection", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
private Collection<Skill> skillCollection = new ArrayList<Skill>();
Hope this helps.

Resources