Let’s take a traditional MVC Spring project and convert it to Spring Data. Does it lead to better and more minimalistic code.? Let’s discover if it does
This document examines a traditional Spring MVC project’s DAO layer. Then it converts it Spring Data and examines the code and its maintainability.
2. Traditional Spring MVC Data Layer
From Figure 1, we can see that:
- This application directly accesses three tables
- A VERS (vehicle exception reporting system) table — VersDAO
- A USER table — UserDAO
- An Incident table — IncidentDAO
- In order to reduce complexity and keep DRY, the code author templatized the basic CRUD capabilities in the BaseDAO<T> class.
Figure 1. Standard DAO Layer.
2.2. Real (must be watered and fed) Code
Actual code exists behind the methods (See Figure 2,)
- . getById ()
- . getAll()
Figure 2. Real “must be maintained” Code.
2.3. Hibernate Mapping Files
From Figure 3 we can see the Hibernate mapping files associated with this application.
- Central location
- Centrally located
- Auto-generation causes weirdly named files reflective of the tables they came from
Figure 3. Multiple Mapping Files.
2.4. Sample Hibernate Mapping
As shown in the following redacted XML stanza for a single mapping file, Hibernate by nature generates verbose standalone XML descriptions.
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<!-- Generated Dec 3, 2013 9:16:38 AM by Hibernate Tools 3.4.0.CR1 -->
<class name="com.uprr.autos.irsvers.domain.Vers" table="INL_VERS_EXCP">
<id name="versExcpId" type="string">
<column name="VERS_EXCP_ID" length="38" />
<generator class="assigned" />
<property name="excpEvntDate" type="date">
<column name="EXCP_EVNT_DATE" length="7" not-null="true" />
<property name="excpCrtnDate" type="date">
<column name="EXCP_CRTN_DATE" length="7" not-null="true" />
<set name="inlVersExcpVins" table="INL_VERS_EXCP_VIN" inverse="true"
lazy="true" fetch="select" cascade="all">
<column name="VERS_EXCP_ID" length="38" not-null="true" />
<one-to-many class="com.uprr.autos.irsvers.domain.VersExceptionVin" />
2.5. Sample Generated Code
Figure 4. The Untyped Hack.
3. Updated to Spring Data
3.1. JPA Annotations
Figure 5, shows how the ease with which we map Java object attributes to DB table columns. This is an improvement from original hibernate generated code in that Java Set’s are now type cast removing pragmas around suppressing warnings and knowing the actual object types returned in the collections.
- Annotation closest to affected code
- IDE contextual help
- JPA annotations spread all over code
Figure 5. Simple JPA Annotations.
Figure 6, depicts the typed data types that allows to refer to associated DB columns and map them to Java “contained” object sets.
Figure 6. Cross Table JPA Annotations.
3.2. Spring Data Repositories
Figure 7 shows the data repositories we created to replace the custom DAOs. Note that these interfaces are sub classed from the JPARepository interface.
Figure 7. Spring Data Repository Layer.
From Figure 8 we can see the plethora of functionality that we inherit from
Figure 8. JPA Repository ‘Free’ Methods.
3.3. Data Repositories Details
Figure 9 depicts the ease with which get the “free” functionality.
Figure 9. Easy JPA CRUD Functionality.
3.3.2. Not so easy
Spring Data makes it easy to get free functionality if you have a good domain model to DB mapping. However, sometimes we have to synthesize a domain object from several DB tables. This is harder and more “hacky”. The solution is to use Spring Data’s Query functionality. See Figure 10. Use JPA @Query annotation. This identifies both the new method name and the SQL query string to execute. Note that the returned types will not match the returned data hence we get an array of objects returned.
Figure 10. Synthetic Domain Objects.
In Figure 11, we can see that all we get back are generic objects. We will have to re-compose these into real ContactInformation domain model objects.
Figure 11. Generic Object Mapping.
Figure 12 illustrates the non-intuitive code to convert from generic objects to a list of real domain model objects.
- Initialize the returned list and the loop counter to traverse generic list
- Get generic object out of list and cast it to an array of Objects
- Use well-known indices to get the following values
- User ID
- Contact Group ID
- Contact ID
- Update the ContactInformation object
- The integer value of the User ID
- User name and UP user id from well known safe indices
- Provide null value of CG id or its integer representation
- Provide integer value of Contact id
Figure 12. Custom Object List to Domain Model Mapping.
3.4. Changes made to Services
In Figure 13, we are trying to determine what code changed in the service layer when we switched from DAOs to the spring data repositories. At first glance, it appears that we added much more code. However, notice that in the original version it was using methods provided by the generic template class.
Figure 13. Compare to template instance.
From Figure 14, we see that our repository object is auto wired. In addition, other than a few naming changes to comply with the Spring data naming conventions, the changes are trivial. Converting this BaseServiceImpl template class to use the repository was outside the scope of this change and the topic of a future refactoring.
Figure 14. Real Service Change.
3.5. Using Spring Boot
I really had a high want to use the Spring Boot with its opinionated dependencies. See Figure 15. However, could not quite get the “magic” to work for this JPA SQL project.
Figure 15. Spring Boot Simple Dependencies.
3.6. Or not Using Spring Boot
The result was a more “traditional” Spring dependency list as depicted in Figure 16.
Figure 16. Standard Spring Data POM.
 Don’t Repeat Yourself
 Interestingly the figure noted came from a Neo4J project that used this simple dependency stanza.