Tomáš Piňos
23.9.2010

Spring – MyBatis integrace a něco navíc



Článek popíše možnosti integrace Spring a MyBatis frameworků a představí užitečné rozšíření – factory na automatickou registraci Mapper rozhranní implementovaných XML definicí.

 

myBatis

 

Pokud chceme na projektu integrovat Spring a MyBatis frameworky, nemáme zatím k dispozici oficiální Springem podporované řešení. Spring označoval MyBatis za “moving target”, ale oznámil, že ho bude podporovat ve Spring Data projektu. MyBatis komunita zatím zahájila vlastní snahu a její implementaci integrace už jde používat. Máme tedy k dispozici:

Protože hotové řešení potřebujeme tady a teď, používáme na aktuálním projektu první zmíněnou verzi, která k naší spokojenosti funguje s MyBatisem 3.0.2 a Springem 3.0.4. Použití je disktuváno v rámci JIRA issue, XML konfigurace pro představu může vypadat takto:

<bean id="sqlSessionFactory" class="org.springframework.orm.ibatis3.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="configLocation" value="classpath:sale-mybatis-config.xml" />
  <property name="mapperLocations">
    <list>
      <value>classpath:sale/*-mapper.xml</value>
    </list>
  </property>
</bean>
<bean id="baseDao" abstract="true" class="org.springframework.orm.ibatis3.MapperFactoryBean">
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="saleDao" parent="baseDao">
  <property name="mapperInterface" value="sale.SaledDao" />
</bean>

Něco navíc

Pokud pracujete s mnoha Dao třídami (resp. s Mappery), XML konfigurace se začne “protivně” rozrůstat o jejich definice. Nabízí se možnost označit mapper rozhranní vhodnou anotací a registraci přenechat nějaké factory, která by to zvládla sama. Cílem je použít místo abstraktního baseDao a konkrétního saleDao (a dalších) z předchozího příkladu podobnou konstrukci:

<bean class="mybatis.MyBatisDaoFactory">
  <property name="basePackage" value="sale.dao"/>
  <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

Pro označení mapper rozhranní jsme zavedli anotaci @MyBatisDao:

@MyBatisDao
public interface SaleDao {
  Long create(Sale sale);
  ... 
}

Implementace MyBatisDaoFactory je nakonec docela jednoduchá. K nalezení kandidátů na mapper použijeme classpath scanning, k registraci jejich implementací potom BeanDefinitionRegistryPostProcessor, který dovoluje přidávat do BeanFactory nové programově vytvořené instance. Samotné vytvoření instance mapperu necháme na org.springframework.orm.ibatis3.MapperFactoryBean, stejně jako v předchozím příkladu. Klíčová metoda postProcessBeanDefinitionRegistry potom vypadá docela jednoduše.

public class MyBatisDaoFactory implements BeanDefinitionRegistryPostProcessor {
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    Iterable<Class> classes = ClasspathUtils.getClasses(basePackage);
    for (Class clazz : classes) {
      if (clazz.isAnnotationPresent(MyBatisDao.class)) {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(MapperFactoryBean.class).getBeanDefinition();
        beanDefinition.getPropertyValues().addPropertyValue("sqlSessionFactory", sqlSessionFactory);
        beanDefinition.getPropertyValues().addPropertyValue("mapperInterface", clazz.getCanonicalName());
        registry.registerBeanDefinition(clazz.getName(), beanDefinition);
      }
    }
  }
}

Pomocná třída ClasspathUtils implementuje classpath scanning podle popisu na StackOverflow. Pokud se chcete vlastní implementaci vyhnout a nevadí vám závislost na API MyBatisu, můžete využít jeho třídu ResolverUtil. Tento příklad také ukazuje, jak obecně z anotovaných rozhranní vytvořit Spring beany, pokud se jejich implementaci tvoří dynamicky.

Shrnutí

Článek popsal stav integrace Spring a MyBatis frameworků a představil jednoduché rozšíření pro automatické vyhledání a registraci mapperů implementovaných XML definicí. Používáte na projektu Spring a MyBatis dohromady? Jak řešíte integraci? Podělte se o své zkušenosti v diskuzi pod článkem.

Odkazy

Vaše emailová adresa nebude zveřejněna

Komentáře

Děkujeme za váš komentář
Další
  • Eduardo

    Hi there Tomas. I had just now started to write a component scan for mappers. I suggested it in Springs JIRA and also added an issue on mybatis. But googling... to my surprise I found you already did it. Would you mind if I add your BeanDefinitionRegistryPostProcessor to MyBatis project? Thanks!

  • Hi Eduardo, I'll be happy if the mapper scanning makes it to the integration project. Feel free to add the processor. If it helps, I can send you the package provided with unit tests.

  • Eduardo

    Great. Thanks a lot Tomas! Here it is http://code.google.com/p/mybatis/source/browse/sub-projects/mybatis-spring/trunk/src/main/java/org/mybatis/spring/annotation/ With some modifications, but its more or less you code. And yes, if you don´t mind please send me the unit test to add them also.