Bugs discovered while writing this book
To demonstrate the dark side of JPA, I’ll point to a couple of bugs I discovered while writing this book (or those that lead to it). With a single ORM you have no choice to switch – with many you always trade some bugs (most of the relevant ones known to you already) for new bugs. If you have good test coverage, you may be able to make a qualified decision. Otherwise you’ll find the bugs later and randomly (or your users will).
Some bugs are closed already, I added them to show it makes sense to report them. Other bugs, on the other hand, give you the feeling it doesn’t make sense to report anything – they are open for years and nothing is happening. ORM and JPA are not different from any other projects and with open source implementation you’ll get some transparency at least. Not all bugs are reported by me originally, but if I participated, commented and created a test case they are included. In any case these are all bugs that were encountered on a single Java project.
- JPA 2.1 FUNCTION not supported? (Hibernate) I reported this bug after trying this JPA 2.1 feature successfully on EclipseLink. Hibernate provides custom functions in HQL/JPQL, but this requires additional configuration. Whether this is against the standard JPA 2.1 is questionable (section 4.6.17.3 on custom functions does not say “no additional configuration must be required” after all) but Hibernate’s grammar has also a bug and does not support function calls without arguments.
- Wrong SQL generated for delete with where crossing tables (Hibernate) Open in Apr 2015, but older linked bug was open in May 2012. Works on EclipseLink.
- Bad SQL result for method countDistinct with join from criteria API This got reported for EclipseLink, however, it is not a JPA 2.1 bug as the specification explicitly says: “The use of DISTINCT with COUNT is not supported for arguments of embeddable types or map entry types.” It is just so unexpected it got reported by someone. This is a big blow for many reasons. You either cannot use embeddables, or you cannot count such cases in the query. This means you actually have to list it (you can use DISTINCT then) and then check the size of the result. This may not be possible for long lists. Finally, while in SQL you can go around this using SELECT in FROM clause, JPA does not allow this (yet). By the way, Hibernate supports this (I mean distinct in count, not select in from clause), even against explicit “not supported”. It seems to be a violation of JPA specification (although handy) and it may become a problem when migrating to another provider. (Question is what “not supported” means exactly in terms of words like MUST or MUST NOT used in RFCs. I understand leaving the feature in your ORM, maybe some compatibility switch/property could control the behaviour.)
-
Stream API gets no results (EclipseLink)
This one is fixed already. It caused that streams based on lazy lists were empty. It was easy to
work around it – just stream the copy of the list – but it was annoying and unexpected. Reason
for the bug was the fact that
IndirectList(lazy list implementation) extended fromVector, (probably some legacy reasons, JPA does not explicitly mentionsVectoras specially supported collection), but ignored invariants of the superclass and itsprotectedfields. This shows two things – first, when you extend something you should follow through completely with its contract, and second,Vectorwas badly designed back in 1995 or so. Oh, and third, prefer delegation instead of extension, of course. That way you can often avoid both previous pitfalls. - Abstract MappedSuperclass in separate JAR is not weaved statically (EclipseLink) This relates to modularity and may be fixed in version 2.7.
-
JPQL Case operator is not thread-safe
(EclipseLink)
CASEin queries cause randomly eitherArrayIndexOutOfBoundsExceptionin EclipseLink stack orSQLExceptionbecause the CASE clause is serialized wrongly in the SQL. As a concurrency problem it shows very randomly, more likely for bigger/longer queries. No response from EclipseLink team so far, so we just synchronize around queries where we absolutely need to useCASE. -
H2 dialect generates FOR UPDATE before LIMIT/OFFSET
(EclipseLink) This one is about particular database vendor SQL support. In H2 you need to say
FOR UPDATEafterLIMIT/OFFSETcombo, but EclipseLink does not respect this syntactic detail. Fix is currently available only as a patch, you can implement your own customH2Platform(very small class) and use that one. -
Deleting in a ManyToManyRelation with a JoinTable with referencedColumn != name in JoinColumn is
not working (EclipseLink) This one is rather
tricky and we hit it when we migrated one table from composite PK to a single one. First we just
introduced new
IDENTITYcolumn, we marked it as@Idin entity and everything worked fine… except for a@ManyToManycollection that was mapped using the two@JoinColumnsfrom the original id (and in DB it was still an ID). This collection did not delete the entries from association table (not mapped as an entity). This actually did not relate to the number of columns, merely to the fact that they were not@Idcolumns anymore. JPA specification does not mention any exception like this. -
ON for LEFT JOIN across association tables not generated in SQL
(EclipseLink) When using additional ON conditions for LEFT JOIN these don’t get generated into
SQL if the LEFT JOIN follows
@ManyToManyor@OneToManywith association table. Works fine if the collection is mapped by a single FK on the entity contained in the collection. Workaround is to explicitly map the association table or using different way to get to the result – possibly more queries, because LEFT JOIN is difficult to rewrite without losing some results. Should work according to specification and works fine with Hibernate. -
Coalesce ignores converters (EclipseLink)
When you want to coalesce value of converted column like
LocalDateto some default value (e.g.LocalDate.now()) the query fails on JDBC driver level (JTDS), or later when it encounters a row where it needs to apply the default (in H2), or possibly succeeds if no such row is found – which is probably even more sneaky. In any case, value is passed into JDBC without conversion and is treated as raw object, not as a date type. Funny enough, if we try to call it withjava.sql.Dateinstead, EclipseLink complaints right away that the types in coalesce are not compatible. Works fine with Hibernate.