persistence
persistence copied to clipboard
void EntityManager.delete(Object primaryKey)
According to the current JPA specification, deleting object is possible by the following ways:
- em.remove(em.find(pk)) – Bad: First loads an instance from the DB into the cache, just for the sake of deleting it.
- em.createNativeQuery(sql) – Bad: SQL string has to be parsed at runtime, and can contain typos not detectable at compile time.
- em.createQuery(jpql) – Bad: JPDQ string has to be parsed at runtime, and can contain typos not detectable at compile time.
- em.createQuery(CriteriaDelete) – Bad: Rather complex java code to write even for simple cases.
Hence I would like to propose the addition of one method:
void EntityManager.delete(Object pk) throws PersistenceException
This method directly issues SQL DELETE WHERE id = ? command to the database. If the JDBC driver answers with "zero rows affected statement", then EntityNotFoundException is thrown. Otherwise (i. e. "one row affected" is reported by the JDBC driver) the object is evicted from the cache.
Benefits:
- Very simple to write and super-easy to understand even for beginners
- Very fast to execute
- Safe against typos
- Typesafe even for compound keys
- No useless SQL SELECT roundtrip
- Correctly reports whether there was a problem, whether there was success, or whether there was nothing to delete.
From the aspect of API completeness, this method is really missing since the beginning of JPA, so IMHO it fits perfectly into a maintenance release.
Affected Versions
[2.2]
- Issue Imported From: https://github.com/javaee/jpa-spec/issues/147
- Original Issue Raised By:@glassfishrobot
- Original Issue Assigned To: @ldemichiel
@glassfishrobot Commented Reported by mkarg
@glassfishrobot Commented jgrassel said: How would this address foreign key constraints referencing the row to be deleted? The database would throw a constraint violation on flush/commit, which would be a different error than what EntityNotFoundException covers.
@glassfishrobot Commented mkarg said: EntityNotFoundException is only thrown when Statement.executeUpdate(DELETE) succeeds but returns a value of zero.
In case the same method throws SQLException, like when a foreign key constraint is violated, a different PersistenceException (in particular NOT EntityNotFoundException) is thrown. In case of inability to commit, typically it would be RollbackException.
@glassfishrobot Commented neilstockton said: A "single SQL" would not always be possible, since inheritance strategy would imply that an object can be stored in multiple tables.
The "pk" passed in is what exactly? a Long? a IdClass instance? what if the user had a single PK field and hence had no IdClass? It would need to know the Class of the entity as well (just like em.find(Class, Object))
As pointed out above, cascading would not be possible without pulling the object back into memory, and having this method ignoring cascading would be utterly inconsistent with em.remove(Object).
@glassfishrobot Commented This issue was imported from java.net JIRA JPA_SPEC-147
@mkarg Commented
-
With "single SQL" I meant "single server roundtrip", not literally "single SQL command". Implementations could set up SQL batches dynamically to reflect the different SQL sequences needed to delete a complete inheritance hierarchy of tables, but a single batch invocation still is a single roundtrip.
-
Updated propsal:
void EntityManager.delete(Class<T> entityClass, Object primaryKey) throws PersistenceException
-
I do not see why cascading should not be possible without pulling the object into memory first. Can you please outline this a bit?
@mkarg Commented @ldemichiel Can you please set me (@mkarg) as the original reporter of this issue, so I will gain edit rights for this issue? Thanks! :-)
@mkarg Commented @lukasj I like to invite you to join this discussion. :-)
@mkarg I was the original reporter.
em.remove(em.find(pk))
– Bad: First loads an instance from the DB into the cache, just for the sake of deleting it.
Maybe em.remove(em.getReference(cls, pk))
could be used to avoid loading the instance, but it may also just load the full instance at a later time although only the PK is needed and known beforehand. Anyway, it should be simpler than that.
Maybe
em.remove(em.getReference(cls, pk))
could be used to avoid loading the instance,
Indeed, exactly this is already supported in Hibernate 6, and I think it's a fine solution.
Anyway, it should be simpler than that.
Really, I think that's quite simple enough.