Why does Hibernate goes StackOverflowError when many-to-one is lazy? - java

I have the following table schema, where a simulation has many searches and any search has many properties.
Since I would like to persist a Simulation entity with its searches and their properties all at once, I mapped my entity like this:
Simulation.java
#Data
#EqualsAndHashCode(of = "id")
#ToString(exclude = "searches")
#Entity
#Table(name = "SIMULATION")
public class Simulation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
#SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "SIMULATION_ID")
private Set<SimulationSearch> searches = new HashSet<>(0);
// other fields
}
SimulationSearch.java
#Data
#EqualsAndHashCode(of = "id")
#ToString(exclude = "properties")
#Entity
#Table(name = "SIM_SEARCH")
public class SimulationSearch implements Serializable {
#EmbeddedId
private SimulationSearchId id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID"),
#JoinColumn(name = "POSITION", referencedColumnName = "POSITION")
})
private Set<SimulationSearchProperty> properties = new HashSet<>(0);
// other fields...
#Data
public static class SimulationSearchId implements Serializable {
#ManyToOne
#JoinColumn(name = "SIMULATION_ID", insertable = false, updatable = false)
private Simulation simulation;
private int position;
}
}
SimulationSearchProperties.java
#Data
#EqualsAndHashCode(of = "id")
#Entity
#Table(name = "SIM_SEARCH_PROPERTY")
public class SimulationSearchProperty implements Serializable {
#EmbeddedId
private SimulationSearchPropertyId id;
private String value;
#Data
public static class SimulationSearchPropertyId implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "SIMULATION_ID", referencedColumnName = "SIMULATION_ID", insertable = false, updatable = false),
#JoinColumn(name = "POSITION", referencedColumnName = "POSITION", insertable = false, updatable = false)
})
private SimulationSearch search;
private String label;
}
}
What happens is that Hibernate keeps printing the following query untill it goes on StackOverflowError.
select simulation0_.*, searches1_.*, properties5_.*
from simulation simulation0_
left outer join sim_search searches1_ on simulation0_.id = searches1_.simulation_id
left outer join sim_search_property properties5_ on searches1_.position = properties5_.position and searches1_.simulation_id = properties5_.simulation_id
where simulation0_.id = ?
While mapping between Simulation and SimulationSearch is very similar to SimulationSearch and SimulationSearchProperty mapping, this error started happening when I set ManyToOne annotation of SimulationSearch#properties as lazy fetch and didn't stop even if I set SimulationSearchPropertyId#search as lazy too.
What am I missing?
UPDATES
I'm using Hibernate 4.2.6.Final
Partial stacktrace log:
java.lang.StackOverflowError
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:148)
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:104)
at org.hibernate.engine.spi.QueryParameters.<init>(QueryParameters.java:81)
at org.hibernate.loader.Loader.loadEntity(Loader.java:2114)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
at org.hibernate.type.EntityType.resolve(EntityType.java:490)
at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
at org.hibernate.loader.Loader.doQuery(Loader.java:911)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:342)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:312)
at org.hibernate.loader.Loader.loadEntity(Loader.java:2121)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:82)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:72)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3927)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:460)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:429)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:206)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:262)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:150)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1092)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1019)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:672)
at org.hibernate.type.EntityType.resolve(EntityType.java:490)
at org.hibernate.type.ComponentType.resolve(ComponentType.java:667)
at org.hibernate.type.ComponentType.nullSafeGet(ComponentType.java:349)
at org.hibernate.type.ManyToOneType.hydrate(ManyToOneType.java:190)
at org.hibernate.type.ComponentType.hydrate(ComponentType.java:642)
at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:775)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:708)
at org.hibernate.loader.Loader.processResultSet(Loader.java:943)
at org.hibernate.loader.Loader.doQuery(Loader.java:911)
...
I've just updated a bit the entity mapping, removing mappedBy annotation attribute and adding instead #JoinColumns annotations.
Now persistence is working fine, but the StackOverflowError is still there when I try to load a single simulation.
I've also cleaned up Hibernate generated sql removing uninteresting pieces of information.

You forgot to annotate you relationship to be bidirectional.
For your first class the change should be
#Data
#EqualsAndHashCode(of = "id")
#ToString(exclude = "searches")
#Entity
#Table(name = "SIMULATION")
public class Simulation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "simulation_generator")
#SequenceGenerator(name = "simulation_generator", sequenceName = "SIMULATION_SEQ", allocationSize = 1)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "SimulationSearchId")
private Set<SimulationSearch> searches = new HashSet<>(0);
// other fields
}
Without the attribute mappedBy a JPA provider assumes that you have 2 unidirectional relationship, which leads to an infinite circle reference between your data.
Reference
JSR 338: Java TM Persistence API, Version 2.1
11.1.40 OneToMany Annotation
4th paragraph, 2nd sentence
If the relationship is bidirectional, the
mappedBy element must be used to specify the relationship field or property of the entity that is the
owner of the relationship.

Related

Hibernate changes way of id generation after using #JoinTable annotation

I have two entities having many to many relation. Relation table is also mapped ad entity. It has id column with IDENTITY strategy.
#Entity
#Table(name = "Employee")
public class Employee {
// ...
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "Employee_Project",
joinColumns = { #JoinColumn(name = "employee_id") },
inverseJoinColumns = { #JoinColumn(name = "project_id") }
)
Set<Project> projects = new HashSet<>();
// standard constructor/getters/setters
}
#Entity
#Table(name = "Project")
public class Project {
// ...
#ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<>();
// standard constructors/getters/setters
}
#Table
public class EmployeeProject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "EmployeeProjectId", nullable = false)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "EmployeeId", nullable = false)
private Employee employee;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "ProjectId", nullable = false)
private Project project;
}
If Employee entity doesn't have #JoinTable mapping generation SQL looks like this:
create table EmployeeProject (EmployeeProjectId integer generated by default as identity
However after adding #JoinTable mapping in Employee generation SQL changes to this:
create table EmployeeProject (EmployeeProjectId integer not null
How can I force hibernate to always generate first version of SQL?
You can't, and you also shouldn't. If you want to model the EmployeeProject entity, then you should also use it, i.e. map the many-to-many association as two one-to-many associations:
#Entity
#Table(name = "Employee")
public class Employee {
// ...
#ManyToMany(mappedBy = "employee", cascade = { CascadeType.ALL })
Set<EmployeeProject> projects = new HashSet<>();
// standard constructor/getters/setters
}
#Entity
#Table(name = "Project")
public class Project {
// ...
#ManyToMany(mappedBy = "project")
private Set<EmployeeProject> employees = new HashSet<>();
// standard constructors/getters/setters
}

Hibernate OneToMany mapping & Query generation : More than one row with the given identifier was found

I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}

Is it possible to implement (0..n) many to many (using #ManyToMany annotation) relation in JPA?

I'm looking for a way to implement (0..n) many to many relation in JPA, much possibly using #ManyToMany annotation. All examples that I found were about (1..n) relations. What I need to accomplish:
- I've got two entities: Contact and Tag. Each Contact can have 0..n Tags. Each Tag can have 0..n Contacts. From SQL point of view it would look like
this: Contact (0..n) --- (1) Contact_has_Tag (1) --- (0..n) Tag.
Code below is not working for me because JPA is linking columns with INNER JOIN.
OFC I could do this using intermediate entity and #OneToMany and #ManyToOne annotations, but I want a simpler sollution.
#Data
#Entity
public class Contact {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#JsonIgnore
private long id;
#Column(unique = true)
private String email;
// ...
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnore
#JoinTable(
name = "contact_has_tag",
joinColumns = #JoinColumn(name = "contact_id", referencedColumnName = "id", updatable = false, nullable = true),
inverseJoinColumns = #JoinColumn(name = "tag_id", referencedColumnName = "id", updatable = false, nullable = true))
private List<ContactTag> contactTags = new ArrayList<ContactTag>();
}
#Entity
#Data
public class ContactTag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column
private String name;
#ManyToMany(mappedBy="contactTags", fetch = FetchType.LAZY)
#JsonIgnore
private List<Contact> contacts = new ArrayList<Contact>();
}
Any ideas how it should be done?

Cascading save Entity objects with Foreign Key as a part of composite Primary Key

I would like to persist object of my QuestionCompletion class with all child elements. One of these childs has a composite primary key. And as a part of this primary key I have also foreign key to another entity. As a result I am getting this error:
Exception caught during request processing: javax.ejb.EJBTransactionRolledbackException:
could not set a field value by reflection setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
javax.ejb.EJBTransactionRolledbackException: could not set a field value by reflection
setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
And the last "caused by" is of course NullPointerException:
Caused by: java.lang.NullPointerException
This is part of my code. The last line causes error.
QuestionCompletion questionCompletion = new QuestionCompletion();
List<QuestionCompletionAnswer> answers = new ArrayList<QuestionCompletionAnswer>();
for (;;) { // loop isn't important; it's loop for answers
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
extendedQuestion.setId(extendedQuestionId); //extendedQuestionId is known to me in that place
for (;;) { // loop isn't important; it's loop for question answers
//questionCompletion and extendedQuestion are popualted here
QuestionCompletionAnswer questionCompletionAnswer = new QuestionCompletionAnswer();
questionCompletionAnswer.setQuestionCompletion(questionCompletion);
questionCompletionAnswer.setExtendedQuestion(extendedQuestion);
answers.add(questionCompletionAnswer);
}
}
questionCompletion.setAnswers(answers);
questionCompletionService.saveOrMerge(questionCompletion);
This is my basic entity class I would like to persist with all its childs elements. I have realized that List<QuestionCompletionAnswer> causes problems. I have used cascade = CascadeType.ALL to allow to persist childs elements also.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "extended_question_id")
protected List<QuestionCompletionAnswer> answers;
}
This is my class - Primary Key for the QuestionCompletionAnswer class.
#Embeddable
public class QuestionCompletionAnswerPK implements Serializable {
#Column(name = "question_completion_id")
protected Long questionCompletionId;
#Column(name = "extended_question_id")
protected Long extendedQuestionId;
}
And this is class which uses my EmbeddedId. Attribues questionCompletionId and questionCompletionId are the foreign key for some another entities so I have placed below also whole objects of these entities with #MapsId annotation.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
Could you tell me if my annotations are correct? Maybe I have mixed up few approaches. Or I can't in that case persist my basic object with all of its child elements.
EDIT
Now my mapping looks like:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
Code of the QuestionCompletionAnswerPK class is the same.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false)
protected ExtendedQuestion extendedQuestion;
}
With that mapping I am still getting the same exception.
EDIT #2
However when I have changed QuestionCompletionAnswer class in this way:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
I am getting that exception:
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated
for:class com.example.model.domain.QuestionCompletionAnswer
Edit 1 and 2 are still not right. You need mapsid specified on the relationship or you must set the field in the embedded id with a value yourself. And when you use mapsid, you shouldn't have the join column marked insertable=false or jpa can't insert a value for you. The last problem I see is that the new question is not persisted so it doesn't get an id assigned that the answer can reference - you need to explicitly persist the new question in the for loop or mark the relationship in the answer to it to cascade persist.
Why are you mixing jpa and hibernate annotations ? jpa annotations alone should be enough.
Try with the following mapping (I removed the class QuestionCompletionAnswerPK) :
QuestionCompletion.java
#Entity
#Table(name = "question_completion")
public class QuestionCompletion {
#Id
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
QuestionCompletionAnswer.java
#Entity
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "question_completion_fk")
private QuestionCompletion questionCompletion;
#Id
#ManyToOne
#JoinColumn(name = "extended_question_fk")
private ExtendedQuestion extendedQuestion;
}
With this mapping, I run the following test case and it worked :
QuestionCompletion questionCompletion = new QuestionCompletion();
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
QuestionCompletionAnswer answer = new QuestionCompletionAnswer();
answer.setQuestionCompletion(questionCompletion);
answer.setExtendedQuestion(extendedQuestion);
questionCompletion.setAnswers(Collections.singletonList(answer));
...
session.save(extendedQuestion);
session.save(questionCompletion);

Can't delete element from #OneToMany collection

i have an entity Entity1 that have one to many relation with Entity2 as follows:
1- Entity1:
#Entity
#Table(name = "Entity1", catalog = "mydb")
public class Entity1 implements java.io.Serializable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "entity1", cascade = javax.persistence.CascadeType.ALL)
#OrderBy("id")
private Set<Entity2> collection = new HashSet<Entity2>(
0);
}
2- Entity2: (equals and hashcode method overridden)
#Entity
#Table(name = "entity2", catalog = "advertisedb")
public class Entity2 implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "pkid", unique = true, nullable = false)
#Basic(fetch = FetchType.EAGER)
private long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "fk_entity1", nullable = false)
private Entity1 entity1;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "apid", nullable = false)
private Entity3 entity3;
}
3- Here's how i am removing the entity from the collection:
entity1Obj.getEntity2().remove(entity2);
log.debug("size after remove: "+ entity1Obj.getEntity2().size()); // size decreases correctly, so the entity is removed from the collection
entity1Dao.updateEntity1(entity1);
4- DAO method:
public void updateEntity1(Entity1 entity1) {
getCurrentSession().update(getCurrentSession().merge(entity1));
}
Problem: what i get in the console, is a select query for the entity2 that should be removed, and no delete query, and nothing is getting deleted.
please advise how to fix this issue.
i replaced cascade = CascadeType.ALL with orphanRemoval = true and it works fine now.

Categories

Resources