Extending the problem case
After meeting with my advisors I extended the document posted previously. I added a more general chapter on the problems that occur when performing the refactorings and I tried to direct this problem towards a solution. Afterwards I concluded by writing down what the solution should be able to do to fix the refactorings for this annotations.
Also more information has been added so the documentation is more complete.
Case and point.
Now that many of the refactorings have been analyzed, and their behavior in the presence of the hibernate annotations has been mapped out we can start showing that there is a real problem present.
For this I will create code that really shows us what goes wrong in the case of refactoring. I will do this structured and for each refactoring separately.
Abstract: This document has only an informative purpose, it is just a document that shows the findings of a long research. The research was about refactorings and how annotations behave when refactored. The main problems that can occur when refactoring code that has been annotated with the annotations used by hibernate.
After the introduction the major refactorings that really break down the code are explained and examples are added to proof the point.
Download the document here: http://www.megaupload.com/?d=H91WVKEG
Analyzing the outcome of refactoring the ManyToOne annotation
For being complete let’s analyze some of the refactorings in the presence of the ManyToOne annotation.
For many cases the results are the same as for the Id annotation and for that reason only the differences will be written down in this blog.
When we move the method (but keep the original) this can influence the behavior. If the Target is not a class that will be mapped to the database no problem occurs but when it is then we should now that there is a getter added and therefore there must be a field present. If so the relation will change in the database.
When we rename the getter method we encounter problems, the program will not run anymore because the field can not be accessed anymore and therefore not mapped correctly.
The rest of the refactorings will maybe alter the behavior but it is the same as for the Id annotation.
Refactoring hibernate in intelliJ
IntelliJ says it as extensive hibernate support, that was a big enough reason to take a look at how it handles refactorings when hibernate annotations are in use.
The most easy one to check is the rename refactoring. When we have a class that represents an entity, the refactoring should add a name to the annotation to preserve behavior. This doesn’t happen so the first and most easy tests failed. This makes me think that other refactorings will not be more supported. But to be sure let’s take a look.
Second refactoring that I want to try is the extract method. Example case we are trying to extract a method from a method in an Entity class. When this new method is a getter then it should have Transient. This does not work in IntelliJ and so after the refactoring is done it gives us an error at runtime.
Third we will check for the push down method and field. Also here the refactorings are just executed which can change the database to in certain cases.
Forth we will take a look at pulling up methods and field. When we pull up a field this works off course because no special action is required. But when we then want to pull up a getter we need to make the superclass a MappedSuperClass this is not done in IntelliJ and therefore the behavior is altered.
And the last one that might be interesting to analyze in intelliJ is the extract superclass. When we extract a class and pull up getters of certain properties the superclass should be a MappedSuperclass and this is not the case in intelliJ.
Conclusion: We can safely state that however intelliJ has extended hibernate support it does not include making the refactorings behave differently when there are annotations present.
Refactoring the MappedSuperclass annotation.
This annotation stands on a class and defines just like the Entity annotation that a class is persistent. The difference however is that a MappedSuperclass does not represent a table in the database but only holds properties that can be mapped for Entities that extend the MappedSuperclass.
For this reason I think most of the refactoring behavior will be the same as for Entities but let’s take a look.
Extract method: If we extract a new method in a MappedSuperClass we might want to make that method Transient, that is if the signature implies that the method is a persistent method.
Inline method: When we inline a method in a MappedSuperClass nothing really changes. So no further action is required and the behavior will be persistent. However if the refactoring deletes the original method and that method was used to map properties into the database then this will give errors.
Refactoring the Transient annotation
The transient annotation defines that a method which is found back in a persistence class and has the correct format to be a persistent property should not be a persisted.
It makes no sense using it in classes that are not persistent and therefore the behavior depends on if the classes or annotated as persistent or not.
Also here some of the refactorings have no influence whatsoever on the Transient annotation because it is placed on methods. For this obvious reason we will not discuss refactorings that concern the fields.
Extract method: When we extract a method that has been annotated with transient we might want to consider to make the newly extracted method also Transient. Now if the name of the new method does not imply it is persistent than it does not need to be annotated to preserve the behavior.
Inline method: When a method get inlined the invoker of the method should not get the Transient annotation. If he didn’t had it before the refactoring there is no reason whatsoever to annotate it after. The behavior won’t be changed. If the original method does not get deleted it should just stay annotated as before the refactoring.
Refactoring the Id annotation
After analyzing the Entity annotation we will now take a look at the Id annotation. This annotation is placed an the getter of the Id property of our POJO class. Here we notice that some of the refactorings are irrelevant and will therefore be neglected.
Extract method: Normally this will not give any problem. The Id annotation should stay present on the getter method. Now if the extracted method is a getter (preceded by get*) and return something different than void then we want to annotate the new method with the Transient annotation.
Inline method: We have to possibilities with this refactoring. First we can leave our method present and then now problem occurs when we leave the Id annotation.
Second we can delete the method. Then the behavior is changed because hibernate will not be able to put an id in the table of the object and that gives errors. No real solution can be found. In some cases putting the @Id on the new method which should be renamed can preserve the behavior. But then we don’t have just an inline method refactoring anymore.
Refactoring the Entity annotation
After configuring eclipse and documenting it correctly I can finally start to think about the refactorings with the entity annotation.
Because we are talking about an annotation that can only be placed on a class we might think we should only consider the refactorings that could affect the class annotation. But I will show below that other refactorings might not be behavior preserving in the presence of the Entity annotation. Later on when looking at other annotations we might want to check the behavior in case of combinations of annotations. For example when an ID annotation changes of class, the Entity annotation must be present on the target class of that annotation. But first let’s focus on the way the entity annotation behaves on his own.
Extract method: This is a refactoring that does not seems to have an influence on the class annotation but because of the behavior of the class annotation it is relevant. For example when we have a getter function where we want to have the name in lowercase. And we extract that behavior because we might need it in our application. We use the extract method refactoring and create a new method.
When we then run our Application we notice that hibernate throws an error because it can not map that new function. This is because in an entity class all the methods are mapped to the database unless they are annotated as transient.
Getting acquainted with hibernate
After a little week where I could not work a lot for my thesis today I started working again.
Because I will check the behavior of annotations from JPA when the code is refactored I think it is important to learn how it works. A good framework that implements this JPA is hibernate. So today I worked to check how this can be configured easily. I found it poorly documented everything was explained on an higher level. For people who already know well how to work with EJB and Spring for example.
After combining a lot of knowledge acquainted on different websites and tutorials I figured out it was not that hard to get it to work. Because of this I decided to get an easy to understand hibernate tutorial on how to get it to work. So people who are just beginning to program can understand it either.
So coming up there will be an easy to understand hibernate tutorial.
Download the tutorial pdf here (MU)
Looking for interesting annotations
Today I checked different libraries to check which annotations are used in them, I wrote them down and I will check later which one might be interesting to check the expected behavior.
The libraries that I checked today are Hibernate, JUnit and JSR 181. The libraries I want to check later are Spring and type annotations. Type annotations will be checked for being complete.