Hi I have a ManyToOne relationship between book and Student entity. I created this relationship as a bi-directional relationship. See classes below
Book class
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "Book")
#Table(name = "book")
public class Book {
#Id
#SequenceGenerator(
name = "book_sequence",
sequenceName = "book_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "book_sequence"
)
#Column(
name = "id",
updatable = false
)
private Long id;
#Column(
name = "book_name",
nullable = false
)
private String bookName;
#ManyToOne
#JoinColumn(
name = "student_id",
nullable = false,
referencedColumnName = "id",
foreignKey = #ForeignKey(
name = "student_book_fk"
)
)
private Student student;
}
Student class
#Getter
#Setter
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Entity(name = "Student")
#Table(name = "student",
uniqueConstraints = {
#UniqueConstraint(name = "student_email_unique", columnNames = "email")
}
)
public class Student {
#Id
#SequenceGenerator(name = "student_seq", sequenceName = "student_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "student_seq")
#Column(name = "id",updatable = false)
private Long id;
#Column(name = "first_name", nullable = false, columnDefinition = "TEXT")
private String firstName;
#OneToMany(
mappedBy = "student",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
private List<Book> books = new ArrayList<>();
public void addBook(Book book){
// if(books == null){
// this.books = new ArrayList<>();
// }
if(!this.books.contains(book)){
this.books.add(book);
book.setStudent(this);
}
}
public void removeBook(Book book){
if(this.books.contains(book)){
this.books.remove(book);
book.setStudent(null);
}
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", age=" + age +
", studentIdCard=" + studentIdCard.getCardNumber() +
'}';
}
}
When I run the application I am getting an error
Cannot invoke "java.util.List.contains(Object)" because "this.books" is null
This is available inside the addBook method in my student entity. In my student entity I initialized my books variable as below. I tried solutions provided in other similar issues raised in stackoverflow but unable to get this issue sorted.
#OneToMany(
mappedBy = "student",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
private List<Book> books = new ArrayList<>();
I am not sure whether use of lombok has something to do with this. But if I write my code inside addBook method as below, issue is sorted
public void addBook(Book book){
if(books == null){
this.books = new ArrayList<>();
}
if(!this.books.contains(book)){
this.books.add(book);
book.setStudent(this);
}
}
Any ideas what might cause this issue?
Cheers
I used the #Builder.Default annotation for the books variable.
#OneToMany(
mappedBy = "student",
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
#Builder.Default
private List<Book> books = new ArrayList<>();
Reference - use Lombok #Builder #Default #Singular to initialise List<>
Related
I send a POST request to create a new user but I get an error: "415 unsupported media type"
I have been reading the documentation and other questions on this topic and by now I have already checked the headers "Content-Type" and other.
I also know for sure that it's not about dependencies and not about configuration. I created a simple TestClass and tested my code on it. Everything works perfectly. From this I conclude that the matter is in my Entity class, but I do not understand what exactly.
This is my rest controller:
#RestController
#RequestMapping("/api/users")
#CrossOrigin(origins = "http://localhost:3000/", maxAge = 3600)
public class UserRestController {
...
#PostMapping()
public ResponseEntity<UserDto> create(#RequestBody User user) {
User result = userService.create(user);
return new ResponseEntity<>(UserDto.fromUser(result), HttpStatus.OK);
}
}
My EntityClass:
#Entity
#Table(name = "users")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User extends BaseEntity {
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Enumerated(EnumType.STRING)
#Column(name = "role")
private Role role;
#Column(name = "active")
private boolean active;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private List<Restaurant> restaurants;
#OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private List<Comment> comments;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "vote_id")
#JsonBackReference
private Vote vote;
#Override
public String toString() {
return "User{" +
"id='" + getId() + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", role=" + role +
", active=" + active +
'}';
}
}
And my BaseEntity class:
#MappedSuperclass
#Data
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#CreatedDate
#Column(name = "created")
private LocalDateTime created;
#LastModifiedDate
#Column(name = "updated")
private LocalDateTime updated;
}
Here is what I get in Postman:
It turned out that the problem is in the fields that are other objects of my application. The parser didn't know what to do with them. It was enough to ignore them (#JsonIgnore) and everything worked as it should.
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
#JsonIgnore
private List<Restaurant> restaurants;
#OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
#JsonIgnore
private List<Comment> comments;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "vote_id")
#JsonBackReference
#JsonIgnore
private Vote vote;
I make a blog on Spring Boot + Spring Date
There is a post, user, comment, and entities that contain links between them.
For each of these 6 entities, I added the annotation
#SequenceGenerator (name = "...", sequenceName = "...", allocationSize = 1)
Also created in the Database additionally hibernate_sequencе
However, the following problems arise.
When I add a post (with id = 1) and delete it, and then create a new post, it is already with id 2, not id 1
When I try to add a comment to it,then throws an error that usually occurs if there is no SequenceGenerator.
Error:
ERROR: insert or update on table "posts_comments" violates foreign key constraint "posts_comments_post_id_fkey"
DETAIL: Key (post_id) = (5) is not present in table
Why?
add comment in CommentService
public void create(Comments new_comment,Long parent_id, String login, int post_id)
{
Users user=userService.findByLogin(login);
Posts post=postsRepository.findById((long) post_id).get();
if((parent_id!=null)&&(commentsRepository.existsById(parent_id)))
{
Comments parentComment=commentsRepository.findById(parent_id).get();
parentComment.getChildComment().add(new_comment);
commentsRepository.save(parentComment);
}
new_comment.setOwnerpost(post);
new_comment.setOwner(user);
commentsRepository.save(new_comment);
}
Comment
#Entity
#Table(name = "comments")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Comments implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "clientsIdSeq1", sequenceName = "comments_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator="comments_id_seq")
private Long id;
#Column(name = "title")
private String title;
#Column(name = "content")
private String content;
#Column(name = "date_create")
private LocalDate dateCreate;
#Column(name = "count_like")
private Long countLike;
#Column(name = "count_dislike")
private Long counterDislike;
#OneToMany(fetch= FetchType.EAGER,cascade=CascadeType.ALL ,orphanRemoval=true )
#JoinTable(name = "parentchild_comment",
joinColumns = #JoinColumn(name= "parent_id"),
inverseJoinColumns = #JoinColumn(name= "child_id"))
#Fetch(value = FetchMode.SUBSELECT)
private List<Comments> childComment;
#ManyToOne(fetch= FetchType.EAGER,cascade=CascadeType.ALL )
#JoinTable(name = "users_comments",
joinColumns = #JoinColumn(name= "comment_id"),
inverseJoinColumns = #JoinColumn(name= "user_id"))
#JsonIgnoreProperties({"listPost", "listComment"})
private Users owner;
#ManyToOne(fetch= FetchType.EAGER,cascade = {CascadeType.REFRESH })
#JoinTable(name = "posts_comments",
joinColumns = #JoinColumn(name= "post_id"),
inverseJoinColumns = #JoinColumn(name= "comment_id"))
#JsonIgnoreProperties({"listComments"})
private Posts ownerpost;
}
Post
#Entity
#Table(name = "posts")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Posts implements Serializable {
#Id
#Column(name = "id")
#SequenceGenerator(name = "clientsIdSeq4", sequenceName = "posts_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator ="posts_id_seq" )
private Long id ;
#Column(name = "title")
private String title;
#Column(name = "content")
private String content;
#Column(name = "date_create")
private LocalDate dateCreate;
#Column(name = "count_like")
private Long countLike;
#Column(name = "count_dislike")
private Long counterDislike;
#OneToMany(mappedBy = "ownerpost",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#Fetch(value = FetchMode.SUBSELECT)
#JsonIgnoreProperties("childComment")
private List<Comments> listComments;
#ManyToOne(fetch= FetchType.EAGER,cascade=CascadeType.REFRESH)
#JoinTable(name = "users_posts",
joinColumns = #JoinColumn(name= "post_id"),
inverseJoinColumns = #JoinColumn(name= "user_id"))
#JsonIgnoreProperties({"listPost", "listComment"})
private Users owner;
}
User
#Entity
#Table(name = "users")
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name = "clientsIdSeq5", sequenceName = "users_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "users_id_seq")
private Long id;
#Column(name = "login")
private String login;
#Column(name = "password")
private String password;
#ManyToOne(optional = false, cascade = CascadeType.REFRESH)
#JoinColumn(name = "position_id")
private Position position;
#OneToMany(mappedBy = "owner",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#JsonIgnoreProperties("listComments")
#Fetch(value = FetchMode.SUBSELECT)
private List<Posts> listPost;
#OneToMany(mappedBy = "owner",fetch= FetchType.EAGER,cascade=CascadeType.ALL,orphanRemoval=true )
#JsonIgnoreProperties("childComment")
#Fetch(value = FetchMode.SUBSELECT)
private List<Comments> listComment;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Users)) return false;
Users users = (Users) o;
if (!Objects.equals(id, users.id)) return false;
if (!Objects.equals(login, users.login)) return false;
if (!Objects.equals(password, users.password)) return false;
if (!Objects.equals(position, users.position)) return false;
if (!Objects.equals(listPost, users.listPost)) return false;
return Objects.equals(listComment, users.listComment);
}
#Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (login != null ? login.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
result = 31 * result + (position != null ? position.hashCode() : 0);
result = 31 * result + (listPost != null ? listPost.hashCode() : 0);
result = 31 * result + (listComment != null ? listComment.hashCode() : 0);
return result;
}
}
my code https://dropmefiles.com/pdv48
Insomnia with with requests https://dropmefiles.com/jPOgB
You mixed the name and the sequence name. The generator attribute must be then name not the sequenceName
#SequenceGenerator(name = "clientsIdSeq1", sequenceName = "comments_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="clientsIdSeq1")
I am running a unit test set up as a DataJpaTest with an H2 database. It worked well, until I introduced a new Immutable Entity set using a Subselect. It does not break, but it does not find data that it should. I see in the SQL that it is correctly joining to the Subselect query.
When running as normal as an application, the #Subselect entity works fine, so it is an issue with the unit test setup somehow.
Here is my Main entity:
#Entity
#DynamicUpdate
#Getter
#Setter
#EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
#Table(name = "apps", schema = "public")
public class App extends BaseEntity
{
#Id
#Column(name = "app_id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ent_generator")
#SequenceGenerator(name = "ent_generator", sequenceName = "entity_seq", allocationSize = 1)
private long appId;
#EqualsAndHashCode.Include
#Basic
#Column(name = "app_name", length = 200)
private String appCode;
#EqualsAndHashCode.Include
#Basic
#Column(name = "app_ver_name", length = 200)
private String appVersion;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "parent_app_id")
private App parent;
// ...
#OneToMany(
mappedBy = "app",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<TrainingRequirement> trainingRequirements = new HashSet<>();
#OneToMany(mappedBy = "app")
private Set<EffectiveTrainingRequirement> effectiveTrainingRequirements = new HashSet<>();
// ...
}
Here is my #Subselect entity:
#Entity
#Immutable
#Subselect(
"SELECT\n" +
" tr.trn_req_id AS trn_req_id\n" +
" , tr.app_id AS app_id\n" +
" FROM apps a\n" +
" JOIN training_reqs tr\n" +
"-- ...")
#Synchronize({"apps","training_reqs"})
public class EffectiveTrainingRequirement
{
#Id
#Column(name = "trn_req_id", nullable = false)
private long trainingRequirementId;
#EqualsAndHashCode.Include
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "app_id")
private App app;
}
Here is the Unit test code (vastly reduced for relevant parts):
#RunWith(SpringRunner.class)
#DataJpaTest
public class TrainingRequirementRepositoryTest
{
#Autowired
private TestEntityManager entityManager;
#Autowired
private AppRepository appRepository;
#Before
public void setup()
{
// ...
app4 = new App(organization, "a4");
app4.addTrainingRequirement(new TrainingRequirement("treq6", c6));
app4.addTrainingRequirement(new TrainingRequirement("treq7", c7, tr1));
app4.addTrainingRequirement(new TrainingRequirement("treq8", c8, tr2));
entityManager.persist(app4);
app4v2 = new App(organization, "a4", app4, "v2");
app4v2.setParent(app4);
app4v2.addTrainingRequirement(treq7chg = new TrainingRequirement("treq7", c7, tr2));
treq7chg.setChangeType(InheritOverride.CHANGE);
app4v2.addTrainingRequirement(treq6rmv = new TrainingRequirement("treq6"));
treq6rmv.setChangeType(InheritOverride.REMOVE);
app4v2.addTrainingRequirement(treq9add = new TrainingRequirement("treq9", c9, tr4));
treq9add.setChangeType(InheritOverride.ADDITION);
entityManager.persist(app4v2);
}
#Test
public void test_AppWithEffectiveTR()
{
App app = appRepository.getAppWithParent(organization, "a4", "v2").get();
logger.debug("# tr: "+app.getTrainingRequirements().size());
logger.debug("# etr: "+app.getEffectiveTrainingRequirements().size());
for (EffectiveTrainingRequirement tr : app.getEffectiveTrainingRequirements())
logger.debug("tr: "+tr.toString());
}
}
The repository:
#Repository
public interface AppRepository extends CrudRepository<App, Long>
{
String APP_FETCH_QUERY = "SELECT a FROM App a " +
"LEFT JOIN FETCH a.parent p " +
"LEFT JOIN FETCH a.trainingRequirements atr " +
"LEFT JOIN FETCH a.effectiveTrainingRequirements ";
#Query(value = APP_FETCH_QUERY +
"WHERE a.organization = :org " +
" AND a.appCode = :appCode " +
" AND a.appVersion = :appVersion" )
Optional<App> getAppWithParent(#Param("org") Organization org,
#Param("appCode") String appCode,
#Param("appVersion") String appVersion);
}
I mapped two entities to those following classes :
#Getter
#Entity
#Table(name = "users")
#JsonIdentityInfo(generator = PropertyGenerator.class,
property = "id")
#SequenceGenerator(name = "id-generator", sequenceName = "seq_users")
#EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false)
#NoArgsConstructor(access = PROTECTED)
#RequiredArgsConstructor
public class User extends IdentifiedById {
#Include
#NonNull
#Column(name = "email_address", unique = true)
private String emailAddress;
#Setter
#JsonIgnore
private String hash;
#Setter
private boolean admin;
#OneToMany(
mappedBy = "user",
orphanRemoval = true,
cascade = ALL
)
#JsonIgnoreProperties("user")
private Set<Cart> carts;
{
carts = new HashSet<>(0);
}
}
#Getter
#Entity
#Table(
name = "carts",
uniqueConstraints = #UniqueConstraint(
columnNames = {
"creation_time",
"user_id"
}
)
)
#JsonIdentityInfo(generator = PropertyGenerator.class,
property = "id")
#SequenceGenerator(
name = "id-generator",
sequenceName = "seq_carts"
)
#EqualsAndHashCode(
callSuper = false
)
#RequiredArgsConstructor
#NoArgsConstructor(access = PROTECTED)
public class Cart extends IdentifiedById {
#NonNull
#Column(name = "creation_time")
private LocalDateTime creationTime;
#NonNull
#ManyToOne(cascade = ALL)
#JoinColumn(
name = "user_id",
referencedColumnName = "id"
)
#JsonManagedReference
private User user;
#Exclude
#JsonProperty("productStoreQuantities")
#JsonSerialize(converter = AdditionConverter.class)
#OneToMany(mappedBy = "cart", orphanRemoval = true, cascade = ALL)
private Set<Addition> additions;
{
additions = new HashSet<>(0);
}
}
If I retrieve a user, its carts do not contain its reference, it is fine by me.
Now from a rest endpoint perspective I would like not to serialize users along with their carts if one requests multiple users like so :
**/api/users -> {"id":1, "emailAddress":"test#test.test", "admin": false}**
**/api/users/1 -> {"id":1, "emailAddress":"test#test.test", "admin": false, "carts": [...]}**
Thus, I created a wrapper class named Users containing a list of users annotated with #JsonValue and #JsonIgnoreProperties("carts") :
#RequiredArgsConstructor
public class Users implements Serializable, List<User> {
#Delegate
#JsonValue
#JsonIgnoreProperties("carts")
private final List<User> values;
}
I don't know why but carts keep being serialized, I heard that #JsonIgnoreProperties does not work on collections and arrays but it does in my first case.
You should use JsonIgnoreProperties in a class level.
This is well explained in this post
https://www.baeldung.com/jackson-ignore-properties-on-serialization
I have follows ManyToMany relationship between WorkDay(has annotation ManyToMany) and Event
WorkDay entity
#Entity
#Table(name = "WORK_DAY", uniqueConstraints = { #UniqueConstraint(columnNames = { "WORKER_ID", "DAY_ID" }) })
#NamedQueries({
#NamedQuery(name = WorkDay.GET_WORK_DAYS_BY_MONTH, query = "select wt from WorkDay wt where wt.worker = :worker and to_char(wt.day.day, 'yyyyMM') = :month) order by wt.day"),
#NamedQuery(name = WorkDay.GET_WORK_DAY, query = "select wt from WorkDay wt where wt.worker = :worker and wt.day = :day") })
public class WorkDay extends SuperClass {
private static final long serialVersionUID = 1L;
public static final String GET_WORK_DAYS_BY_MONTH = "WorkTimeDAO.getWorkDaysByMonth";
public static final String GET_WORK_DAY = "WorkTimeDAO.getWorkDay";
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "WORKER_ID", nullable = false)
private Worker worker;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "DAY_ID", nullable = false)
private Day day;
#Column(name = "COMING_TIME")
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime comingTime;
#Column(name = "OUT_TIME")
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime outTime;
#Enumerated(EnumType.STRING)
#Column(name = "STATE", length = 16, nullable = false)
private WorkDayState state = WorkDayState.NO_WORK;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "WORK_DAY_EVENT", joinColumns = {
#JoinColumn(name = "WORK_DAY_ID", nullable = false)}, inverseJoinColumns = {
#JoinColumn(name = "EVENT_ID", nullable = false)})
#OrderBy(value = "startTime desc")
private List<Event> events = new ArrayList<>();
protected WorkDay() {
}
public WorkDay(Worker worker, Day day) {
this.worker = worker;
this.day = day;
this.state = WorkDayState.NO_WORK;
}
}
Event entity
#Entity
#Table(name = "EVENT")
public class Event extends SuperClass {
#Column(name = "DAY", nullable = false)
#Convert(converter = LocalDateAttributeConverter.class)
private LocalDate day;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TYPE_ID", nullable = false)
private EventType type;
#Column(name = "TITLE", nullable = false, length = 128)
private String title;
#Column(name = "DESCRIPTION", nullable = true, length = 512)
private String description;
#Column(name = "START_TIME", nullable = false)
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime startTime;
#Column(name = "END_TIME", nullable = true)
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime endTime;
#Enumerated(EnumType.STRING)
#Column(name = "STATE", nullable = false, length = 16)
private EventState state;
protected Event() {
}
}
Attached UI form for clarity
When I push Clock with run icon first time, it means "create event and start work day" in bean, calling the following methods:
public void startEvent() {
stopLastActiveEvent();
Event creationEvent = new Event(workDay.getDay().getDay(), selectedEventType, selectedEventType.getTitle(),
LocalDateTime.now());
String addEventMessage = workDay.addEvent(creationEvent);
if (Objects.equals(addEventMessage, "")) {
em.persist(creationEvent);
if (workDay.isNoWork()
&& !creationEvent.getType().getCategory().equals(EventCategory.NOT_INFLUENCE_ON_WORKED_TIME)) {
startWork();
}
em.merge(workDay);
} else {
Notification.warn("Невозможно создать событие", addEventMessage);
}
cleanAfterCreation();
}
public String addEvent(Event additionEvent) {
if (!additionEvent.getType().getCategory().equals(NOT_INFLUENCE_ON_WORKED_TIME)
&& isPossibleTimeBoundaryForEvent(additionEvent.getStartTime(), additionEvent.getEndTime())) {
events.add(additionEvent);
changeTimeBy(additionEvent);
} else {
return "Пересечение временых интервалов у событий";
}
Collections.sort(events, new EventComparator());
return "";
}
private void startWork() {
workDay.setComingTime(workDay.getLastWorkEvent().getStartTime());
workDay.setState(WorkDayState.WORKING);
}
In log I see:
insert into event table
update work_day table
insert into work_day_event table
on UI updated only attached frame. Always looks fine.. current WorkDay object have one element in the events collection, also all data is inserted into DB.. but if this time edit event row
event row listener:
public void onRowEdit(RowEditEvent event) {
Event editableEvent = (Event) event.getObject();
LocalDateTime startTime = fixDate(editableEvent.getStartTime(), editableEvent.getDay());
LocalDateTime endTime = fixDate(editableEvent.getEndTime(), editableEvent.getDay());
if (editableEvent.getState().equals(END) && startTime.isAfter(endTime)) {
Notification.warn("Невозможно сохранить изменения", "Время окончания события больше времени начала");
refreshEvent(editableEvent);
return;
}
if (workDay.isPossibleTimeBoundaryForEvent(startTime, endTime)) {
editableEvent.setStartTime(startTime);
editableEvent.setEndTime(endTime);
workDay.changeTimeBy(editableEvent);
em.merge(workDay);
em.merge(editableEvent);
} else {
refreshEvent(editableEvent);
Notification.warn("Невозможно сохранить изменения", "Пересечение временых интервалов у событий");
}
}
to the work_day_event insert new row with same work_day_id and event_id data. And if edit row else do one more insert and etc.. In the result I have several equals rows in work_day_event table. Why does this happen?
link to github project repository(look ver-1.1.0-many-to-many-problem branch)
Change CascadeType.ALL to CascadeType.MERGE for events in the WorkDay entity
Use this code
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
instead of
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
Do not use ArrayList, use HashSet. Because ArrayList allows duplicates.
For more info about CasecadeType, follow the tutorial:
Hibernate JPA Cascade Types
Cascading best practices
I think the simple solution is to remove the cascade on many to many relationship and do the job manually ! . I see you already doing it redundantly anyway . So try removing you CascadeType.ALL
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
How to persist #ManyToMany relation - duplicate entry or detached entity