I see there has been
a question regarding Hibernate and Scala a short time ago. Unfortunately, there were no answers. My question is somewhat similar, so I will try to pose the questions in such a way that they can be answered by a Hibernate expert who does not know Scala.
Scala collections are a bit different than Java collections. While superficially, they have similar names and do similar things, Scala collections are categorized much finer. Scala collections do not implement the Java collection interfaces. Therefore, when you use Scala and Hibernate, you are heading for trouble.
I have already seen a workaround in practice: programming in Scala, but using the Java collections. This feels highly "unnatural", because unlike the Java collections, Scala collections fit perfectly into the Scala programming style. When using Java collections in Scala, many of the advantages that Scala has will vanish. Also, how should one decide if it is okay or not to use Scala collections in a class? You need to know beforehand if you intend to store instances of this class via Hibernate or not. But this somehow perverts the "as transparent as possible" attitude of Hibernate. So, I actually don't see the idea of using Java collections in Scala classes that one intends to store via Hibernate as a workaround, it is more of a hack.
Now, I have of course already tried UserCollectionType. Since I am rather new to Hibernate, and what I try to accomplish (introducing Hibernate to a different kind of collection type) seems to be expert stuff, I am constantly running into traps here. And apart from one very good article on JavaLobby, I did not find any useful tutorials or comprehensive documentation on UserCollectionType. So I finally came to this forum.
My first try for implementing a custom collection type is aimed at a mutable set - should be fairly easy, I thought. So I implemented it, fixed some exceptions, and now I arrived at a problem that seems to require a lot of effort to solve. When I try to store a class that has the user collection type as a member, the following happens:
- Hibernate does some magic.
- Hibernate calls the "wrap" method in my user collection type, and in return gets an instance of PersistentCollection. (PersistentSet, to be more precise.)
- Hibernate actually tries to assign the previously obtained PersistentSet instance back to the member of my persistent object. This of course fails, because PersistentSet does not implement the required interface scala.collection.mutable.Set.
The last step astonished me! Hibernate's UserCollectionType seems to be founded from the assumption that every user collection type can be substituted with an instance of PersistentCollection. This seems pretty invasive to me. Isn't there a way around this?
I tried to make a subclass of scala.collection.mutable.Set that implements PersistentCollection. This is really painful because both interfaces contain many methods. I tried using Hibernate's AbstractPersistentCollection class as a superclass, but found that one of its method collides with Scala's set interface: The "empty" method. For Hibernate, it returns a boolean, but in the Scala interface, the method with the same name returns another set. To avoid this clash, it seems I have to do the complete PersistentCollection interface implementation by myself. Of course, I can copy many methods from the hierarchy of PeristentSet and hope that there will be no further fatal method clashes. But that's not really a good and clean way of programming, is it?
It gets worse with immutable collections. Methods like "add" or "remove" make no sense for unmodifiable collections. When adding to an unmodifiable collection, you don't modify the collection itself, but create a new unmodifiable collection with all the elements of the other collection, plus the one that you wanted to add. Then, you re-assign the reference to the collection to the newly created collection. Is this something that UserCollectionType would be able to handle?
On the other hand, I have a really bad feeling when I try to create those classes. It feels as if I try to fake my way into a feature that was not planned to be used the way I try to use it. Basically, I can imagine an easier way to solve the problem. It would require a rather simple change to the core of Hibernate. But probably Hibernate has grown so huge that such a change would be difficult to work into it, and probably there are some complications that I currently am not aware of. Anyway, just for completeness, this is my idea for the easier change:
I would like to tell Hibernate (via some kind of configuration): "Hey Hibernate, whenever you encounter a relational mapping of something that must be a collection but that you don't recognize as a collection, you throw an exception. Please, in the future before throwing an exception in such cases, do the following: Check if the instance in question is of a subtype of scala.collection.mutable.Set (just as an example). If so, here I give you a simple class that can convert from scala.collection.mutable.Set to java.util.Set and vice versa. Use that converter when marshalling and unmarshalling such instances. So you don't have to throw exceptions any more in such cases, and you can internally work with Java collections, something that you are really good at."
I almost feel tempted to modify the Hibernate code. But I just don't do so right now because this might lead me to a lot of trouble.
Please, Hibernate experts, is there anything you can help me with? I am pretty sure that the combination of Scala and Hibernate is currently a matter of great and growing interest.