4

In order to keep transfered data small I created two entities for my files in the database. The fileheader to keep some general information about the files and the fileblob, including fileId and the blob. Often, I only need to ask for general fileinformations.

So I need to load the fileblobs lazily.

As I learned in this discussion and that discussion. this could be achieved with optional = false. It works perfect to load the fileblobs lazily. Unfortunately it affects save by cascade.

So here is my attribute in the Fileh.class for the blob:

@OneToOne(mappedBy = "fileh", targetEntity = Fileblob.class, fetch = FetchType.LAZY, optional = false)
@org.hibernate.annotations.Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK })
private Fileblob fileblob;

If I now save a fileh with attached fileblob, this error is thrown:

org.hibernate.id.IdentifierGenerationException: null id generated for:class Fileblob

if i switch from id-generation strategy "identity" to "increment" this error is thrown:

ERROR SqlExceptionHelper:147 - Cannot add or update a child row: a foreign key constraint fails (`CORE`.`FILEBLOB`, CONSTRAINT

FKFILEBLOB412557 FOREIGN KEY (ID) REFERENCES FILEH (ID)) Query is: insert into CORE.FILEBLOB (FILEBLOB, ID) values (?, ?)

So there is a problem with generating the id... If i now turn off save by cascade my attribute looks like this.

@OneToOne(mappedBy = "fileh", targetEntity = Fileblob.class, fetch = FetchType.LAZY, optional = false)
private Fileblob fileblob;

In order to save now, I have to call

persistentSession.saveOrUpdate(fileh);
persistentSession.saveOrUpdate(fileblob);

Is this not just what CascadeType.SAVE_UPDATE is supposed to do? Why is this working for "manual cascading" but not automatically?

P.s.: To complete my example here the counterpart in fileblob.class

@PrimaryKeyJoinColumn   
@OneToOne(targetEntity=Fileh.class, fetch=FetchType.LAZY)   
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK}) 
@JoinColumns({ @JoinColumn(name="`ID`", referencedColumnName="`ID`", unique=true, nullable=false) })    
private Fileh fileh;

@Column(name="`ID`", nullable=false, insertable=false, updatable=false, unique=true)    
@Id 
@GeneratedValue(generator="FILEBLOB_FILEHID_GENERATOR") 
@org.hibernate.annotations.GenericGenerator(name="FILEBLOB_FILEHID_GENERATOR", strategy="foreign", parameters=@org.hibernate.annotations.Parameter(name="property", value="fileh")) 
private int filehId;
Maciej Kowalski
  • 21,760
  • 10
  • 42
  • 54
Vincent
  • 189
  • 1
  • 12

2 Answers2

2

I encountered exactly the same issue and tried to solve it for several days! It turns out it's an issue which has lied in Hibernate's bug system for more than 2 years!

Though the answer here does help resolve the issue, we are forced to give up using Persist Cascade from the parent Entity (the inverse side, also the side with mappedBy defined)

Please see this link: https://hibernate.atlassian.net/browse/HHH-9670

If you want to apply lazy loading to a OneToOne relationship, and be able to do Persist cascade as usual, please leave a comment to show your concern for the issue!

1

Well first i would use JPA cascading instead of hibernate one:

@OneToOne(mappedBy = "fileh", fetch = FetchType.LAZY, optional = false)
private Fileblob fileblob;

And the Fileblob, well i think your configuration can be a bit simpler without those generators and stuff(assuming that the id should be actually a foreign key pointing to the Fileh.id).

@Column(name="`ID`", nullable=false, unique=true)    
@Id 
private int filehId;

@JoinColumn(name = "id", referencedColumnName = "id")
@OneToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@MapsId
private Fileh fileh;
Maciej Kowalski
  • 21,760
  • 10
  • 42
  • 54
  • Hi Maciej, thank you for your answer! If I switch to JPA cascading, it is not cascading, the fileblob is missing in the database if I only save the fileheader. Am I missing something else? – Vincent May 03 '17 at 08:40
  • how do you assemble the entities before save? Also check the update @Column(name="`ID`", nullable=false, unique=true) – Maciej Kowalski May 03 '17 at 08:56
  • I call the setters viceverca: fileh.setfileblob(fileblob) and fileblob.setfileh(fileh). The @Column is updated as you considered. – Vincent May 03 '17 at 09:10
  • try to set only this side: `fileblob.setfileh(fileh)` and save the blob entity `saveOrUpdate(fileblob)` – Maciej Kowalski May 03 '17 at 09:22
  • unfortunatly just the same behaviour. I´m afraid JPA cascading has no affect, I just wonder why.... – Vincent May 03 '17 at 10:31
  • ok i have went through my notes and updated the answer. keep saving only the fileblob – Maciej Kowalski May 03 '17 at 10:50
  • Hooray, seems to work right now. I will just switch this, so I only have to save the fileh. Just wonder what was wrong in my approach. The missing @MapsId? Using hibernate annotations? Will do some experiments and then edit my post. Thx for your answer! – Vincent May 03 '17 at 11:04