Social Icons

twitterfacebookgoogle pluslinkedinrss feedemail

Tuesday, December 13, 2011

Using Spring and MyBatis for non-POJO Data Retrieval

This post will be part one of two dealing with data retrieval in Spring with MyBatis. This post in particular will discuss data retrieval whereby you have a situation where you do not have a DB Schema-to-POJO relationship, e.g. in a properties or settings table.

I won't get into how to set up a Spring datasource bean and will assume you already have one in your Spring config file.  In your Spring config file, per http://code.google.com/p/mybatis/wiki/Spring, add a new bean to configure your SqlSessionFactory using the datasource you defined above.

Next, the mybatis-spring library allows us to wire up things automagically (without declaring our mappers in XML) with the following:

<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="com.foo.persistence"/>
</bean>

This tells the scanner to recursively scan the com.foo.persistence package and create Spring beans for each mapper it finds. If you're using only one datasource, you don't even need to wire that in as the scanner will take care of this for you.

Next, you'll need to create a mapper class like so:

package com.foo.persistence;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface SettingsMapperInterface {

  @Select("SELECT value FROM settings WHERE name = #{name}")
  String getValue(@Param("name") String name);

  @Insert("INSERT INTO settings VALUES(#{name}, #{value})")
  void setValue(@Param("name") String name,
                @Param("value") String value);
}

This defines two methods, one for getting a value from our settings table and one for inserting a value into our settings table. Note that the package name matches what we defined as the "basePackage" for the scanner bean above. Remember that this is recursive so we could have put this in package "com.foo.persistence.settings" and it would still work. In fact, for larger systems, organizing your mappers by package is probably not a bad idea.

Next, let's define a Spring service that is responsible for interacting with the database. This is our DAO layer or business layer. We don't want to pollute our controller with business logic and vice versa - i.e., we don't want to put any UI or action logic in our business layer. Here's what the service class looks like:

package com.foo.service;

import com.foo.persistence.SettingsMapperInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SettingService {

  public String getValue(String name) {
    return this.settingsMapper.getValue(name);
  }

  @Transactional
  public void setValue(String name, String value) {
    this.settingsMapper.setValue(name, value);
  }

  @Autowired
  private SettingsMapperInterface settingsMapper;
}

Recall how we said above that the scanner automatically creates Spring beans for each of our mappers. Here, you can see we are autowiring this bean into our service class. One note on IDEs, my IntelliJ instance complains about there not being a bean defined with the SettingsMapperInterface type. This is annoying, but it's not a problem. At the time of this writing, I could not find any MyBatis support for IntelliJ.

Finally, we can autowire our service up like any other bean as follows:

package com.foo.controller;

import com.foo.service.SettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class SettingController {

  @RequestMapping(value = "/foo/getSetting",
                  method = RequestMethod.GET)
  public void getSetting(HttpServletRequest request,
                         HttpServletResponse response) {

    String settingName = request.getParameter("settingName");
    String settingValue = this.settingService.getValue(settingName);

    // do something with "settingValue"
  }

  @Autowired
  private SettingService settingService;
}

Here, we pull the name of the setting we want to retrieve off of the request as a parameter (called "settingName") and pass it to the service method for retrieving a setting's value from the database. We can then take this value and do anything we want with it.

0 comments:

Post a Comment