Spring 4 + MyBatis integration issue using Java 8
Asked Answered
B

1

3

I'm facing the following exception when using Java 8 (1.8.0_60), Spring 4.2.1 and MyBatis 3.3.0

Sep 29, 2015 11:02:58 AM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@246b179d: startup date [Tue Sep 29 11:02:58 EDT 2015]; root of context hierarchy
Sep 29, 2015 11:02:58 AM org.mybatis.spring.mapper.ClassPathMapperScanner checkCandidate
WARNING: Skipping MapperFactoryBean with name 'userMapper' and 'com.poc.test.spring4app.mapper.UserMapper' mapperInterface. Bean already defined with the same name!
Sep 29, 2015 11:02:58 AM org.mybatis.spring.mapper.ClassPathMapperScanner doScan
WARNING: No MyBatis mapper was found in '[com.poc.test.spring4app.mapper]' package. Please check your configuration.
Creating tables
Sep 29, 2015 11:02:59 AM org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userMapper' defined in com.poc.test.spring4app.config.AppConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.poc.test.spring4app.mapper.UserMapper]: Factory method 'userMapper' threw exception; nested exception is org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
    at com.poc.test.spring4app.config.AppInitializer.main(AppInitializer.java:16)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.poc.test.spring4app.mapper.UserMapper]: Factory method 'userMapper' threw exception; nested exception is org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
    ... 13 more
Caused by: org.apache.ibatis.binding.BindingException: Type interface com.poc.test.spring4app.mapper.UserMapper is not known to the MapperRegistry.
    at org.apache.ibatis.binding.MapperRegistry.getMapper(MapperRegistry.java:47)
    at org.apache.ibatis.session.Configuration.getMapper(Configuration.java:675)
    at org.mybatis.spring.SqlSessionTemplate.getMapper(SqlSessionTemplate.java:293)
    at com.poc.test.spring4app.config.AppConfiguration.userMapper(AppConfiguration.java:51)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$$8dd4b125.CGLIB$userMapper$0(<generated>)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$$8dd4b125$$FastClassBySpringCGLIB$$677c8295.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:318)
    at com.poc.test.spring4app.config.AppConfiguration$$EnhancerBySpringCGLIB$$8dd4b125.userMapper(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 14 more

The application has been running fine using JDK 1.7 & Spring 3.2.2. But since Spring 4 is needed for JDK upgrade, I'm facing these compatibility issues.

Below are the sample code details. The below POC is based on the example in this blog. Thanks to Mike for that. The app works fine if I change Spring version from 4.2.1 to 3.2.9. In case of 4.2.1, I get the above exception.

POM

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.poc.test</groupId>
<artifactId>spring4app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Spring4App</name>
<url>http://maven.apache.org</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>4.2.1.RELEASE</spring.version>
    <spring-batch.version>3.0.5.RELEASE</spring-batch.version>
    <mybatisVersion>3.3.0</mybatisVersion>
    <mybatisSpringVersion>1.2.3</mybatisSpringVersion>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.batch</groupId>
        <artifactId>spring-batch-core</artifactId>
        <version>${spring-batch.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.batch</groupId>
        <artifactId>spring-batch-test</artifactId>
        <version>${spring-batch.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- DB RELATED -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatisVersion}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatisSpringVersion}</version>
    </dependency>
    <!-- Binding for Log4J -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <!-- H2Db related -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.3.167</version>
    </dependency>
</dependencies>
<build>
    <finalName>Spring4App</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.18.1</version>
            <configuration>
                <includes>
                    <include>**/${includeTestGroup}/**/*Test.java</include>
                </includes>
            </configuration>

        </plugin>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.5.3</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <finalName>Spring4App</finalName>
                <appendAssemblyId>false</appendAssemblyId>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.6</version>
            <executions>
                <execution>
                    <goals>
                        <goal>test-jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

AppInitializer

package com.poc.test.spring4app.config;

import java.util.List;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;

import com.poc.test.spring4app.domain.User;
import com.poc.test.spring4app.mapper.UserMapper;

public class AppInitializer {

    public static void main(String[] args) {
        AbstractApplicationContext context = null;
        try {
            context = new AnnotationConfigApplicationContext(
                    AppConfiguration.class);
            context.registerShutdownHook();
            UserMapper userMapper = context.getBean(UserMapper.class);
            List<User> users = userMapper.getAllUsers();
            System.out.println(users.toString());
            context.close();
        }
        catch (final Exception e) {
            if (context != null) {
                context.close();
            }
        }
    }
}

AppConfiguration

package com.poc.test.spring4app.config;

import javax.sql.DataSource;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;

import com.poc.test.spring4app.mapper.UserMapper;

@Configuration
@MapperScan("com.poc.test.spring4app.mapper")
public class AppConfiguration {

    @Bean
    public DataSource dataSource() {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        dataSource.setDriverClass(org.h2.Driver.class);
        dataSource.setUsername("sa");
        dataSource.setUrl("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
        dataSource.setPassword("");
        // create a table and populate some data
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        System.out.println("Creating tables");
        jdbcTemplate.execute("drop table users if exists");
        jdbcTemplate.execute("create table users(id serial, firstName varchar(255), lastName varchar(255), email varchar(255))");
        jdbcTemplate.update("INSERT INTO users(firstName, lastName, email) values (?,?,?)", "FirstName", "LastName", "[email protected]");
        return dataSource;
     }

    @Bean
    public DataSourceTransactionManager transactionManager() {
         return new DataSourceTransactionManager(dataSource());
    }
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource());
        sessionFactory.setTypeAliasesPackage("com.poc.test.spring4app.mapper");
        return sessionFactory;
     }

    @Bean
    public UserMapper userMapper() throws Exception {
        final SqlSessionTemplate sessionTemplate = new SqlSessionTemplate(sqlSessionFactory().getObject());
        return sessionTemplate.getMapper(UserMapper.class);
    }
}

User Mapper

package com.poc.test.spring4app.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.poc.test.spring4app.domain.User;

public interface UserMapper {

    @Select("select id, firstName, lastName, email from users")
    @Results(value={
            @Result(column="id", property="id"),
            @Result(column="firstName", property="firstName"),
            @Result(column="lastName", property="lastName"),
            @Result(column="email", property="email")
    })
    List<User> getAllUsers();

}

User

package com.poc.test.spring4app.domain;

import java.io.Serializable;

public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    private long id;
    private String firstName;
    private String lastName;
    private String email;
    /**
     * @return the id
     */
    public long getId() {
        return id;
    }
    /**
     * @param id the id to set
     */
    public void setId(long id) {
        this.id = id;
    }
    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }
    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }
    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }
    /**
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }

    // getters and setters

 }
Bubbly answered 28/9, 2015 at 18:14 Comment(2)
We use Java 8, Spring 4, Spring Boot with latest MyBatis and mybatis-spring. This stack works without problems. Show the code.Desireah
@Desireah I updated the question with the code. Thanks in advance.Bubbly
D
3

I believe this might be your problem:

 WARNING: Skipping MapperFactoryBean with name 'userMapper' and  'com.poc.test.spring4app.mapper.UserMapper' mapperInterface. Bean already defined with the same name!

Try to remove that duplicity.

Solution to the problem:

Using Spring 4 & MyBatis 3, Mappers shouldn't be defined explicitly if you're already @MapperScan to scan the MyBatis Mapper implementations. The instantiation of the beans are handled automatically.

Desireah answered 29/9, 2015 at 15:47 Comment(2)
Thanks for pointing it luboskrnac! This POC has helped; I had Spring warnings in my actual app suppressed; so likely missed it. But do u have an idea why it works in Spring 3.x but not 4?Bubbly
Sorry, no clue, didn't have a chance to use MyBatis with Spring 3. Was using different libraries in my Spring 3 days.Desireah

© 2022 - 2024 — McMap. All rights reserved.