using the spring framework.
The app will consist in a simple implementation of a crud (create read update and delete) type app for simple person domain object.
1.1) The spring.jar from the springframework.org which contains almost all the jars neccessary
1.3) commons-dbcp.jar and commons-pool.jar( if you download the spring framework) with all the dependencies you will find also these jars in the lib/jackarta-commons dir
- DB (I am using mysql, but I reccomend some lighter one for this app like Derby(which is included in the jsdk 6) Or HSQL)
// The Sql for the db is
DROP TABLE IF EXISTS `springtutorial`.`person`;
CREATE TABLE `springtutorial`.`person` (
`id` int(10) unsigned NOT NULL auto_increment,
`firstname` varchar(45) NOT NULL,
`lastname` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
)
|
Then, we define the associated domain object
package net.fantayeneh.springtutorial.domain
public class Person {
private Long id;
private String firstName;
private String lastName;
/** Creates a new instance of Person */
public Person()
{
}
// define all the getters and setters
}
|
Next, we define what service we want to expose for the end user
so, since it is a crud app, it will be:
package net.fantayeneh.springtutorial.service
public interface IPersonService
{
Person getPersonById(Long id);
List<Person> getAllPerson();
void savePerson(Person person);
void updatePerson(Person person);
void deletePerson(Long id);
}
|
For the implementation we need some sort of dao object that will
incapsulate all the details regarding the interaction with the underlined db.
In our case we will use the spring jdbctemplate implementation.
So...
package net.fantayeneh.springtutorial.service
public class PersonServiceImpl implements IPersonService
{
//we need the dao object
private IPersonDAO personDAO;
public void setPersonDAO(IPersonDAO personDAO)
{
this.personDAO = personDAO;
}
// now we only have to call the dao object for completing
// our service
public Person getPersonById(Long id)
{
return getPersonDAO().getPersonById(id);
}
public List<Person> getAllPerson()
{
return getPersonDAO().getAllPerson();
}
public void savePerson(Person person)
{
getPersonDAO().savePerson(person);
}
// conitnue delegating to the dao object
}
|
Note: As you note I did not create explicitly any IPersonDAO object in the previous code.
Here is where the magic happens, the spring containter will inject at runtime
the object in the
PersonServiceImpl space, using the dependency injection feature based on a configuration file. But for now don't bother, we will come back to it at the end when wiring all the modules together.
Now the DAO.
Firstly, we define the DAO interface (remember program against an interface)
package net.fantayeneh.springtutorial.dao
interface IPersonDAO {
Person gerPersonById(Long id);
List<Person> getAllPerson();
void savePerson(Person person);
void updatePerson(Perosn person);
void deletePerson(Long id);
}
|
an implementation will use, as I said, the jdbctemplate of the spring framework
package net.fantayeneh.springtutorial.dao
public class PersonDAOJdbc extends JdbcDaoSupport implements IPersonDAO
{
private static final String GET_ALL_PERSON_SQL =
"select * from person";
public List getAllPerson()
{
return getJdbcTemplate().query(GET_ALL_PERSON_SQL, new PersonMapper());
}
private static final String GET_PERSON_SQL = "SELECT * FROM person WHERE id = ?";
public Person getPersonById(Long id)
{
try
{
return (Person)getJdbcTemplate()
.queryForObject(GET_PERSON_SQL,
new Object[]{id},
new PersonMapper());
}
catch (DataAccessException dae)
{
return null;
}
}
private static final String UPDATE_PERSON_SQL = "" +
"update person set firstname = ?, lastname = ? " +
"where id = ?";
public void updatePerson(Person person)
{
Object[] parameters = new Object[]{
person.getFirstName(),
person.getLastName(),
person.getId()};
getJdbcTemplate().update(UPDATE_PERSON_SQL, parameters);
}
private static final String INSERT_PERSON_SQL=
"insert into person(firstname, lastname) values (?, ?)";
public void savePerson(final Person person)
{
Object[] parameters =
new Object[]{person.getFirstName(), person.getLastName()};
getJdbcTemplate().update(INSERT_PERSON_SQL, parameters);
}
public void deletePerson(Long id)
{
}
// Here we implement the rowmapper callback
// for mapping a single row to a domain object
private class PersonMapper implements RowMapper
{
public Object mapRow(ResultSet resultSet, int i) throws SQLException
{
Person person = new Person();
person.setId(resultSet.getLong("id"));
person.setFirstName(resultSet.getString("firstname"));
person.setLastName(resultSet.getString("lastname"));
return person;
}
}
}
|
Now that we are done with the backend stuff we move to the configuration.
- We have four conf files to deal with
- springtutorial-servlet.xml
- springtutorial-dao.xml
- springtutorial-service.xml
- web.xml
we can merge the first three files in one but it is a good style to keep the various conf stuff in a separate file, so, keep the configuration that interests the service layer in an apropriate file, and so on
First We start by wiring the dao layer and the service layer:
springtutorial-dao.xml
<beans>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/springtutorial" />
<property name="username" value="xxxx" />
<property name="password" value="xxxx" />
</bean>
<bean id="personDAO" class="net.fantayeneh.springtutorial.dao.jdbc.PersonDAOJdbc">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Transaction Manager For a Single JDBC DataSource-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"> <ref local="dataSource" /></property>
</bean>
</beans>
|
Note: Here we define the personDAO that we will be referring to in the springtutorial-service.xml
For the service layer we have only one bean to deal with, so :
springtutorial-service.xml
<beans>
<bean id="personService" class="net.fantayeneh.springtutorial.service.PersonServiceImpl">
<property name="personDAO" ref="personDAO"/>
</bean>
</beans>
|
Now the springtutorial-servlet.xml file a typical bean configuration is
<bean name="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<!--
This bean will associate what method to associate to a given action
-->
<bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
<property name="mappings">
<value>
/view.html=view
/viewAllPerson.html=viewAllPerson
</value>
</property>
</bean>
<bean name="/*.html" class="net.fantayeneh.springtutorial.web.PersonController" >
<property name="personService" ref="personService" />
<property name="methodNameResolver" ref="methodNameResolver"/>
</bean>
<bean id="personAddEdit" class="net.fantayeneh.springtutorial.web.AddEditPersonFormController">
<property name="commandClass" value="net.fantayeneh.springtutorial.domain.Person"/>
<property name="commandName" value="person"/>
<property name="formView" value="addEditPerson"/>
<property name="successView" value="redirect:/viewAllPerson.html"/>
<property name="personService" ref="personService" />
</bean>
<bean name="/addPerson.html" parent="personAddEdit">
<property name="action" value="add" />
</bean>
<bean name="/editPerson.html" parent="personAddEdit">
<property name="action" value="edit" />
</bean>
<!-- View Resolution -->
<bean id="beanNameViewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="0"/>
</bean>
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
|
Note: As you see I use the same class to make the add and edit stuff( I hate repeating codes)the two beans addPerson and editPerson have the attribute parent set to person
AddEdit so they are extending the parent class and they have access to all the properties.
When we implement the controller class we have to figure out which operation
we are dealing with, here comes that innocent property "action",
the spring framework will inject the actual value, so we only need to make a simple test. great no????
Before implementing the controllers that handle the various
user request we configure the web.xml .
Every Spring MVC needs the
DispatherServlet
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/springtutorial-servlet.xml
classpath:net/fantayeneh/springtutorial/dao/springtutorial-dao.xml
classpath:net/fantayeneh/springtutorial/service/springtutorial-service.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springtutorial</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springtutorial</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
|
The main things to note are:
1) there is a direct link between the dispatcher servlet name and the spring configuration file springtutorial-servlet.xml, so if you call your dispatcher servlet with foo, you have to name also the config file with foo-servlet.xml.
2) We also specify the place where the spring framework container has to look for the various xml conf files.
Now that all the wirings are set, let's complete the tutorial by implementig
the various controllers
We start with the
PersonController which is a
MultiActionContrller(handle the all the get
stuff:(showing a single or the list of person).
For various other type of controllers please refer to the spring doc)
public class PersonController extends MultiActionController
{
private IPersonService personService;
public ModelAndView view(HttpServletRequest request,
HttpServletResponse response) throws IOException,
HttpRequestMethodNotSupportedException, ServletRequestBindingException
{
if (!request.getMethod().equals(METHOD_GET))
{
throw new HttpRequestMethodNotSupportedException("The requested method is not supported");
}
Long id = ServletRequestUtils.getLongParameter(request, "id");
return new ModelAndView("view", "person",
personService.getPersonById(new Long(id)));
}
public ModelAndView viewAllPerson(HttpServletRequest request,
HttpServletResponse response) throws IOException,
HttpRequestMethodNotSupportedException
{
List<Person> personList = personService.getAllPerson();
return new ModelAndView("viewAllPerson", "personList", personList);
}
public void setPersonService(IPersonService personService)
{
this.personService = personService;
}
}
|
The
MultiActionController is useful because we can define multiple actions in the same controller class, so use this one for getting data.
The next thing to note is that in both cases we are sending a model object to the view(person, and personList):
The view resolver will be resposible for selecting the actual resolver based on the configuration(in this case jsp resolver).
This allow us to separate completley the view from the controller
The associated jsp files for this two actions are
1) view.jsp
// include all the neccessary taglibs
.....
<h4>Person Detail</hr>
<b>First Name: </b> <c:out value="{person.firstName}" />
<b>LastName Name: </b> <c:out value="{person.lastName}" />
|
2) viewPerson.jsp
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
<c:forEach items="${personList}" var="person">
<tr>
<td><c:out value="${person.firstName}" /></td>
<td><c:out value="${person.lastName}" /></td>
<td colspan="2">
<a href="/springtutorial/editPerson.html?id=<c:out value="${person.id}" />">Edit</a> |
<a href="/springtutorial/delete.html?id=<c:out value="${person.id}" />">Delete</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
|
Now the add and edit case:
We implement the
AddEditPersonFormController which is a
SimpleFormController
public class AddEditPersonFormController extends SimpleFormController
{
private IPersonService personService;
private String action;
/** Creates a new instance of AddChildFormController */
public AddEditPersonFormController()
{
}
protected Object formBackingObject(HttpServletRequest request) throws Exception
{
Long id = ServletRequestUtils.getLongParameter(request, "id");
if(id != null)
{
Person person = personService.getPersonById(id);
if(person != null)
return person;
else
throw new PersonNotFoundException();
}
return new Person();
}
protected ModelAndView onSubmit(
final HttpServletRequest request,
final HttpServletResponse response,
final Object object,
final BindException bindException) throws Exception
{
Person person = (Person)object;
if(action.equals("add"))
{
personService.savePerson(person);
}
else
{
personService.updatePerson(person);
}
return new ModelAndView(getSuccessView());
}
public void setPersonService(IPersonService personService)
{
this.personService = personService;
}
public void setAction(String action)
{
this.action = action;
}
}
|
There are two things to note:
1) the first one is the form
BackingObject method which by default intiantiates a person object. By overriding it we can either create a new one or pull it from the db. This depends if we are dealing with an edit or add operation.
In the request we try to get on from the db.
2) The second observation is that in the onSubmit method the service method to call is choosen according to the configured action property(do you remember that???)
Now we set the jsp file for the insert and edit
operation:(we are using the same for both) add
EditPerson.jsp
<form:form commandName="person">
<p>
<label>First Name: </label><form:input path="firstName" /><br />
<label>LastName: </label><form:input path="lastName" /><br />
</p>
<p class="botton">
<input class="button" type="submit" value="Save" />
<input class="button" type="reset" value="Reset" />
</p>
</form:form>
|
Here we are using the new spring 2.0 form tags for binding.
We have almost done...
I know, I know... I didn't forget about the delete method, I leave it to you:
Conclusion:
1)I did not implement any validation stuff, but in the next tutorial
I will do it in a detailed way.
2)I attach the project files.
for any question you can reach me at fantayeneh at netscape dot net
att:20070705210801189-0-springtutorial.rar