MapStruct DTO->Entity mapping with OneToMany as FK in DTO - java

Hi I'm pretty new to Java and Spring Boot. I have an API which uses 3 entities:
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(updatable = false, nullable = false)
int id;
// Other fields
#Column(nullable = false)
String accountId;
#OneToMany(mappedBy = "account", cascade = CascadeType.ALL)
#JsonBackReference
List<StockMarketTransaction> transactions;
#ManyToMany
#JsonBackReference
Set<StockMarketSubject> currentlyOwnedSubjects;
public int getId() {
return id;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public List<StockMarketTransaction> getTransactions() {
return transactions;
}
public void setTransactions(List<StockMarketTransaction> transactions) {
this.transactions = transactions;
}
public void addTransaction(StockMarketTransaction transaction){
this.transactions.add(transaction);
}
public Set<StockMarketSubject> getCurrentlyOwnedSubjects() {
return currentlyOwnedSubjects;
}
public void setCurrentlyOwnedSubjects(Set<StockMarketSubject> currentlyOwnedSubjects) {
this.currentlyOwnedSubjects = currentlyOwnedSubjects;
}
}
#Entity
public class StockMarketSubject{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
// Other fields
#OneToMany(mappedBy = "subject")
#JsonBackReference
List<StockMarketTransaction> transactions;
#ManyToMany
#JsonBackReference
Set<Account> holdingAccounts;
public int getId() {
return id;
}
public List<StockMarketTransaction> getTransactions() {
return transactions;
}
public void setTransactions(List<StockMarketTransaction> transactions) {
this.transactions = transactions;
}
public void addTransaction(StockMarketTransaction transaction){
this.transactions.add(transaction);
}
public Set<Account> getHoldingAccounts() {
return holdingAccounts;
}
public void setHoldingAccounts(Set<Account> holdingAccounts) {
this.holdingAccounts = holdingAccounts;
}
}
#Entity
public class StockMarketTransaction{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
// Other fields
#ManyToOne
#JsonManagedReference
StockMarketSubject subject;
#ManyToOne(cascade = CascadeType.ALL)
#JsonManagedReference
Account account;
public Long getId() {
return id;
}
public StockMarketSubject getSubject() {
return subject;
}
public void setSubject(StockMarketSubject subject) {
this.subject = subject;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
I'm also using DTO to transfer data between service layer (fetch/create/update) and controllers. GET endpoints return DTO structure with data and POST endpoints use DTO as #RequestBody in order to capture user data sent via request.
For Transaction entity I use this DTO with MapStruct Mapper:
public class StockMarketTransactionDTO {
#Schema(accessMode = Schema.AccessMode.READ_ONLY)
Long id;
// Other fields
#NotEmpty
#PositiveOrZero
int subjectId;
#NotEmpty
#PositiveOrZero
int accountId;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getSubjectId() {
return subjectId;
}
public void setSubjectId(int subjectId) {
this.subjectId = subjectId;
}
public int getAccountId() {
return accountId;
}
public void setAccountId(int accountId) {
this.accountId = accountId;
}
public StockMarketTransactionDTO(){}
}
#Mapper
public interface StockMarketTransactionMapper {
StockMarketTransactionMapper INSTANCE = Mappers.getMapper(StockMarketTransactionMapper.class);
#Mapping(source = "subject.id", target = "subjectId")
#Mapping(source = "account.id", target = "accountId")
StockMarketTransactionDTO stockMarketTransactionToStockMarketTransactionDTO(StockMarketTransaction transaction);
#InheritInverseConfiguration
StockMarketTransaction stockMarketTransactionDTOToStockMarketTransaction(StockMarketTransactionDTO dto);
}
Insted of exchanging whole objects related to transaction I want to only attach their ID (PK) to DTO. Now when I use this mapper to create transaction DTO from entity and return it with response it works perfectly fine. It gives users a valid ID of related subject and account. But the issue I have is creating entity from DTO filled with user data. With this payload:
{
"transactionDate": "2022-12-13T17:15:47.023Z",
"pricePerOne": 20,
"volume": 20,
"operationType": "BUY",
"description": {
"advantages": "string",
"disadvantages": "string",
"reason": "string"
}, // Other fields
"subjectId": 1,
"accountId": 1
}
I get internal error:
org.hibernate.PropertyValueException: not-null property references a null or transient value : com.rcbg.afku.investmentdiary.brokeraccounts.entities.Account.accountId
I tried find any efficient and proper way to map object like this with MapStruct but I failed.
Is there any possibility to handle this with #Mapping annotation, or should I create my own implementation of mapping method?

Related

Spring- Postman too many information with ManyToMany

I have a problem with my Spring application- am practicing ManyToMany Relationship.
I made two Entity- student, and groups for them- everything is good but when am trying to display them in postman i have spaghetti result like :
{"id":4,"lastName":"guzik","groups":[{"id":1,"groupName":"grupka","students":
....(shorter version, its about 1000 lines)
[{"id":1,"lastName":"smith","groups":[{}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}{"timestamp":"2022-04-25T08:25:31.387+00:00","status":200,"error":"OK","path":"/api/student/4"}
What am doing wrong ?
enter code here
#RestController
#RequestMapping("/api")
public class MainController{
#Autowired
GroupRepo groupRepo;
#Autowired
StudentRepo studentRepo;
#GetMapping("/student/{id}")
ResponseEntity<?> getStudent(#PathVariable long id){
Optional<Students> student=studentRepo.findById(id);
return ResponseEntity.ok().body(student);
}
#GetMapping("/group/{id}")
ResponseEntity<?> getGroup(#PathVariable long id){
Optional<Groupssss> group=groupRepo.findById(id);
return ResponseEntity.ok().body(group);
}
#GetMapping("/student/{id}/groups")
ResponseEntity<?> studentGroups(#PathVariable long id){
Students student=studentRepo.findById(id).orElseThrow(()-> new UsernameNotFoundException("student not found"));
return ResponseEntity.ok().body(student.getGroups());
}
#PostMapping("/add")
#Transactional
ResponseEntity<?> addStudentToGroup(#RequestHeader long id) throws Exception{
Students student=studentRepo.findById(id)
.orElseThrow(()-> new UsernameNotFoundException("student not found"));
Groupssss group=groupRepo.findByGroupName("grupka").orElseThrow(Exception::new);
student.addGroup(group);
studentRepo.save(student);
return ResponseEntity.ok().build();
}
#Entity
public class Groupssss {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String groupName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "groups")
private Set<Students> students=new HashSet<>();
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setStudents(Set<Students> students) {
this.students = students;
}
public Set<Students> getStudents() {
return students;
}
public String getGroupName() {
return groupName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Entity
#Table(name="students")
public class Students {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String firstName;
String lastName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name="student_group",
joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="group_id"))
Set<Groupssss> groups=new HashSet<>();
public Students(){}
public Students(String firstName, String lastName){
this.firstName=firstName;
this.lastName=lastName;
}
public void addGroup(Groupssss group){
this.groups.add(group);
group.getStudents().add(this);
}
public Set<Groupssss> getGroups() {
return groups;
}
public String getLastName() {
return lastName;
}
public String getName() {
return firstName;
}
public void setGroups(Set<Groupssss> groups) {
this.groups = groups;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setName(String firstName) {
this.firstName = firstName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
If you use ManyToMany relationship, It calls the Parent and the Child entity recursively when you try to get List of it. There must be ignorance in child entity like:
In Parent entity you could call #JsonManagedReference
In Child entity you could call #JsonBackReference
Documentation is here:
Bidirectional Relationships
public class User {
public int id;
public String name;
#JsonManagedReference
public List<Item> userItems;
}
public class Item {
public int id;
public String itemName;
#JsonBackReference
public User owner;
}
Or you can use #JsonIgnore on top of the Parent declaration in Child entity. Some of the case it gets worst when you use JsonIgnore.
Documentation is here:
Jackson Ignore Properties
public class MyDto {
private String stringValue;
#JsonIgnore
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
Or use #JsonIgnoreProperties when you need to ignore many fields
#JsonIgnoreProperties(value = { "intValue" })
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
!Enjoy

Error in composite key in Hibernate org.hibernate.MappingException: Could not determine type for: java.util.Set,

Friends I have stuck in a Problem in Hibernate composite key.
Error : org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: IDP_UserManagerMapping, for columns: [org.hibernate.mapping.Column(idps)].I have Tried wit #transient and other things but not effective Please Help.
public class IDPUserManagerMapping implements Serializable {
private Long id;
private Users user;
private Users manager;
private Users assessor;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="user_id" ,referencedColumnName = "user_id"),
#JoinColumn(name="manager_id" ,referencedColumnName = "manager_id")
,#JoinColumn(name="assessor_id" ,referencedColumnName = "assessor_id")
})
#Cascade(CascadeType.ALL)
private Set<IDPMaster> idps = null;
#Id
#SequenceGenerator(sequenceName="idp_usermanagermapping_id_seq",name="idp_usermanagermapping_id_seq_gen")
#GeneratedValue(generator="idp_usermanagermapping_id_seq_gen")
#Column(name="id",nullable=false,unique=true,updatable=false)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<IDPMaster> getIdps() {
return idps;
}
public void setIdps(Set<IDPMaster> idps) {
this.idps = idps;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id",nullable=false)
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="manager_id",nullable=false)
public Users getManager() {
return manager;
}
public void setManager(Users manager) {
this.manager = manager;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="assessor_id")
public Users getAssessor() {
return assessor;
}
public void setAssessor(Users assessor) {
this.assessor = assessor;
}
}
Second Entity
public class IDPMaster implements Serializable {
private Long id;
private Users managerUser;
private Users assessorUser;
private Users idpUsers;
#Id
#SequenceGenerator(name="idp_master_id_seq_gen",sequenceName="idp_master_id_seq")
#GeneratedValue(generator="idp_master_id_seq_gen")
#Column(name="id",nullable=false,unique=true)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id",nullable=false)
public Users getIdpUsers() {
return idpUsers;
}
public void setIdpUsers(Users idpUsers) {
this.idpUsers = idpUsers;
}
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="manager_id")
public Users getManagerUser() {
return managerUser;
}
public void setManagerUser(Users managerUser) {
this.managerUser = managerUser;
}
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="assessor_id")
public Users getAssessorUser() {
return assessorUser;
}
public void setAssessorUser(Users assessorUser) {
this.assessorUser = assessorUser;
}
}
You should move the annotations
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="user_id" ,referencedColumnName = "user_id"),
#JoinColumn(name="manager_id" ,referencedColumnName = "manager_id")
,#JoinColumn(name="assessor_id" ,referencedColumnName = "assessor_id")
})
#Cascade(CascadeType.ALL)
from private Set<IDPMaster> idps = null; to public Set<IDPMaster> getIdps() getter
Hibernate analyzes annotations by ID column and if you use annotations on getter there you should use getter annotations everywhere.

Hibernate duplicating insert

SOLVED
But the real problem wasn't hibernate, it was mainly me and second Netbeans.
I was making a post, and saving my data. But I was making a duplicate post in js, and Netbeans won't fire breakpoints twice (and I don't really know why). So, for me, it was making one post and just one insert. But no, two posts, and two inserts.
Sorry for losing your time :/
I'm new to Hibernate, so I'm having some problems, maybe it's just a silly one, I don't really know.
I'm trying to insert an object to database with a #ManyToOne relationship, but Hibernate is duplicating it when I persist and commit the transaction. This is my code.
User Post Class
#Entity
#Table(name = "USERPOST", schema = "ADMIN1")
public class UserPost implements java.io.Serializable {
private int iduserpost;
private String detail;
private User user;
public UserPost() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "IDUSERPOST", unique = true, nullable = false)
public int getIduserpost() {
return this.iduserpost;
}
public void setIduserpost(int iduserpost) {
this.iduserpost = iduserpost;
}
#Column(name = "DETAIL", nullable = false)
public String getDetail() {
return this.detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
#ManyToOne
#JoinColumn(name = "IDUSER")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
User Class
#Entity
#Table(name = "USER", schema = "ADMIN1")
public class User implements java.io.Serializable {
private int iduser;
private String name;
private String email;
private Set<UserPost> posts;
public User() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "IDUSER", unique = true, nullable = false)
public int getIduser() {
return this.iduser;
}
public void setIduser(int iduser) {
this.iduser = iduser;
}
#Column(name = "NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "EMAIL", nullable = false)
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
#OneToMany(mappedBy = "user")
public Set<UserPost> getPosts() {
return posts;
}
public void setPosts(Set<UserPost> posts) {
this.posts = posts;
}
}
Insert Method on Manager
public void Insert(Object data) {
Session session = null;
try {
session = hibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.persist(data);
session.getTransaction().commit();
} catch (Exception e) {
System.err.println(e.getMessage());
throw e;
} finally {
if (session.isOpen()) {
session.close();
}
}
}
Insert on Servlet
FeedManager manager = new FeedManager();
UserPost post = new UserPost();
post.setDetail(text);
User usrPesist = (User) manager.GetById(User.class, idUser);
post.setUser(usrPesist);
manager.Insert(post);
The result I'm getting is TWO UserPosts being inserted to database, when what I want is just one.
Is there something wrong?
Since you have defined as private Set<UserPost> posts; in User Class,
implement equals and hashcode methods in UserPost class , in that way you ensure that since it is a Set it wont add duplicates
But this is not a optimal way though.
I think this is problem with you id generation strategy in your user class. Just try by setting id manually in user class object it will work.
Try changing your code with this :
#org.hibernate.annotations.Cascade( {org.hibernate.annotations.CascadeType.SAVE_UPDATE})
#OneToMany(mappedBy = "user")
#JoinColumn(name = "IDUSER")
public Set<UserPost> getPosts() {
return posts;
}

Hibernate issue " Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1"

I'm using Jersey + spring + hibernate4.17 to develop api system; The problem is if the cleaFields is called twice at same time, the 2nd call will throw an exception as below,
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.checkRowCounts(BatchingBatch.java:133)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:110)
at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:101)
at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:149)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:162)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:357)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:280)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
The clearFields method in the Controller,
#Transactional(isolation=Isolation.REPEATABLE_READ)
public void clearFields(Integer userId) {
User user = this.userDao.get(userId);
user.getFields().clear();
userDao.flush(); //call the current session.flush(); this line can throw exception.
}
User Entity class
#Entity
#Table(name="users")
#NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id", unique = true, nullable = false)
private int userId;
#OneToMany(mappedBy = "user", cascade = { CascadeType.ALL }, targetEntity = UserProfile.class, fetch = FetchType.LAZY, orphanRemoval=true)
#OrderBy("id")
private List<UserProfile> fields = new ArrayList<UserProfile>();
public User() {
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public List<UserProfile> getFields() {
return fields;
}
public void setFields(List<UserProfile> fields) {
this.fields = fields;
}
}
UserProfile class,
#Entity
#Table(name="user_profiles")
public class UserProfile implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#Column(name="field_name")
private String fieldName;
#ManyToOne(targetEntity = User.class, fetch = FetchType.LAZY)
#JoinColumn(name="user_id")
private User user;
private String value;
public UserProfile() {
}
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFieldName() {
return this.fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
I did some research, the root cause is if the 2nd calling get some fields, and before delete them, the 1st calling already delete all fields from database.
In another word, the 2nd calling try to delete some records which already were deleted by others.
How can I solve the problem?

Can't delete row from join table (many-to-many)

I have mapped a bidirectional relation between two classes. This is the situation: I have Trips and Users. Users should be able to subscribe for a trip and to unsubscribe. So I don't want to delete the obects, only the row in the join table. Currently, the subscription part works but not unsubscribing.
When I test it the changes are made in memory, but not in the database. What could be wrong? My code is shown below.
Trip:
#ManyToMany(mappedBy = "enrolledExecutionMoments", cascade = {
CascadeType.ALL, CascadeType.ALL }, fetch = FetchType.EAGER)
private List<User> tripExecutionMomentParticipators = new ArrayList<User>();
public TripExecutionMoment() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#JsonIgnore
public List<User> getTripExecutionMomentParticipators() {
return tripExecutionMomentParticipators;
}
public void setTripExecutionMomentParticipators(
List<User> tripExecutionMomentParticipators) {
this.tripExecutionMomentParticipators = tripExecutionMomentParticipators;
}
public void addEnrollement(User user) {
tripExecutionMomentParticipators.add(user);
}
public void removeEnrollement(User user) {
tripExecutionMomentParticipators.remove(user);
}
User:
#Entity
#Table(name = "t_user")
public class User {
#Id
#GeneratedValue
private int id;
#NotBlank(message = "err_invalid_mail")
#Email(message = "err_invalid_mail")
private String email;
#ManyToMany(cascade = { CascadeType.ALL, CascadeType.ALL }, fetch = FetchType.EAGER)
#JoinTable(joinColumns = { #JoinColumn(name = "userId") }, inverseJoinColumns = { #JoinColumn(name = "tripExecutionMomentId") })
private List<TripExecutionMoment> enrolledExecutionMoments = new ArrayList<TripExecutionMoment>();
public User() {
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getId() {
return id;
}
#JsonIgnore
public TripExecutionMoment getCheckedInTripExecutionMoment() {
return checkedInTripExecutionMoment;
}
public void setCheckedInTripExecutionMoment(
TripExecutionMoment checkedInTripExecutionMoment) {
this.checkedInTripExecutionMoment = checkedInTripExecutionMoment;
}
public void addEnrollement(TripExecutionMoment tripExecutionMoment) {
enrolledExecutionMoments.add(tripExecutionMoment);
}
public void removeEnrollement(TripExecutionMoment tripExecutionMoment) {
enrolledExecutionMoments.remove(tripExecutionMoment);
}
}
This is the code where I enroll(subscribe) and dis-enroll(unsubscribe)
#Transactional
public void enrollParticipator(User user,
TripExecutionMoment tripExecutionMoment)
throws AlreadyBoundException {
if (tripExecutionMoment.getTripExecutionMomentParticipators().contains(
user)) {
throw new AlreadyBoundException(
"This user is already enrolled for this trip");
}
tripExecutionMoment.addEnrollement(user);
user.addEnrollement(tripExecutionMoment);
getEntityManager().merge(tripExecutionMoment);
}
#Transactional
public void disenrollParticipator(User user,
TripExecutionMoment tripExecutionMoment) {
tripExecutionMoment.removeEnrollement(user);
user.removeEnrollement(tripExecutionMoment);
getEntityManager().merge(user);
}

Categories