Hibernate persists only first nested entity - java

My problem is that Hibernate does not persist nested entities given in entity.
Consider following entities:
PollEntity
#Table(name = "\"poll\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#EqualsAndHashCode(of = "id")
#Builder
public class PollEntity {
#Transient
public OptionEntity addOption(OptionEntity pollOptionEntity) {
if(options == null)
options = new HashSet<>();
options.add(pollOptionEntity);
pollOptionEntity.setPoll(this);
return pollOptionEntity;
}
#Transient
public OptionEntity dropOption(OptionEntity pollOptionEntity) {
options.remove(pollOptionEntity);
pollOptionEntity.setPoll(null);
return pollOptionEntity;
}
#Id
#Column(name = "id")
private UUID id;
#Column(name = "author")
#UUIDv4
private UUID author;
#Column(name = "poll_question")
#Size(max = 1000)
private String pollQuestion;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "poll", cascade = CascadeType.MERGE)
#Builder.Default
#Valid
private Set<OptionEntity> options;
}
OptionEntity
#Table(name = "\"option\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#EqualsAndHashCode(of = "id")
#Builder
public class OptionEntity {
#Id
#GeneratedValue(generator = "UUID")
#Column(name = "id")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#JoinColumn(name = "poll_id")
#ManyToOne
private PollEntity poll;
#Column(name = "option")
#Size(max = 1000)
#NotNull
private String option;
}
And here's service method:
#Transactional(rollbackFor = Throwable.class)
public void createPoll(#Valid PollEntity pollEntity) throws ValidationException {
validationService.validateOrThrow(pollEntity);
if (pollRepository.findById(pollEntity.getId()).isPresent())
throw new ValidationException("Invalid id", Map.of("id", "Poll with id (" + pollEntity.getId() + ") already exists"));
pollEntity = validationService.validateAndSave(pollRepository, pollEntity);
And corresponding test:
#Test
public void createPollTest() throws ValidationException {
var uuid = UUID.randomUUID();
var pollOption1 = OptionEntity.builder()
.option("Test option 1")
.build();
var pollOption2 = OptionEntity.builder()
.option("Test option 2")
.build();
var pollOption3 = OptionEntity.builder()
.option("Test option 3")
.build();
var poll = PollEntity.builder()
.id(uuid)
.pollQuestion("Test question")
.author(UUID.randomUUID())
.build();
poll.addOption(pollOption1);
poll.addOption(pollOption2);
poll.addOption(pollOption3);
pollService.createPoll(poll);
}
Which gives following output in database
poll
2e565f50-7cd4-4fc9-98cd-49e0f0964487 feae5781-ff07-4a21-9292-c11c4f1a047d Test question
option
c786fe5d-632d-4e94-95ef-26ab2af633e7 fc712242-8e87-41d8-93f2-ff0931020a4a Test option 1
and rest options ended up unpersisted.
I've also used to create options in separate method
#Transactional(propagation= Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public Set<OptionEntity> createOptions(#Valid Set<OptionEntity> pollOptionsEntities) throws ValidationException {
for (var pollOption : pollOptionsEntities) {
validationService.validateAndSave(pollOptionsRepository, pollOption);
}
return pollOptionsEntities;
}
and option entities were getting produced but had to switch to persisting from built-in entity methods due to errors with persisting poll entity.
Database schema looks like this:
CREATE TABLE "poll"
(
"id" UUID PRIMARY KEY,
"author" UUID NOT NULL,
"poll_question" VARCHAR(1000) NOT NULL
)
CREATE TABLE "option"
(
"id" UUID PRIMARY KEY,
"poll_id" UUID NOT NULL REFERENCES "poll" ("id") ON DELETE CASCADE
"option" VARCHAR(1000) NOT NULL
)
What are the possible approaches to try?
UPD 1
Having considered various looking-alike questions (1,2,3,4,5)
I've came up with this addition to entity which suppose make entities persistence regardless of actual value and still having only one option in output. What was done wrong?
#Override
public boolean equals(Object object) {
if ( object == this ) {
return false;
}
if ( object == null || object.getClass() != getClass() ) {
return false;
}
final OptionEntity other = OptionEntity.class.cast( object );
if ( getId() == null && other.getId() == null ) {
return false;
}
else {
return false;
}
}
#Override
public int hashCode() {
final HashCodeBuilder hcb = new HashCodeBuilder( 17, 37 );
if ( id == null ) {
while (getOptions().iterator().hasNext())
hcb.append( getOptions().iterator().next() );
}
else {
hcb.append( id );
}
hcb.append( options );
return hcb.toHashCode();
}

So the answer were quite trivial all the way long.
In order to overcome this the only thing that should be done is to change container: Set<OptionEntity> to List<OptionEntity>.
Hope this will not produce some hard-to-tackle bugs but if it can - please add comment.
Because in my case uniqueness was not strict requirement, ended up with this:
PollEntity:
#Table(name = "\"poll\"")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Builder
public class PollEntity {
#Transient
public OptionEntity addOption(OptionEntity pollOptionEntity) {
options.add(pollOptionEntity);
pollOptionEntity.setPoll(this);
return pollOptionEntity;
}
#Transient
public OptionEntity dropOption(OptionEntity pollOptionEntity) {
options.remove(pollOptionEntity);
pollOptionEntity.setPoll(null);
return pollOptionEntity;
}
#Id
#Column(name = "id")
private UUID id;
#Column(name = "status")
#Enumerated(EnumType.STRING)
#NotNull
private Status status;
#Column(name = "author")
#UUIDv4
private UUID author;
#Column(name = "poll_question")
#Size(max = 1000)
private String pollQuestion;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "poll", cascade = CascadeType.MERGE)
#Builder.Default
#Valid
private List<OptionEntity> options = new ArrayList<>();
}

Related

How to Map a dto to an entity with avoiding complexs objects

I try to map dto to an entity with avoiding complex objects but I have a null exception when I try to save my article entity??
in my ArticleRequest instead of using
Department & ArticleCategory as a full object I just put their ids (uid) .
Here is my ArticleRequest:
#Slf4j
#Builder
#Data
public class ArticleRequest {
private Long id;
#Size(min = 32, message = "uid should have at least 32 characters")
private String uid;
#Size(min = 2, message = "title should have at least 2 characters")
private String title;
#NotBlank(message = "content should not be empty value")
private String content;
#NotNull(message = "article category id should not be null value")
private String articleCategoryUid;
#NotNull(message = "department id should not be empty value")
private String departmentUid;
#JsonIgnore
private List<ArticleVote> articleVoteList;
public static ArticleRequest fromEntity(Article article) {
if (article == null) {
log.warn("Class: ArticleRequest || Method: fromEntity() || Error: article is null!!!");
return null;
}
return ArticleRequest.builder()
.id(article.getId())
.uid(article.getUid())
.title(article.getTitle())
.content(article.getContent())
.articleCategoryUid(article.getArticleCategory().getUid())
.departmentUid(article.getDepartment().getUid())
.build();
}
public static Article toEntity(ArticleRequest articleRequest) {
if (articleRequest == null) {
log.warn("Class: ArticleRequest || Method: toEntity() || Error: articleRequest is null!!!");
return null;
}
Article article = new Article();
article.setId(articleRequest.getId());
article.setUid(articleRequest.getUid());
article.setTitle(articleRequest.getTitle());
article.setContent(articleRequest.getContent());
article.getArticleCategory().setUid(articleRequest.getArticleCategoryUid()); // i have null exeption here !! because ArticleCategory already null
article.getDepartment().setUid(articleRequest.getDepartmentUid()); // i have null exeption here !! because Department already null
return article;
}
}
here is my baseEntity
#Data
#NoArgsConstructor
#AllArgsConstructor
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public class BaseEntity implements Serializable {
//region Simple Properties
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, updatable = false)
private Long id;
#NotNull
#Size(min = 32, message = "uid should have at least 32 characters")
#Column(name = "uid", nullable = true,updatable = false)
private String uid;
//endregion
}
here is my article category class
#Data
#Entity
#Table(name = "article_categories", schema = "public")
public class ArticleCategory extends BaseEntity {
//region Simple Properties
#NotNull
#Size(min = 2, message = "name should have at least 2 characters")
#Column(name = "name")
private String name;
#NotNull
#Size(min = 2, message = "slug should have at least 2 characters")
#Column(name = "slug")
private String slug;
#NotNull
#Size(min = 2, message = "description should have at least 2 characters")
#Column(name = "description")
private String description;
//endregion
//region Complex Properties
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "articleCategory", orphanRemoval = true)
private List<Article> articleList;
//endregion}
here is my department class
#Data
#Entity
#Table(name = "departments", schema = "public")
public class Department extends BaseEntity {
//region Simple Properties
#Size(min = 2,message = "name should have at least 2 characters")
#Column(name = "name")
private String name;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private DepartmentStatusEnum status;
//endregion
//region Complex Properties
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "department", orphanRemoval = true)
private List<Article> articleList;
}
and here is my service
#Override
public ArticleRequest saveArticle(ArticleRequest articleRequest) {
if (articleRequest == null) {
throw new InvalidEntityException(CustomErrorMessage.ARTICLE_CAN_NOT_BE_NULL.getMessage(), CustomErrorCodes.ARTICLE_NOT_VALID);
}
articleRequest.setUid(UUID.randomUUID().toString());
Article saveArticle=ArticleRequest.toEntity(articleRequest);// null exception
Article newArticle = articleRepository.save(saveArticle);
ArticleRequest newArticleRequest = ArticleRequest.fromEntity(newArticle);
return newArticleRequest;
}
so how I can save my article entity & pass uid of articleCategory and department with the right way!
thanks in advance.
Informations given dont give a great idea , you should past the class Department and Article in your question
Probably your problem is here :
article.getArticleCategory().setUid(articleRequest.getUid());
article.getDepartment().setUid(articleRequest.getUid());
You should set the ArticleCategory and the Department of your article , creating a new objtects and setting them .
I think the solution is replacing these lines with :
article.setArticleCategory(new ArticleCategory());
article.getArticleCategory().setUid(articleRequest.getArticleCategoryUid());
article.setDepartment(new Department());
article.getDepartment().setUid(articleRequest.getDepartmentUid());
TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
You should save or persist the Properties values in the database ( instances ) before the persistance of the Article object, so the code will be like that :
ArticleCategory ac = new ArticleCategory();
ac.setUid(articleRequest.getArticleCategoryUid());// if you need you can set the other properties of the ac , get them from the articleRequest
articleCategoryRepository.save(ac);
Department dep = new Department();
dep.setUid(articleRequest.getDepartmentUid());
departmentRepository.save(dep);
article.setArticleCategory(ac);
article.setDepartment(dep);
articleRepository.save(article);
article.getArticleCategory() //this gives you the NPE
Initialize articleCategory before you call the getter method of it.
E.g:
article.setArticleCategory(new ArticleCategory());
Its the same for department. Initialize department object before you call getDepartment()

Java JPA OneToMany relation and query

maybe duplicate question but I couldn't fina a solution for my case which I think is pretty simple.
I have two tables like so :
And those are the related DTO Object :
First table
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
Second table :
#Entity
#Table(name = "DA10004_REF_MESSAGE")
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
And the following query to get all the RefSignelement with the associated message :
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
Unfortunately it's returning an empty list, I have tried to change it with join fetch but nothing change.
Thank for the help
Remember that in JPQL you have to think in Objects, not relations. You want to fetch all 'RefSignalement' and eagerly fetch their 'refMessages' properties:
SELECT DISTINCT s FROM RefSignalement s JOIN FETCH s.refMessages
Here the "distinct" is only needed by JPA when assembling your resulting entities, but add unnecessary overhead to the SQL Query. If you have a Hibernate version >= 5.2.2 (I think), then there is a query hint you can use to avoid that:
List<RefSignalement> sigs = entityManager
.createQuery(
"select distinct s " +
"from RefSignalement s " +
"left join fetch s.refMessages ")
.setHint("hibernate.query.passDistinctThrough", false)
.getResultList();
Read more about it here.
a couple of things, RefMessage class is using composite primary key so i guess you need to use #IdClass or #EmbeddedId annotation. here I'm providing using
#IdClass
public class RefId implements Serializable {
private String codeSignalement;
private String destinataires;
// default constructor
public RefId() {
}
public RefId(String codeSignalement, String destinataires) {
this.codeSignalement = codeSignalement;
this.destinataires = destinataires;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RefId refId = (RefId) o;
return Objects.equals(codeSignalement, refId.codeSignalement) &&
Objects.equals(destinataires, refId.destinataires);
}
#Override
public int hashCode() {
return Objects.hash(codeSignalement, destinataires);
}
}
then you need to use like follows
#Entity
#Table(name = "DA10004_REF_MESSAGE")
#IdClass(RefId.class)
public class RefMessage {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Id
#Column(name = "DESTINATAIRE")
public String destinataires;
#Column(name = "MESSAGE")
public String message;
}
define your repository as follows:
public interface RefSignalementRepo extends
JpaRepository<RefSignalement, String> {
}
RefSignalement class defination as follows:
#Entity
#Table(name = "DA10003_REF_SIGNALEMENT")
public class RefSignalement {
#Id
#Column(name = "CD_SIGNALEMENT")
public String codeSignalement;
#Column(name = "LIBELLE")
public String libelle;
#Column(name = "CATEGORIE")
public String categorie;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
#JoinColumn(name = "CD_SIGNALEMENT")
public List<RefMessage> refMessages;
}
very example app
#SpringBootApplication
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(App.class, args);
RefSignalementRepo repo = context.getBean(RefSignalementRepo.class);
RefSignalement obj = new RefSignalement();
obj.codeSignalement = "1";
obj = repo.save(obj);
obj.refMessages = new ArrayList<>();
RefMessage message = new RefMessage();
message.codeSignalement = "1";
message.destinataires = "2";
message.message = "custom message";
obj.refMessages.add(message);
obj = repo.save(obj);
List<RefSignalement> objs = repo.findAll();
System.out.println(objs.get(0).refMessages.size());
EntityManager em = context.getBean(EntityManager.class);
List<RefSignalement> listRefSignalement = em.createQuery("SELECT p FROM RefSignalement p, RefMessage m", RefSignalement.class).getResultList();
System.out.println(listRefSignalement.get(0).refMessages.size());
}
}

Hibernate Map ID automatically from field

I have something similar to this:
#Entity
#Table(name = "claim", schema = "test")
public class Claim implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idClaim", unique = true, nullable = false)
private Integer idClaim;
#OneToOne(mappedBy = "claim", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JsonManagedReference
private ClaimReturnInfo claimReturnInfo;
#Column(name = "notes")
private String notes;
// Getters and setters
}
#Entity
#Table(name = "claim_returninfo", schema = "test")
public class ClaimReturnInfo implements Serializable {
#Id
#Column(name = "Claim_idClaim")
private Integer id;
#MapsId("Claim_idClaim")
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Claim_idClaim")
#JsonBackReference
private Claim claim;
#Column(name = "description")
private String description;
// Getters and setters
}
ClaimReturnInfo Id is not autogenerated because we want to propagate the Id from its parent (Claim). We are not able to do this automatically and we are getting this error: ids for this class must be manually assigned before calling save() when 'cascade' is executed in ClaimReturnInfo .
Is it possible to map Claim Id into ClaimReturnInfo Id or should we do this manually?
Even if we set this ID manually on claimReturnInfo and we can perform updates, we still get this error when trying to create a new Claim:
// POST -> claimRepository.save() -> Error
{
"notes": "Some test notes on a new claim",
"claimReturnInfo": {
"description": "Test description for a new claimReturnInfo"
}
}
In the ServiceImplemetation:
#Override
#Transactional
public Claim save(Claim claim) throws Exception {
if(null != claim.getClaimReturnInfo()) {
claim.getClaimReturnInfo().setId(claim.getIdClaim());
}
Claim claimSaved = claimRepository.save(claim);
return claimSaved;
}
I have tried using the following mappings and from your comments it was apparent that Json object is populated correctly.
I have noticed that the annotation #MapsId is the culprit.If you check the documentation of #MapsId annotation it says
Blockquote
The name of the attribute within the composite key
* to which the relationship attribute corresponds. If not
* supplied, the relationship maps the entity's primary
* key
Blockquote
If you change #MapsId("Claim_idClaim") to #MapsId it will start persisting your entities.
import javax.persistence.*;
#Entity
#Table(name = "CLAIM")
public class Claim {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "idClaim", unique = true, nullable = false)
private Long idClaim;
#Column(name = "notes")
private String notes;
#OneToOne(mappedBy = "claim", cascade = CascadeType.ALL, optional = false)
private ClaimReturnInfo claimReturnInfo;
public Long getIdClaim() {
return idClaim;
}
public String getNotes() {
return notes;
}
public void setNotes(String notes) {
this.notes = notes;
}
public ClaimReturnInfo getClaimReturnInfo() {
return claimReturnInfo;
}
public void setClaimReturnInfo(ClaimReturnInfo claimReturnInfo) {
if (claimReturnInfo == null) {
if (this.claimReturnInfo != null) {
this.claimReturnInfo.setClaim(null);
}
} else {
claimReturnInfo.setClaim(this);
}
this.claimReturnInfo = claimReturnInfo;
}
}
package com.hiber.hiberelations;
import javax.persistence.*;
#Entity
#Table(name = "CLAIM_RETURN_INFO")
public class ClaimReturnInfo {
#Id
#Column(name = "Claim_idClaim")
private Long childId;
#Column(name = "DESCRIPTION")
private String description;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Claim_idClaim")
private Claim claim;
public Long getChildId() {
return childId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Claim getClaim() {
return this.claim;
}
public void setClaim(Claim claim) {
this.claim = claim;
}
}

Jpa/Hibernate Composite Key with many to one and #IdClass detached entity

I have two entities Train and Station. They have #OneToMany relation on Timetable entity. In Timetable entity I have composite key on Train and Station as #ManyToOne.
All classes have setters and getters
Train
#Entity
#Table(name = "train")
public class Train {
#Id
#GeneratedValue
private Long id;
#Column(name = "type")
private String type;
#OneToMany(mappedBy = "train",fetch = FetchType.EAGER)
#OrderBy("curentnumber ASC")
private List<Coach> coaches;
#OneToMany(mappedBy = "train",fetch = FetchType.LAZY)
#OrderBy("arrivalTime ASC")
private List<Timetable> timetable ;
...
}
Station
#Entity
#Table(name = "station")
public class Station {
#Id
#GeneratedValue
private Long id;
#Column(name = "name", length = 16, nullable = false)
private String name;
#OneToMany(mappedBy = "station", fetch = FetchType.LAZY)
private List<Timetable> timetable;
...
}
Timetable
#Entity
#IdClass(TimetableId.class)
#Table(name = "timetable")
public class Timetable implements Serializable, Comparable<Timetable>{
#Id
#ManyToOne
#JoinColumn(name = "train_id", referencedColumnName = "id")
private Train train;
#Id
#ManyToOne
#JoinColumn(name = "station_id", referencedColumnName = "id")
private Station station;
#Column(name = "arrivalTime")
private Timestamp arrivalTime;
#Column(name = "departureTime")
private Timestamp departureTime;
...
}
TimetableId
public class TimetableId implements Serializable{
Long train;
Long station;
public TimetableId() {
}
...
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimetableId that = (TimetableId) o;
if (train != null ? !train.equals(that.train) : that.train != null) return false;
return station != null ? station.equals(that.station) : that.station == null;
}
#Override
public int hashCode() {
int result = train.hashCode();
result = 31 * result + station.hashCode();
return result;
}
}
I have service class too.
//Result timetable
Timetable addedTimetable = new Timetable();
addedTimetable.setTrain(new Train());
addedTimetable.getTrain().setId(Long.valueOf(trainid));
addedTimetable.setStation(new Station());
addedTimetable.getStation().setId(Long.valueOf(stationid));
try{
timetableRepository.create(addedTimetable);
} catch (Exception e) {
e.printStackTrace();
}
And some from GenericDAO
#Override
public void create(T entity) {
EntityManager manager = emf.createEntityManager();
try {
try {
manager.getTransaction().begin();
manager.persist(entity);
manager.getTransaction().commit();
}
finally {
if (manager.getTransaction().isActive())
manager.getTransaction().rollback();
}
} finally {
manager.close();
}
}
The problem is when I have #IdClass annotation and try to create new Timetable referenced on new Station() and new Train with existing id, then i get error for "detached entity passed to persist:entity.Station" in my dao level. But if i remove #IdClass annotation, then everything is good and Timetable creates correctly. I think that i make something wrong in TimetableId and annotations...
Please help me, i stack here.

hibernate: deleting entity in a one to many relationship

I have a following error:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`spindledb`.`section`, CONSTRAINT `FK_ftoru9cp83n512p9is8x3vo53` FOREIGN KEY (`scenario_id`) REFERENCES `scenario` (`scenario_id`))
Here are my classes:
Scenario:
#Entity
#Table(name = "scenario")
public class Scenario {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "scenario_id")
private int id;
#Column(name = "title", nullable = false)
private String title;
#NotNull
#DateTimeFormat(pattern = "dd/MM/yyyy")
#Column(name = "creation_date", nullable = false)
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate creationDate;
#ManyToOne
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name = "id", nullable = false)
private User user;
#OneToMany(mappedBy = "scenario", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Plot> plotList = new HashSet<Plot>();
#OneToMany(mappedBy = "scenario", orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Character> characterList = new HashSet<Character>();
#OneToMany(mappedBy = "scenario", cascade=CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
#OrderBy("sequence ASC")
private Set<Section> sectionList = new HashSet<Section>();
Section:
#Entity
#Table(name = "section")
public class Section {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "section_id")
private int id;
#Size(min = 4, max = 50)
#NotNull
#Column(name = "name")
private String name;
#NotNull
#Column(name = "type")
private String type = SectionType.TEXT.getSectionType();
#Column(name = "visibility")
private boolean visibility;
#NotNull
#Column(name = "sequence")
private int sequence;
#ManyToOne (cascade=CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
#JoinColumn(name = "scenario_id", nullable = false)
private Scenario scenario;
Controller:
#RequestMapping(value = { "/delete-{id}-scenario" }, method = RequestMethod.GET)
public String deleteScenario(#PathVariable int id) {
scenarioService.deleteScenarioById(id);
return "redirect:/home";
}
Scenario service:
#Service("scenarioService")
#Transactional
public class ScenarioServiceImpl implements ScenarioService {
#Autowired
private ScenarioDao dao;
#Override
public Scenario findById(int id) {
return dao.findById(id);
}
#Override
public void saveScenario(Scenario scenario) {
dao.saveScenario(scenario);
}
public void updateScenario(Scenario scenario) {
Scenario entity = dao.findById(scenario.getId());
if(entity!=null){
entity.setTitle(scenario.getTitle());
entity.setCreationDate(scenario.getCreationDate());
}
}
#Override
public void deleteScenarioById(int id) {
dao.deleteScenarioById(id);
}
Dao
#Repository("scenarioDao")
public class ScenarioDaoImpl extends AbstractDao<Integer, Scenario> implements ScenarioDao {
#Override
public Scenario findById(int id) {
return getByKey(id);
}
#Override
public void saveScenario(Scenario scenario) {
persist(scenario);
}
#Override
public void deleteScenarioById(int id) {
Query query = getSession().createSQLQuery("delete from scenario where id = :id");
query.setString("id", ""+id);
query.executeUpdate();
}
I understand that the problem is that there may be a Section that can not exist without scenario. Right now however section table in database is empty and I still can't remove Scenario. Thanks for advice
Deleting an entity via Query would bypass any Cascade settings you put via annotation.
I would suggest find the entity first by id, then delete the entity object:
Object scenario = session.load(Scenario.class, id);
if (scenario != null) {
session.delete(scenario);
}
use cascade=CascadeType.ALL with all #ManyToOne relations in class Scenario because if you are going to delete any Scenario from database it must not be referenced any where in data base.
the other way to delete is.
Serializable id = new Long(1); //your id
Object persistentInstance = session.load(Scenario.class, id);
if (persistentInstance != null) {
session.delete(persistentInstance);
}

Categories