Spring and Hibernate with Annotations

Web Components

Now let’s build the web application interface side and show how the model and DAOs work to make a full application.  Our web application will have a context of “departments”, making all the URLs to the app http://localhost:8080/departments/[foo].  This assumes that you are using Tomcat (or other application server) on localhost on port 80.  If you aren’t familiar on how to set this up on Eclipse, you can read through my tutorial using Spring Hello World Web Application for one method.

Our user interface is extremely simple.  We have an index page with links to “manage employees” and “manage departments”.

depts-index

Manage employees has a list of known employees, and links to edit or delete them.  It also provides the “Add New Employee” button.

depts-manageEmployees

Edit employee lets you change the employee’s name and change which departments they are in.  Since one employee can be in many departments, we’ll use a list of checkboxes to allow multiple selection.

depts-editEmployee

Add employee is the same as edit employee, but with a different title (and a message about not having an ID yet).

depts-addEmployee

The department screens are quite similar to the employee screens.

depts-manageDepartments

depts-editDepartment depts-addDepartment

The delete functions don’t have an interface, but they do ask “Are you sure?” before deletion.

In the web layer, We are going to continue with our theme of using MappedModels instead of Employees and Departments when it makes sense to do so.  But for now, let’s start with the simplest web component, the IndexController.

/src/main/java/com/technologicaloddity/departments/controller/IndexController.java

package com.technologicaloddity.departments.controller;

import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
public class IndexController {

    @RequestMapping("/index.html")
    public String showIndex() {
        return "index";
    }
}

Very simple. Here’s the line-by-line:

  • Lines 1-4: Standard Java package and import statements.
  • Line 6: We are using Spring’s @Controller stereotype, which provides functions like URL mapping and request parameter handling.
  • Line 7: The start of the controller.  If you are used to previous versions of Spring, you may be surprised that IndexController doesn’t extend something like AbstractController.  The @Controller stereotype makes this unnecessary since Spring 2.5.
  • Line 9: The @RequestMapping annotation tells Spring that this function should be invoked for the given URL.  Note that URL is relative to the webapp context.  So, in this example, @RequestMapping(“index.html”) maps to http://localhost:8080/departments/index.html in a browser (assuming you are using Tomcat on localhost on port 8080).  Since we have index.html listed as our welcome page in web.xml, this method will also be called for the “root” of the webapp: http://localhost:8080/departments/
  • Lines 10-12: Very simple request handler.  The String that is returned is the name of the view that we want to show.  Since we aren’t adding anything programmatic to the view, we don’t need any incoming parameters (we’ll see an example of that later though).  We return simply “index”, which in turn is processed by our ViewResolver from dispatch-servlet.xml.  That ViewResolver says, given “foo”, look for the actual view in WEB-INF/jsp/foo.jsp, so, “index” actually returns WEB-INF/jsp/index.jsp.
  • Line 13: End of class.

Here’s the actual view that is returned:

/src/main/webapp/WEB-INF/jsp/index.jsp

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
<head>
<title>Index</title>
</head>
<body>
<h1>Index</h1>
<hr>
<p>
<a href="<c:url value='/manageDepartments.html'/>">Manage Departments</a>
</p>
<p>
<a href="<c:url value='/manageEmployees.html'/>">Manage Employees</a>
</p>
</body>
</html>

Nothing fancy there.  If you are not familiar with <c:url>, it simply creates a URL that includes the webapp context.  In other words, “manageDepartments.html” becomes “departments/manageDepartments.html”. We’ve included a file called “include.jsp” that sets up our tag libraries and prevents caching of the web page.   For completeness, here is the source of that file.

/src/main/webapp/WEB-INF/jsp/include.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="sp" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="spf" uri="http://www.springframework.org/tags/form" %>
<%
response.setHeader("Cache-Control","no-cache"); //HTTP 1.1
response.setHeader("Pragma","no-cache"); //HTTP 1.0
response.setDateHeader ("Expires", 0); //prevents caching at the proxy server
%>

Standard taglib definitions and header setting here.   By adding this file into each JSP page, we don’t have to keep defining the tag libraries each time.

Next, let’s look at the ManageDepartmentsController.  This controller produces the list of all departments to the browser.

/src/main/java/com/technologicaloddity/departments/controller/ManageDepartmentsController.java

package com.technologicaloddity.departments.controller;

import java.util.*;

import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

import com.technologicaloddity.departments.dao.*;
import com.technologicaloddity.departments.model.*;

@Controller
public class ManageDepartmentsController {

    @Autowired
    private DepartmentDao departmentDao;

    @ModelAttribute("departments")
    public List<Department> getDepartments() {
        return departmentDao.findAll();
    }

    @RequestMapping("/manageDepartments.html")
    public String handleRequest() {
        return "manageDepartments";
    }

}

The line-by-line:

  • Lines 1-10: Standard package and import statements.
  • Lines 12-13: We are again using the Spring @Controller stereotype, just like IndexController.
  • Lines 15-16: In order to get a list of Department objects from the database, we need to use the DepartmentDao we created in the Data Access Objects section.  We add it here, and use the @Autowired annotation to tell Spring to fill it in with a real instance of DepartmentDao.  We can do this because DepartmentDao is also a @Component (remember?), which means it can be auto-wired.
  • Line 18: Here’s a new annotation: @ModelAttribute. A method annotated with @ModelAttribute will supply the value it returns to the Spring Model object.  What does this mean?  If, as in this case, you have @ModelAttribute(“departments”), you can use ${departments} in your JSP page and it will be filled with the value of the getDepartments() method.  Note that with annotations, there are many other ways to add items to the Model, too.  For example, we could have added a Model parameter to the handleRequest method, and modified the Model there.
  • Lines 19-21: Here’s the implementation for the @ModelAttribute above.  We return a list of all known departments by calling DepartmentDao’s findAll() method (which, of course, is really MappedModelDao’s findAll method).
  • Lines 23-26: Here we use another @RequestMapping to map the method handleRequest() to URL “manageDepartments.html”.  We are returning the manageDepartments view, which maps to /WEB-INF/jsp/manageDepartments.jsp in our ViewResolver.

Since we are using the manageDepartments view, let’s take a look at it now.

/src/main/webapp/WEB-INF/jsp/manageDepartments.jsp

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<html>
<head>
<title>Manage Departments</title>
<link rel="stylesheet" href="<c:url value='/css/departments.css'/>" type="text/css" >
<script type="text/javascript">
function confirmDelete(departmentId) {
    var answer = confirm("Delete this department?");
    if(answer) {
        document.location.href = "<c:url value='/deleteDepartment.html'/>?id=" + departmentId;
    }
}
</script>
</head>
<body>
<h1>Manage Departments</h1>
<hr>
<table width="60%">
<c:choose>
    <c:when test="${empty departments}">
    <tr>
        <td colspan="4">No departments were found.</td>
    </tr>
    </c:when>
    <c:otherwise>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th align="left">Functions</th>
        </tr>
        <c:forEach items="${departments}" var="department" varStatus="status">
            <tr class="row${status.index % 2 }">
                <td align="center">${department.id}</td>
                <td align="center">${department.name}</td>
                <td><a href="<c:url value='/editDepartment.html'><c:param name="id" value="${department.id}"/><c:param name="action" value="edit"/></c:url>">Edit</a>&nbsp;<a href="javascript:void(0);" onclick="confirmDelete('${department.id}');">Delete</a></td>
            </tr>
        </c:forEach>
    </c:otherwise>
</c:choose>
<tr style="margin-top:20px;">
    <td>&nbsp;</td>
    <td colspan="2"><button onclick="document.location.href='<c:url value='/editDepartment.html'><c:param name='action' value='edit'/></c:url>';">Add new department</button></td>
</tr>
<tr style="margin-top:20px">
    <td>&nbsp;</td>
    <td><button onclick="document.location.href='<c:url value='/index.html'/>';">Home</button></td>
</tr>
</table>
</body>
</html>

Since there is a lot of HTML in here, I’ll only hit the functional parts in the line-by-line.

  • Line 1: We are again using the include.jsp file (from above) to load the standard tag libraries.
  • Lines 7-12: This javascript is a simple “Are you sure?” popup if you are deleting a department.
  • Line 20: If there aren’t any departments, show a “no departments found” message.  Note that we are using “departments” as part of the test, which is filled by the @ModelAttribute(“departments”) method we discussed in ManageDepartmentsController.
  • Line 31: Again we are using the departments @ModelAttribute, and iterating over it as a list.
  • Lines 32-36: Displays a single department from the list.  The “status % 2” bit is there to alternate the stylesheet colors (dark row, light row, dark row, etc).   Note that you can reference department (the “var” part of the c:forEach) like you would expect: department.name, department.id, etc.
  • Lines 35: Note the link to editDepartment.html (which we will see next), passing a parameter of “action” and value “edit”, and also passes the department ID.
  • Lines 42: This is a link to add a department.  Note that it also links to editDepartment.html with value action = edit, but without a department ID.

Now let’s look at the classes that actual handle editDepartment.html.  As you may have guessed, EditDepartment and EditEmployee are actually both subclasses of another controller named MappedModelFormController<T extends MappedModel>.  This allows us to put the meat of the form handling in one controller, and only use EditDepartment (and EditEmployee later) for basic information.  So let’s have a look at MappedModelFormController.

/src/main/java/com/technologicaloddity/departments/controller/MappedModelFormController.java

package com.technologicaloddity.departments.controller;

import org.springframework.dao.*;
import org.springframework.stereotype.*;
import org.springframework.ui.*;
import org.springframework.validation.*;
import org.springframework.web.bind.annotation.*;

import com.technologicaloddity.departments.dao.*;
import com.technologicaloddity.departments.model.*;

@Controller
public abstract class MappedModelFormController<T extends MappedModel> {    

    protected Validator validator;

    protected MappedModelDao<T> modelDao;

    public abstract Class<T> getActualClass();

    public abstract void setMappedModelValidator(Validator validator);

    public abstract void setModelDao(MappedModelDao<T> modelDao);

    private String getTypeName() {
        return getActualClass().getSimpleName();
    }

    @RequestMapping(method=RequestMethod.GET,params="action=edit")
    public String showForm(Model model,@RequestParam(value="id",required=false) String id) {
        T object = null;
        if(id == null) {
            // add new
            try {
                object = getActualClass().newInstance();
            } catch(Exception ie) {
                ie.printStackTrace();
            }
        } else {
            // edit
            object = modelDao.findById(id);
        }
        model.addAttribute("mappedModel", object);
        model.addAttribute("mappedModelName", getTypeName());
        return getEditView();
    }

    @RequestMapping(method=RequestMethod.POST)
    public String handleSubmit(Model model, @ModelAttribute("mappedModel") T mappedModel, BindingResult bindingResult, @RequestParam(value="submit",required=true) String submitType) {
        String result = makeRedirect(getManageView());
        if(submitType.toLowerCase().equals("save")) {
            validate(mappedModel, bindingResult);
            if(bindingResult.hasErrors()) {
                model.addAllAttributes(bindingResult.getAllErrors());
                model.addAttribute(mappedModel);
                model.addAttribute("mappedModelName", getTypeName());
                model.addAttribute("action","edit");
                result = getEditView();
            } else {
                try {
                    modelDao.saveOrUpdate(mappedModel);
                } catch(DataIntegrityViolationException dive) {
                    String message = dive.getMostSpecificCause().getMessage();
                    bindingResult.reject("error.constraint.violation", message);
                    model.addAllAttributes(bindingResult.getAllErrors());
                    model.addAttribute("mappedModel", mappedModel);
                    model.addAttribute("mappedModelName", getTypeName());
                    result = getEditView();
                }
            }
        }
        return result;
    }

    protected void validate(T mappedModel, BindingResult bindingResult) {
        if(this.validator != null) {
            this.validator.validate(mappedModel, bindingResult);
        }
    }

    public String makeRedirect(String viewName) {
        return "redirect:/" + viewName + ".html";
    }

    public String getEditView() {
        return "edit" + getTypeName();
    }

    public String getDeleteView() {
        return "delete" + getTypeName();
    }

    public String getManageView() {
        return "manage" + getTypeName() + "s";
    }

}

As always, the line-by-line for MappedModelFormController.

  • Lines 1-10: Standard package and import statements.
  • Line 12: Even though it is abstract, MappedModelFormController is still a @Controller, since we need to use @RequestMappings with it.
  • Line 13: Notice that this class is abstract and parameterized with <T extends MappedModel>.  Sound familiar?  (If it doesn’t, go back and read the part on Data Access Objects)
  • Line 15: Here we define a Spring Validator to validate our objects before saving them to the database.  Important to note here is that it is NOT @Autowired!  We’ll see why in a minute.
  • Line 17: Just like the Validator, the modelDao is NOT @Autowired.
  • Line 19: Each subclass must tell us what kind of object it is working on.  getActualClass() does this, just like it did for the DAOs.
  • Line 21: Ah.. this is why Validator wasn’t @Autowired.  We have made the setValidtor function abstract, so that each subclass must define their own Validator.  If we had made the Validator itself @Autowired, each type of class would have to use the same validator.
  • Line 23: Ditto with the modelDao.  You can’t use a DepartmentDao with Employees, for example, so each subclass must define their own setModelDao.
  • Lines 25-27: The getTypeName function simply returns the name of the class that is being managed.  “Department” for a Department, “Employee” for an Employee object, etc.
  • Line 29: Here is our first request mapping.  What the heck?  There is no URL in that mapping?? That’s right.  Since we didn’t give a specific URL mapping (like “editDepartment.html”, for example), this function will intercept all GET requests that come to it, as long as it has a request parameter named “action”.  Note that we must supply the “action” parameter as part of the URL, and we did that in manageDepartments.jsp lines 35 and 42.  How does it know to handle editDepartment.html specifically then?  We’ll see that later in the EditDepartment controller.
  • Line 30: The showForm method, as we have seen on line 29, handles all incoming GET requests to this controller.  When it is called, it expects to be passed a Spring Model object, and Spring handles this for you automatically.  ShowForm also expects a request parameter called “id”, which may be null (since it is optional).  You must pass “id” on the URL if you need it, and that is exactly what we did above in manageDepartments.jsp line 35 (but notably NOT on manageDepartments.jsp line 42, the “add” link).
  • Line 31: We will eventual bind the Spring form to variable “object”.  Here we create the variable of type T  (which is part of the class parameterization, “T extends MappedModel”), and assign it a null value.  Note that if the subclass is “EditDepartment extends MappedModelFormController<Department>”, then T will equal Department for that subclass.
  • Lines 32: Here we are making a distinction between an edit of an existing object, or the addition of a new object into the database.  If “id” is null, we assume that we this is an “add new” request.
  • Lines 34-38: Here we create a new T object using the standard java.lang.Class method newInstance().  “object = new T()” is not legal in Java since T isn’t a real Class, so we use the getActualClass() abstract method along with newInstance() to create the new object.
  • Lines 39-42: In the else branch, “id” is not null, so we are assuming this is an “edit existing” request.  In this case we will use the modelDao to retrieve the existing object by the given id.
  • Lines 43-44: Here we add some elements into the Spring Model object so that we can use them on the next JSP view page.  First, we add “object”, which is the object we are editing (or adding), then we add the name of the type of object we are adding (for example “Department”).
  • Line 45: Here we return a String that represents the view we want to show with our Model.  In this case, it is the result of getEditView(), which we will see later in this file.
  • Line 48: Just like showForm() will handle any GET, handleSubmit() is @RequestMapped to any POST (that is, a form submit).
  • Line 49: And here is the handleSubmit signature.  It expects a Spring Model, a @ModelAttribute called “mappedModel” (which we set in showForm(), remember?), a Spring BindingResult, and a @RequestParam called “submit”, which we will store in the variable “submitType”.  Note that at this point, the mappedModel should have the NEW user-edited version of the object.  The BindingResult is there to tell us if there were any errors in matching up the form fields to the new or edited object (like a name conflict between the form field name and the member name, for example).  The submitType is either “Save” or “Cancel”, and we will handle each accordingly.
  • Line 50: The return value of handleSubmit(), like most of our Controller methods, is a String that represents the view we want to show.  We’ll initialize it here to the default, which is the value of getManageView().  (If you are confused about this, read on to the EditDepartment section, where we will do a short walkthrough of which view is showing when).
  • Line 51: Here we check if the submitType is “Save”.  If it is, we’ll process the form post and save the new (or edited) object.  If it isn’t save, we assume that it is “Cancel”, and we’ll just drop out of the function and return the getManageView() result.  In other words, there is no else clause to this if statement.
  • Line 52: We call the validate function to insure that we have all the data the we need and that it is proper.  For example, all Departments must have a value for “name”.
  • Line 53-57: If initial binding by Spring fails, or if our Validator fails, then bindingResult.hasErrors() will be true.  If that is the case, we want to put the edit/add form back up and show the end-user what the errors were.  We change the result variable to getEditView() to return to the edit/add form.  We also add all the errors to the Spring Model object, and reinsert the mappedModel as it was bound by Spring.  (Later, in the JSP page, we’ll show these errors if they exist)
  • Line 58-69:  If there were no binding errors, we try to save the object using the supplied modelDao.  If the saveOrUpdate returns a DataIntegrity exception, this usually means duplicate information (for example, two departments with the same name).  If that happens, we will again change the result to getEditView(), push the error message onto the model, and send the user back to the edit/add form, just like if the Validator had failed above.
  • Line 71: If all went well, the result will be getManageView().  If there were errors, the returned view will be getEditView() with the errors inside the model.
  • Lines 74-78: The validate() method simply checks if a validator was supplied by the subclass, and if it was, it calls it on the given mappedModel.
  • Lines 80-82: It is good practice to return from a form POST with a redirect action to the next view.  This prevents accidental double-posting by the end-user.
  • Lines 84-94: Nothing fancy here.  The Edit, Manage, and Delete view names are created by adding in the getTypeName() method.  So, if T == Department, then getEditView() == “editDepartment”.  Same with getDeleteView() and getManageView() (although getManageView adds an “s” to the URL to make it sound better in English).
  • Line 96: End of class.

Whew, that’s a lot to take in.  Fortunately, using the MappedModelFormController makes both EditDepartment and EditEmployee trivial to create.  Since we are working with Departments, let’s look at EditDepartment next.

/src/main/java/com/technologicaloddity/departments/controller/EditDepartment.java

package com.technologicaloddity.departments.controller;

import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.validation.*;
import org.springframework.web.bind.annotation.*;

import com.technologicaloddity.departments.dao.*;
import com.technologicaloddity.departments.model.*;

@Controller
@RequestMapping("/editDepartment.html")
public class EditDepartment extends MappedModelFormController<Department> {

    @Override
    public Class<Department> getActualClass() {
        return Department.class;
    }

    @Override
    @Autowired
    @Qualifier("departmentValidator")
    public void setMappedModelValidator(Validator validator) {
        this.validator = validator;
    }

    @Override
    @Autowired
    @Qualifier("departmentDao")
    public void setModelDao(MappedModelDao<Department> modelDao) {
        this.modelDao = modelDao;
    }
}

EditDepartment line-by-line:

  • Lines 1-9: Standard package and import statements.
  • Line 11: EditDepartment handles web requests, so we will use Spring’s @Controller stereotype here.
  • Line 12: Here we map the entire class to “editDepartment.html” with @RequestMapping.  Wait.. don’t request mappings have to resolve to method name?  Yes, indeed they do.  Since EditDepartment is a subclass of MappedModelFormController, it also inherits the GET and POST @RequestMappings in the parent class.  So, Spring uses both the mapping for “editDepartment” here combined with the GET/POST mappings in MappedModelFormController to decide which method call.  So, for example, a GET to editDepartment.html will call MappedModelFormController showForm(). A POST to editDepartment.html will be handled by MappedModelFormController handleSubmit().
  • Line 13: Notice that this class extends MappedModelFormController, parameterized with the “Department” class.
  • Line 15-18: Implementation of the getActualClass method, as required by MappedModelFormController.
  • Line 20-25: Implementation of the setMappedModelValidator as required by MappedModelFormController.  Note that even though it is @Autowired, we use a qualifier of “departmentValidator” here to specify the DepartmentValidator class, since there are other types of Validators in the webapp (namely, EmployeeValidator).  To avoid confusion, we specify which one we want.
  • Line 27-32: Implementation of setModelDao as required by MappedModelFormController.  Again we use a qualifier of “departmentDao” to avoid confusion with other DAO objects.
  • Line 33: End of class.

Here’s the DepartmentValidator we set above.

/src/main/java/com/technologicaloddity/departments/validator/DepartmentValidator.java

package com.technologicaloddity.departments.validator;

import org.springframework.stereotype.*;
import org.springframework.util.*;
import org.springframework.validation.*;

import com.technologicaloddity.departments.model.*;

@Component
public class DepartmentValidator implements Validator {

    public boolean supports(Class<?> clazz) {
        return (clazz.isAssignableFrom(Department.class));
    }

    public void validate(Object command, Errors errors) {
        Department department = (Department)command;
        if(!StringUtils.hasText(department.getName())) {
            errors.rejectValue("name", "error.required.name", "Name is required");
        }

    }
}

Since the Validator interface is well-known, we’ll only cover the important lines in the DepartmentValidator:

  • Line 13: We tell the validator that it should only try to validate Department objects.
  • Lines 17-20: Basically, there is only one validation rule for Department: there must be some text in the name, it cannot be empty or null.

Let’s look how these classes work with the view, editDepartment.jsp.

/src/main/webapp/WEB-INF/jsp/editDepartment.jsp

<%@ include file="/WEB-INF/jsp/include.jsp" %>
<jsp:include page="pageHeader.jsp" flush="true"></jsp:include>
<spf:form modelAttribute="mappedModel">
<spf:errors path="*"/>
<table  cellpadding="20">
    <tr style="background-color:#c0c0c0">
        <td>ID</td>
        <td><c:choose><c:when test="${empty mappedModel.id}">[Assigned on save]</c:when><c:otherwise>${mappedModel.id}</c:otherwise></c:choose></td>
    </tr>
    <tr style="background-color:#c0c0c0">
        <td>Name</td>
        <td><spf:input path="name"/><spf:errors path="name" cssClass="errors"/></td>
    </tr>
    <tr>
        <td>&nbsp;</td>
        <td><input type="submit" name="submit" value="Save"/>&nbsp;<input type="submit" name="submit" value="Cancel"/></td>
    </tr>
</table>
</spf:form>
<jsp:include page="pageFooter.jsp" flush="true"></jsp:include>

editDepartment, unique parts:

  • Line 1: We are again using the include.jsp file to set up the tag libraries.
  • Line 2: We’ve also used a common form page header called pageHeader.jsp.  We’ll look at that in a minute.
  • Line 3: Here we set up the Spring Form and bind it to the mappedModel object from the Spring model.
  • Line 4: We use the <spf:errors> tag to show any errors that are in the model.  This would only show errors after a submit, as we set the errors in MappedModelFormController (line 54 or line 64).
  • Line 8: If there is an ID, then display it. If not, put up the message “Assigned on Save”. Note that in either case, the ID is never editable.  The reason for this is that the ID is Hibernate’s primary key for finding the object.  If you change the ID, it will not know which object you are trying to edit.
  • Line 12: This is the mapping for the “name” member of the Department.  We also print out any specific errors with the name here with <spf:errors>.
  • Line 16: There are two submit buttons here.  One has a value of “Submit”, and one has a value of “Cancel”.  These values become the “submitType” value in handleSubmit() of MappedModelFormController (line 49).
  • Line 20: Just as we are using a pageHeader, we also have a pageFooter.jsp that is common among forms.  We’ll look at this file in a bit.

Here’s the pageHeader.jsp we are using.

/src/main/webapp/WEB-INF/jsp/pageHeader.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:choose>
    <c:when test="${empty mappedModel.id}">
        <c:set var="pageTitle">Add ${mappedModelName}</c:set>
    </c:when>
    <c:otherwise>
        <c:set var="pageTitle">Edit ${mappedModelName}</c:set>
    </c:otherwise>
</c:choose>
<html>
<head>
<title>${pageTitle}</title>
<link rel="stylesheet" href="<c:url value='/css/departments.css'/>" type="text/css" >
</head>
<body>
<h1>${pageTitle}</h1>
<hr>

Important lines in pageHeader.jsp:

  • Line 1: Although the main page has already included the “c” tag library, this page can’t access it.  So we declare it here too, since we need it for <c:set> later.
  • Lines 2-9: Here we set the title of the page, depending on what operation we are doing.  If our mappedModel has an ID, then it must be an edit of an existing model. If not, it is an add.  We set it in page scope with the <c:set> tag.
  • Line 13: Including the stylesheet for forms.
  • Lines 12 and 16: We use the pageTitle twice: once in the TITLE tag, and once as the H1 for the page.

And here is the footer.

/src/main/webapp/WEB-INF/jsp/pageFooter.jsp

</body>
</html>

Important lines in pageFooter.jsp: none!  It simply closes the BODY and HTML tags for the form.  So why use it?  By using the pageHeader and pageFooter, we can focus on creating only the form for Departments, and we will use the same pageHeader and pageFooter later with Employees.

20 thoughts on “Spring and Hibernate with Annotations”

  1. Excellent tutorial! Very high quality, it all works beautifully, and very well explained. Thanks for putting it all together. I’ve tried some other similar tutorials before this but none worked. I was using Spring STS as my dev env, and it was fine, with a little change in the initial project setup.

    Only very minor comment is that jsp directory is not usually inside WEB-INF, but alonside it in webapp directory. As I said, this is very minor, viewed against a small mountain of very well designed/executed code.

    All the best
    Noel

    1. Noel,

      Thanks for the kind comments.

      On the matter of the location of the JSP directory, the developers of Spring use the WEB-INF/jsp location themselves (see http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html#mvc-viewresolver ).

      The reason is for security. End users cannot directly reference any file under the WEB-INF directory (with something like http://yoursite.com/webapp/WEB-INF/foo.jsp ) as this produces an error. However, if you use only /jsp, they can reference http://yoursite.com/webapp/jsp/foo.jsp , in the same way they can access http://yoursite.com/webapp/images/logo.gif .

      Using WEB-INF/jsp allows you to maintain complete control of which JSP is shown when, and keeps the script-kiddies from poking around so much.

      Bob

      1. Hi Bob,
        your explanation about why you put the jsp directory into the WEB-INF directory sounds entirely reasonable to me – thanks for that.

        Noel

  2. Wow! Great Tutorial. I’m just in the process of absorbing everything now. The “step by step” code explanation is great for beginners like me. This has got to be the best maven+spring+hibernate tutorial I have come across yet (and I must have seen them all)

    1. Also, it wasn’t working for me at first. I was getting errors like:

      annotations are not supported in -source 1.3
      (use -source 5 or higher to enable annotations)

      I found that Maven’s default java compiler version is 1.3, and even if you don’t have it installed, you’ll still get the above errors unless you explicitly change this by adding the following code within in pom.xml:

      org.apache.maven.plugins
      maven-compiler-plugin
      2.3.2

      1.5
      1.5

        1. Deepak,

          You are 100% correct. You need to specify source and target 1.5 as you have shown above. I have updated the tutorial and source files to reflect this.

          Good catch, thanks!

          Bob

  3. Great tutorial and I learned a lot from this

    I made some changes (Oracle / combined jdbc.properties into the Spring config file etc) and it all works fine

    Then I tried to add some unit tests, but changed the MappedModelDao to implement an interface (extracted through Eclipse) – as explained in
    http://zenoconsulting.wikidot.com/blog:8#toc9

    As soon as I did this, and rebuilt the war, it failed to start in Tomcat with the following exception:

    SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘deletionController’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.technologicaloddity.departments.dao.DepartmentDao com.technologicaloddity.departments.controller.DeletionController.departmentDao; nested exception is java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:283)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1055)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:450)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:289)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:286)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:188)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:558)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:842)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:416)
    at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:261)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:192)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4701)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5204)
    at org.apache.catalina.core.StandardContext$1.call(StandardContext.java:5199)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.technologicaloddity.departments.dao.DepartmentDao com.technologicaloddity.departments.controller.DeletionController.departmentDao; nested exception is java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:504)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:280)
    … 21 more
    Caused by: java.lang.IllegalArgumentException: Can not set com.technologicaloddity.departments.dao.DepartmentDao field com.technologicaloddity.departments.controller.DeletionController.departmentDao to $Proxy21
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:146)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:150)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:63)
    at java.lang.reflect.Field.set(Field.java:657)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:500)
    … 23 more

    Also, I got a similar exception when trying to run a simple JUnit test in Eclipse
    My Spring knowledge is limited, so any help as to what the problem might be would be appreciated
    Thanks

  4. it works with postgres with quite minimal changes, just add the dependency for the jdbc connector for postgres to pom.xml and change jdbc driver class(dispatch-servlet.xml) to postgres(org.postgresql.Driver) and the db url(to something like jdbc:postgresql://localhost:5432/database ).

    great tutorial, better than most other spring and hibernate tutorials on the net! simple working approach that doesn’t need table creation by hand(so simpler to adapt to postgresql).

  5. Hi,

    This is probably one of the best tutorial that I came across.

    Just one thing, why there is no service layer added in the code or shown. Most of the enterprise applications do have a Service Layer for interacting with DB, I guess.

    Thanks & Regards
    Sunil

  6. One more thing, I have to include the servlet dependency in pom file otherwise the project was not getting run and giving error as “Cannot find class file for javax/servlet/ServletContext”.

    Regards
    Sunil

  7. U truly created a number of outstanding stuff within ur article, “Spring and Hibernate with Annotations | Technological
    Oddity”. I may possibly be coming to ur site soon.
    Thanks a lot ,Steve

  8. Now that I’ve just devoted six hours on your website reading your posts, I’m hooked on your blog.
    I bookmarked it as well so that I can keep up with it on a regular basis.
    Have a look at my web site too and tell me what you think.

  9. Heya i am for the first time here. I came across this board and I find It really useful & it helped me out a lot.
    I hope to give something back and aid others like you helped
    me.

  10. This is very interesting, You are a very skilled blogger. I have joined your
    rss feed and look forward to seeking more of your fantastic
    post. Also, I’ve shared your sitee in my social networks!

    1. Lakhan,

      I’m using a MySQL database. The database is automatically created by Hibernate, since “hibernate.hbm2ddl.auto = update” is in the hibernate.properties. So there is no SQL to show. If you have other questions, feel free to ask.

Leave a Reply

Your email address will not be published. Required fields are marked *