Skip to content


Spring JDBC Template – A leaner alternative to fat Hibernate

Objective

This post will show you how to use Spring JDBC Template to perform real world CRUD operations. I hope I can show you that you do not need overweight Hibernate to interact with database of your choice.

The Problem Domain

Almost all application I have coded needed to store and get data of some sort. In most cases it was relational database. With hibernate being all the rage these days I have used it in multiple projects with varying degree of problems and success. In my personal opinion hibernate tries to solve a problem that really is not there. If you can not wire SQL you should not be using Hibernate or be doing any software development.

I advise the use of Spring JDBC templates. In essence Spring JDBC Template will offload all the mundane steps of database access from you shoulders only requiring you to write SQL and move data from result set into your model objects. Here is how Spring documentation describes responsibilities:

Action Spring You
Define connection parameters. X
Open the connection. X  
Specify the SQL statement.   X
Declare parameters and provide parameter values   X
Prepare and execute the statement. X  
Set up the loop to iterate through the results (if any). X  
Do the work for each iteration.   X
Process any exception. X  
Handle transactions. X  
Close the connection, statement and resultset. X  


CRUD Example with Spring JDBC Template

For this example you will need spring-core, spring-tx and spring-aop components. We will use spring-tx to manage the transaction so we can get back the auto generated IDs of the persisted objects and aop to set up the transaction management. Here is meat for spring configuration file:

<bean id="propertyPlaceholder"      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >   <property name="location" value="/WEB-INF/config.properties" /> </bean> <bean id="mysqlDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"      destroy-method="close">   <property name="driverClass" value="com.mysql.jdbc.Driver"/>   <property name="jdbcUrl" value="${database.url}"/>   <property name="user" value="${database.user}"/>   <property name="password" value="${database.password}"/>   <property name="initialPoolSize" value="${database.connections.start}"/>   <property name="maxPoolSize" value="${database.connections.max}"/>     <property name="idleConnectionTestPeriod" value="270" /> </bean> <bean id="txManager"      class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >   <property name="dataSource" ref="mysqlDataSource" /> </bean> <tx:advice id="txAdvice" transaction-manager="txManager">   <tx:attributes>     <!-- all methods starting with 'get' are read-only -->     <tx:method name="get*" read-only="true"/>     <tx:method name="list*" read-only="true"/>       <!-- other methods use the default transaction settings (see below) -->       <tx:method name="*"/>     </tx:attributes> </tx:advice> <aop:config>   <aop:pointcut id="uOwnerDataSvcOperation"      expression="execution(* com.es.rto.dao.UOwnerDataServiceImpl.*(..))"/>   <aop:advisor advice-ref="txAdvice" pointcut-ref="uOwnerDataSvcOperation"/> </aop:config> <bean id="namedTemplate"    class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">   <constructor-arg index="0" ref="mysqlDataSource"/>   </bean> <bean id="qandADao" class="com.es.rto.dao.QandADao" >   <property name="jdbcTemplate" ref="namedTemplate" /> </bean> <bean id="uOwnerDataSvc" class="com.es.rto.dao.UOwnerDataServiceImpl" >   <property name="qandADao" ref="qandADao" /> </bean>

First thing we do we load our config.properties file where we have database configuration defined. Second we define a pooled data source to connect to mysql database. Third bean is a transaction manager that is watching our data source from the step two. Next we set up our transaction manager. We instruct transaction manager that methods starting with “get” and “list” are read only. This means that they will not be subject to transaction management. Then we define named jdbc template and pass in the data source as first constructor argument and then pass in the template into our own DAO bean.

Why do we need Transaction Management?

When we create a new row in a mysql table with autoincrement id value we need a way to get that auto generated id. To do so we need to query the database using same connection. When we use jdbcTemplate.update() or jdbcTemplate.query() it will check out a connection form the pool execute the query, process the results and return this connection to the pool. So if we do update() and then get() to query “SELECT LAST_INSERT_ID()” we will get another connection, which is a problem since we need to do this on the same connection to get the right id. This is where the transaction comes in. Instead of releasing connection back to the pool spring will hold on to it until we are done with it.

Java code side of the Spring JDBC template

We will use 3 beans to illustrate the scenario: question, answer and user.

public class Question {         private long questionId;         private long categoryId;         private long userId;         private long areaId;         private String question;         private String verifyKey;         private Date created;         private User user;         private List<Answer> answers;         private long answerCount;         private String name;         // getters and setters omited... }
public class Answer {         private long answerId;         private long questionId;         private long userId;         private String answer;         private String verifyKey;         private String url;         private Date created;         private User user;         private String name;         // getters and setters omited... }
public class User implements Serializable {         private long id;         private String email;         private String retypeE;         private String name;         private String password;         private String password2;         private String verifyKey;         private boolean buyer;         private boolean seller;         private boolean active;         private boolean privacy;         private int pageSize = 20;         private String next;         private boolean administrator;         // getters and setters omited... }

Here are the 2 classes that do all the work:

public class QandADao {   private final Logger logger = Logger.getLogger(QandADao.class);   protected NamedParameterJdbcTemplate jdbcTemplate = null;     @SuppressWarnings("unchecked")   public List<Question> getQuestions( String search, int page, int step ) {     try {       MapSqlParameterSource params = new MapSqlParameterSource( "search", "%"+search+"%" );       String sql = "SELECT q.*, count( a.answer_id ) as answers, u.name, u.seller  FROM users u, questions q LEFT JOIN answers a "+       "ON  a.question_id = q.question_id WHERE a.answer LIKE :search AND q.user_id = u.id "+       "GROUP BY q.question_id, q.category_id, q.user_id, q.question, q.created, q.area_id, u.name, u.seller "+         "UNION "+       "SELECT q.*, count( a.answer_id ) as answers, u.name, u.seller  FROM users u, questions q LEFT JOIN answers a "+       "ON  a.question_id = q.question_id WHERE q.question LIKE :search AND q.user_id = u.id "+       "GROUP BY q.question_id, q.category_id, q.user_id, q.question, q.created, q.area_id, u.name, u.seller "+       "LIMIT "+(step*(page-1))+", "+step;       logger.debug( sql );       return jdbcTemplate.query(sql, params, new RowMappers.QuestionsMapper());     } catch ( DataAccessException exc ) {       logger.error("FAILED to get Question List", exc);       return new ArrayList<Question>();     }   }   public Question saveQuestion( Question question ) {     try {       BeanPropertySqlParameterSource namedParameters = new BeanPropertySqlParameterSource(question);       String sql;       if( question.getQuestionId() == 0 )         sql = "INSERT INTO questions ( category_id, user_id, question, area_id, uname ) VALUES ( :categoryId, :userId, :question, :areaId, :name )";       else         sql = "UPDATE questions SET question=:question, category_id=:categoryId, area_id=:areaId WHERE question_id=:questionId";       logger.debug( sql );       jdbcTemplate.update(sql, namedParameters);       if( question.getQuestionId() == 0 )         question.setQuestionId( this.getInsertedID() );             } catch ( DataAccessException exc ) {       logger.error("SAVE answer FAILED!", exc);       throw exc;     }     return question;   }   protected long getInsertedID() {     return jdbcTemplate.queryForLong( "SELECT LAST_INSERT_ID()" , new MapSqlParameterSource());   }   }
public class RowMappers {   public static final class QuestionMapper implements RowMapper {       public Object mapRow(ResultSet rs, int rowNum) throws SQLException {         Question q = new Question();         q.setQuestionId(rs.getLong( "question_id" ));         q.setCategoryId(rs.getLong( "category_id" ));         q.setUserId(rs.getLong( "user_id" ));         q.setAreaId( rs.getLong("area_id"));         q.setQuestion( rs.getString("question"));         q.setCreated( rs.getTimestamp( "created" ) );         q.setName( rs.getString("uname") );         User u = new User();         u.setName( rs.getString("name") );         u.setSeller( rs.getBoolean("seller") );         u.setId(q.getUserId());         q.setUser(u);       return q;     }   } }

So what is going on here? Lets look at the QuestionMapper. It implements one method – mapRow. Its hob is to process a single row of the result set. As you see here we create a Question bean and a User bean which is included in the Questions bean. It can not be more strait forward then this.

QandADao is the class where all the magic happens. First lets examine the getQuestions() method. First we define query parameter(s) using MapSqlParameterSource class. We simply put the values in the map and jdbc template will pull them out by the key. Next we write our sql query. In this case iti is pretty complex to pull out any questions where either question or the answer have the thing we are searching for. We use union to be able to pull out the questions without the answers. Lastly we call query method on the jdbc template and we pass in our query, parameters and the row mapper instance. Spring takes care of all the plumbing. What we get out of this call is a list of Question objects.

saveQuestion() method is not much different. In this case we use BeanPropertySqlParameterSource which maps bean properties to sql parameters. If you define a sql parameter “name” your bean should have “getName()” method. Instead of calling query() we call update() on the jdbc template which only takes sql and the parameters.

Conclusion

You may have noticed that the effort to use the Spring approach to JDCB is very simple, flexible and powerful. You have all the control in all the right places! You write the sql with all the performance hints and tweaks, you write code how to transfer results to the domain objects the way you need it or like it. I only mentioned about one tenth of the options you have with spring. Read The Fine Manual (RTFM) to explore the full potential.

  • Facebook
  • Twitter
  • Digg
  • del.icio.us
  • Reddit
  • Google Bookmarks
  • LinkedIn
  • Slashdot
  • MySpace
  • Propeller
  • StumbleUpon
  • Yahoo! Buzz
  • Add to favorites
  • email
  • Yahoo! Bookmarks
  • Live
  • FriendFeed
  • Technorati

Posted in Spring Framework.

Tagged with , , .


0 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.



Some HTML is OK

or, reply to this post via trackback.