Instance save transient before

Hi!

I guess you missed me :rofl:

Upgraded my Hibernate core from 6.5.2.Final to 6.6.0.Final and got this exception while trying to delete a record using repository method:

TransientObjectException
persistent instance references an unsaved transient instance of ‘com.thevegcat.app.entity.article.Article’ (save the transient instance before flushing)

The code of my app is the same and the only change is dependency upgrade previously mentioned.
When I look at the migration guide, there is no mention of transients.
Also I don’t understand the message because I expect transients not to be persisted if I had one, but the message asks me the opposite - to persist transients.
No matter my understaning or maybe better to say lack of understanding - I’m not sure is it a bug or a feature and the main question is should change my code to handle this new behaviour or not.

Thanks a lot!
Hrvoje

1 Like

You’re probably being hit by a bugfix that took place in version 6.6.0 that involved transient-checks for deleted entities that are still referred to from associations of other entities in the persistence context.

You’re probably forgetting to null-out some association after deleting the target entity.

2 Likes

I fixed it:

  • set Article property priceInfos (which is List<PriceInfo>) to null,
  • delete all PriceInfo(s) by Article ID from PriceInfo repository,
  • delete Article from Article repository.
@Override @Transactional(readOnly = false)
public void deleteById( final Integer id ) {
	final Article entity = getById( id );
	if (entity == null) {
		throw new VegArticleNotFoundException(
			ENTITY_NAME + " not found (ID = " + id + ").");
	}
	entity.setPhotos( null );
	this.photoService.deleteByIds(
		entity.getPhotos().stream().map( Photo::getId ).toList()
	);
	entity.setPriceInfos( null );
	this.priceInfoService.deleteByArticleId( id );
	this.articleRepository.delete( entity );
}

@Override
public void deleteByArticleId( final Integer articleId ) {
	final JPAQueryFactory jpaQueryFactory =
			new JPAQueryFactory( this.entityManager );
	final QPriceInfo priceInfo = QPriceInfo.priceInfo;
	jpaQueryFactory
		.delete( priceInfo )
		.where( priceInfo.article.id.eq( articleId ) )
		.execute();
}

Having this EXACT same behavior after moving to 6.6.1.Final and doing some much needed regression testing of formerly working code. It seems like a false positive on the removal of a ‘leaf’/child end of a @OneToMany relationship. Been trying to address this for hours over a few days. Quite frustrating. And just discovered your post. Maybe looking at the underlying code change will give me clue how to mitigate this.

Did I mention how frustrating this is? :confused:

Interesting, I just got so frustrated with this that I went back to 6.5.1.Final and my code works just fine. sigh

My scenario is a fairly generic @OneToMany + @ManyToOne scenario and it’s the removal of the child/leaf in particular that throws the “save the transient instance before flushing” exception.

I need to look at the change log between 6.5.1.Final and 6.6.1.Final and see what might be causing my situation. But for now I have to get things done, so back to 6.5.1.Final it is :slight_smile:

The code I provided as an example has an error. Two lines are in a wrong order but I can’t edit original post.

After upgrading to 6.5.2 (through Quarkus), I get this when some entities, returned from a query but untouched, are linked to an entity I delete. It’s quite impossible to track all queried entities and change their relations, and that kind of check is counter-productive. I would understand a check on change.

Here is our use case, which I’m pretty sure is legit (just changed entity names for example purposes):
Assuming an Article→Author entity graph.

  1. exists(ArticleSearch) method checks for articles matching certain criteria’s, implemented by loading 1 of them and return true if there was any result
    (effect: loads 1 article in persistence context)
    [… unrelated business]
  2. delete(Author):
    2)a) criteria update to set Article to another Author so none is matching the Author to delete
    (no effect on persistence context, as expected)
    2)b) delete author

→ then we get that exception:
org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of ‘be.fiscalteam.nitro.domain.entity.ThirdPartyEntity’ (save the transient instance before flushing)
at org.hibernate.engine.spi.CascadingActions$9.cascade(CascadingActions.java:382)
at org.hibernate.engine.spi.CascadingActions$9.cascade(CascadingActions.java:372)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:570)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:492)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:253)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:192)
at org.hibernate.event.internal.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:169)

Expected behavior: Hibernate should let me delete the Author instance. I didn’t change any Article in persistence context, it’s just a side-effect from another query (I implemented it in a better way now, but still).

This is expected behavior. You have to manage both sides of an association. You can’t just remove an author, you also have to ensure that objects within the persistence context don’t point to that author anymore.
If you change the Article through a DML update query, you should probably clear the persistence context or at least try to detach the entity that you’re updating from the persistence context before or afterward that operation.