Jpa - Hibernate ManyToMany do many insert into join table - oracle

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

Related

Why hibernate is throwing constraintViolationException?

Order Entity
#Entity
#Table(name = "Order",
indexes = {
#Index(name = "ORDER_X1", columnList = "REFERENCE_ID,SOURCE_ID"),
#Index(name = "ORDER_X2", columnList = "TYPE,STATUS")
}
)
#DiscriminatorColumn(name="PROCESSOR_TYPE", discriminatorType=DiscriminatorType.STRING, length = 80)
#SequenceGenerator(name="orderSeq", sequenceName="ORDER_SEQ")
#Inheritance(strategy= InheritanceType.JOINED)
public abstract class OrderEntity implements Serializable {
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE, generator="orderSeq")
private Long id;
#ManyToMany(cascade={CascadeType.MERGE})
#JoinTable(
name = "FILE_ORDER_MAP",
joinColumns = {#JoinColumn(name = "ORDER_ID")},
inverseJoinColumns = {#JoinColumn(name = "FILE_ID")}
)
private Set<TransferFile> transferFiles = new HashSet<>();
#Column(name = "TYPE")
#Enumerated(EnumType.STRING)
private OrderType type;
#Column(name = "AMOUNT", precision = 12, scale = 2)
private LcMoney amount;
#Column(name = "STATUS")
#Enumerated(EnumType.STRING)
private OrderStatus reconStatus;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "STATUS_D", nullable = false)
#LcDateTimeUtc()
private DateTime reconStatusDate;
#Column(name = "REFERENCE_ID")
private Long referenceId;
#Column(name = "SOURCE_ID")
private Long sourceId;
#Column(name = "ACCOUNT_ID")
private Long accountId;
#Column(name = "PROCESSOR_TYPE", insertable = false, updatable = false)
#Enumerated(EnumType.STRING)
private OrderProcessorType processorType;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "TX_EXECUTION_D")
#LcDateTimeUtc()
private DateTime executedDate;
#Type(type = LcUtc.JPA_JODA_TIME_TYPE)
#Column(name = "CREATE_D")
#LcDateTimeUtc()
private DateTime createDate;
#Column(name = "IS_ON_DEMAND")
#Type(type = "yes_no")
private boolean isOnDemand;
#ManyToOne(fetch = FetchType.LAZY, optional = true, cascade = {CascadeType.PERSIST})
#JoinColumn(name="PAYER_ID", nullable=true)
private Payer payer;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID")
private List<OrderTransaction> orderTransactions;
#OneToMany(cascade = {CascadeType.ALL})
#JoinColumn(name = "ORDER_ID", referencedColumnName = "ID",
foreignKey = #ForeignKey(name = "FK_ORDER")
)
private List<MatchResult> matchResults;
#Version
#Column(name = "VERSION")
private Integer version;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "externalSourceId", column = #Column(name = "TRANS_EXT_SRC_ID")),
#AttributeOverride(name = "externalId", column = #Column(name = "TRANS_EXT_REF_ID"))
})
private LcExternalIdEntity transExtId;
#PreUpdate
#PrePersist
public void beforePersist() {
if (reconStatusDate != null) {
reconStatusDate = reconStatusDate.withZone(DateTimeZone.UTC);
}
if (executedDate != null) {
executedDate = executedDate.withZone(DateTimeZone.UTC);
}
if (createDate != null) {
createDate = createDate.withZone(DateTimeZone.UTC);
}
}
// getters and setters
}
//controller method
public Response processFile(){
// separate trasaction
service.readFileAndCreateOrders(); // read files and create orders in new status
List<Order> newOrders = service.getNewOrders();
for( Order order: newOrders){
service.processOrder(order); // separate transaction
}
}
#Transaction
void processOrder(OrderEntity order){
matchResultJpaRepository.save(orderEntity.id);
log.info("Saving matchId={} for order={}", match.getId(), order.getId());
// create new transaction and add to order
OrderTransaction transaction = createNewTransaction(order);
order.getTransactions().add(transaction);
order.setStatus("PROCESSED");
log.info("Saving Order id={}, Type={}, Status={} ", order.getId(), order.getType(), order.getStatus());
orderRepository.save(order);
}
I am seeing this below error.
ORA-01407: cannot update ("PAYMENTS"."MATCH_RESULT"."ORDER_ID") to NULL
This endpoing is not exposed to user. There is a batch job which invokes this endpoint.
This code has been there for atleast a year and this is the first time i am seeing this.
This happened only once and for only one call. I am seeing both the logs printed. I am puzzled why I am seeing above error complaining about NULL order id. From the logs, we can confirm that the order id is definitely not null.
Any idea why this is happening? What can be done to fix this?

Left Join Fetch Behaving Like Inner Join

I have a one-to-many relationship between routes and stops. In order to maintain an audit trail, my Stop entities have a "historic" boolean.
When fetching a route, I want to ignore historic stops, and so I constructed this query:
#Query("select r from Route r " +
"left join fetch r.schedules schedule " +
"left join fetch r.stops stop " +
"where r.routeId = :routeId and stop.historic = false ")
Optional<Route> findByIdLoadStops(#Param("routeId") int routeId);
This works fine when the route has non-historic stops and no stops, but when the route only has a historic stop (which shouldn't happen but I want to be able to at least handle it), it returns an empty optional as though an inner join has been performed.
When logging the JPA query created by hibernate, I can see that the query uses a left outer join.
What have I done incorrectly?
Route and Stop entities:
#Table(name = "route")
#Entity
public class Route {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "routeId", columnDefinition = "SMALLINT(5) UNSIGNED")
private int routeId;
#Column(name = "locked")
private boolean locked = false;
#OneToMany(mappedBy = "route",
cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
#OrderBy("stopTime asc")
private SortedSet<Stop> stops = new TreeSet<>();
public Route() {
}
}
#Table(name = "stop", uniqueConstraints = {
#UniqueConstraint(columnNames = {"stopTime", "routeId"}),
#UniqueConstraint(columnNames = {"stopName", "routeId"})})
#Entity
public class Stop implements Comparable<Stop> {
#Id
#Column(name = "stopId")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int stopId;
#Column(name = "routeId",
columnDefinition = "SMALLINT(5)",
updatable = false, insertable = false)
private int routeId;
#ManyToOne(cascade = CascadeType.MERGE,
fetch = FetchType.LAZY)
#JoinColumn(name = "routeId")
private Route route;
#Column(name = "stopTime")
private LocalTime stopTime;
#Column(name = "stopName")
private String stopName;
#JoinColumn(name = "originalId", referencedColumnName = "stopId")
#ManyToOne(fetch = FetchType.LAZY)
private Stop originalStop = this;
#Column(name = "historic")
private boolean historic = false;
public Stop() {
}
}

How to retrieve data based on inverseColumn data using CrudRepository in springboot?

I have two tables i.e. users and events. Users table will be filled when new user will sign up. Later same user can create calendar events. so events table will be filled and users_events will keep mapping of events based on user.
I would like to find all events based on logged in userId. so here is query, it should return data based on it.
select * from events where eventid in (select eventId from users_event where id_user=x ). Here is my Users and Event Entity class.
User.java
#Entity
#Table(name = "users")
public class User {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "family_name", nullable = false)
private String familyName;
#Column(name = "e_mail", nullable = false)
private String email;
#Column(name = "phone", nullable = false)
private String phone;
#Column(name = "language", nullable = false)
private String language;
#Column(name = "id_picture")
private String pictureId;
#Column(name = "login", nullable = false)
private String login;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "birth_date")
private Date birthDate;
#Column(name = "enabled")
private Boolean enabled;
//getter and setter
Event.java
#Entity
#Table(name = "events")
public class Event {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
#Column(name = "eventId", nullable = false)
private Long eventId;
#Column(name = "title", nullable = false)
private String title;
#Column(name = "description", nullable = true)
private String description;
#Column(name = "startAt", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date startAt;
#Column(name = "endAt", nullable = true)
#Temporal(TemporalType.TIMESTAMP)
private Date endAt;
#Column(name = "isFullDay", nullable = false)
private Boolean isFullDay;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "users_event", joinColumns = { #JoinColumn(name = "id_event", referencedColumnName = "eventId") }, inverseJoinColumns = { #JoinColumn(name = "id_user", table = "users", referencedColumnName = "id") })
private Set<User> user = new HashSet<User>();
/getter and setter
EventRepo.java
public interface EventRepo extends CrudRepository<Event, Long> {
Event findByUser(Set<User> user);
}
I am trying to implement something, which can give me output of this query.
select * from events where eventid in (select eventId from users_event where id_user=x )
here is my implementation.any input please?
#RequestMapping(value = "/events", method = RequestMethod.GET)
public #ResponseBody List<Event> getEvents() {
logger.debug("get event list");
User x=new User();
x.setId(1);
Set<User> user= new HashSet();
user.add(x);
return (List<Event>) eventRepo.findByUser(user);
}
Just add a following method to your EventRepo:
List<Event> findAllByUserId(Long userId);
And modify your controller to something like this:
#RequestMapping(value = "/events", method = RequestMethod.GET)
public List<Event> getEvents() {
return eventRepo.findAllByUserId(1L);
}

Spring data JPA entity change not being persisted

I have a Spring data entity (using JPA w/ Hibernate and MySQL) defined as such:
#Entity
#Table(name = "dataset")
public class Dataset {
#Id
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
private Long id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "guid", nullable = false)
private String guid;
#Column(name = "size", nullable = false)
private Long size;
#Column(name = "create_time", nullable = false)
private Date createTime;
#OneToOne(optional = false)
#JoinColumn(name = "created_by")
private User createdBy;
#Column(name = "active", nullable = false)
private boolean active;
#Column(name = "orig_source", nullable = false)
private String origSource;
#Column(name = "orig_source_type", nullable = false)
private String origSourceType;
#Column(name = "orig_source_org", nullable = false)
private String origSourceOrg;
#Column(name = "uri", nullable = false)
private String uri;
#Column(name = "mimetype", nullable = false)
private String mimetype;
#Column(name = "registration_state", nullable = false)
private int registrationState;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "dataset_id")
#JsonManagedReference
private List<DatasetFile> datasetFiles;
I have the following repository for this entity:
public interface DatasetRepo extends JpaRepository<Dataset, Long> {
#Query("SELECT CASE WHEN COUNT(p) > 0 THEN 'true' ELSE 'false' END FROM Dataset p WHERE p.uri = ?1 and p.registrationState>0")
public Boolean existsByURI(String location);
#Query("SELECT a FROM Dataset a LEFT JOIN FETCH a.datasetFiles c where a.registrationState>0")
public List<Dataset> getAll(Pageable pageable);
#Query("SELECT a FROM Dataset a LEFT JOIN FETCH a.datasetFiles c WHERE a.registrationState>0")
public List<Dataset> findAll();
#Query("SELECT a FROM Dataset a LEFT JOIN FETCH a.datasetFiles c where a.guid= ?1")
public Dataset findByGuid(String guid);
}
Now - In a controller, I am fetching a dataset, updating one of its attributes and I would be expecting that attribute change to be flushed to the DB, but it never is.
#RequestMapping(value = "/storeDataset", method = RequestMethod.GET)
public #ResponseBody
WebServiceReturn storeDataset(
#RequestParam(value = "dsGUID", required = true) String datasetGUID,
#RequestParam(value = "stType", required = true) String stType) {
WebServiceReturn wsr = null;
logger.info("stType: '" + stType + "'");
if (!stType.equals("MongoDB") && !stType.equals("Hive") && !stType.equals("HDFS")) {
wsr = getFatalWebServiceReturn("Invalid Storage type '" + stType + "'");
} else if (stType.equals("MongoDB")) {
/* Here is where I'm reading entity from Repository */
Dataset dataset = datasetRepo.findByGuid(datasetGUID);
if (dataset != null) {
MongoLoader mongoLoader = new MongoLoader();
boolean success = mongoLoader.loadMongoDB(dataset);
logger.info("Success: " + success);
if (success) {
/* Here is where I update entity attribute value, this is never flushed to DB */
dataset.setRegistrationState(1);
}
wsr = getWebServiceReturn(success ? 0 : -1, "Successfully loaded dataset files into " + stType + " storage", "Failed to load dataset files into " + stType + " storage");
}
}
return wsr;
}
Thank you
You need to annotate the method of request mapping with #Transactional.
Why? If you want to modify an object in memory and then it is updated transparently in the database you need do it inside an active transaction.
Don't forget you're using JPA (spring-data is using JPA) and if you want your Entity will be in a managed state you need an active transaction.
See:
http://www.objectdb.com/java/jpa/persistence/update
Transparent Update Once an entity object is retrieved from the
database (no matter which way) it can simply be modified in memory
from inside an active transaction:
Employee employee = em.find(Employee.class, 1);
em.getTransaction().begin();
employee.setNickname("Joe the Plumber");
em.getTransaction().commit();

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