简单示例

这一节中,我们不再从头编写 MyBatis 的方法,直接使用第2、3、4、6章中已经开发好的 simple 项目。如果没有按照前面章节中的介绍实现书中提到的所有方法,可以直接下载 simple 项目,然后使用 Maven 命令(mvn install)打包到本地仓库或将该项目引入到当前的工作空间即可。simple 项目的下载地址是 http://mybatis.tk/book/simple-all.zip

引入simple依赖

做好 simple 项目准备后,在 pom.xml 中添加 simple 项目依赖,代码如下。

<dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>simple</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

引入新的依赖后,还需要配置 MyBatis,让 MyBatis 可以扫描到这个依赖下面的 Mapper 接口。上一节的例子中使用的是 @Mapper 注解,但是对于已经存在的 simple 来说,一个个去添加注解并不合适,因此要换一种新的方式来扫描 Mapper 接口。

修改 Application 类,添加 @MapperScan 注解,代码如下。

@SpringBootApplication
@MapperScan(basePackages = {"cn.liaozh.mybatis2.ch10.boot.mapper", "cn.liaozh.mybatis2.ch2.xml.mapper"}, nameGenerator = MapperNameGenerator.class)
public class BootApplication implements CommandLineRunner {
    // 省略
}

修改 Application 类后就会通过这种方式去扫描 Mapper 接口,还需要修改 application.properties,让映射文件也可以被扫描到。

mybatis.mapper-locations=classpath:mapper/.xml,classpath:tk/mybatis/*/mapper/.xml

修改好这两处配置后,在当前项目中就可以直接使用已经写好的 Mapper 接口了。但是此时如果执行 Application 类就会报错,错误信息如下。

这是因为在刚刚引入的依赖中,存在 tk.mybatis.simple.mapper.CountryMapper 接口,和上一节添加的例子 tk.mybatis.springboot.mapper.CountryMapper 的接口名完全相同。因为 MyBatis 扫描接口时会默认用首字母小写的类名作为 Spring 中 bean 的名字,使得 CountryMapper 对应的 countryMapper 相同,因此会报错。

就当前项目来说,解决这个问题有两种方式。第一种就是去掉上一节添加的这个接口,毕竟只是测试用的,去掉也无所谓。但是当项目的依赖有很多时,很有可能会出现不能删除的情况,这样就只能考虑另一种方式,即从 bean 的名字入手,修改默认的名字生成规则,让它们生成的名字不再重复。

在 tk.mybatis.springboot 包下新建 MapperNameGenerator 类,代码如下。

package cn.liaozh.mybatis2.ch10.boot;

import java.beans.Introspector;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.util.ClassUtils;

/**
 * Mapper 名字生成器
 **/
public class MapperNameGenerator implements BeanNameGenerator {
    Map<String, Integer> nameMap = new HashMap<String, Integer>();

    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        //获取类的名字,如  CountryMapper
        String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
        //将类名转换为规范的变量名,如 countryMapper
        String beanName = Introspector.decapitalize(shortClassName);
        //判断名字是否已经存在,如果存在,则在名字后面增加序号
        if (nameMap.containsKey(beanName)) {
            int index = nameMap.get(beanName) + 1;
            nameMap.put(beanName, index);
            //增加序号
            beanName += index;
        } else {
            nameMap.put(beanName, 1);
        }
        return beanName;
    }

}

该类的说明见代码注释,创建好名字生成器后,修改 Application 中的 @MapperScan 注解。

@MapperScan(value={
        "tk.mybatis.springboot.mapper",
        "tk.mybatis.simple.mapper"
    },
    nameGenerator = MapperNameGenerator.class
)

指定 nameGenerator 属性为 MapperNameGenerator.class 即可,这样在扫描所有的 Mapper 接口时,如果出现重复,就会通过增加序号的方式避免这个问题。

名字重复的问题解决了,但是代码使用 @Resource 注解注入时,可能会由于 beanName 和类型不匹配导致注入失败。

举个例子,假设 tk.mybatis.simple.mapper.CountryMapper 生成的名字为 countryMapper,tk.mybatis.springboot.mapper.CountryMapper 生成的名字为 countryMapper2。当存在如下注解代码时,通过名字注入会使用 countryMapper 对应的 tk.mybatis.simple.mapper.CountryMapper,这样就会因为类型不匹配而出错。

@Resource
private tk.mybatis.springboot.mapper.CountryMapper countryMapper;

因此在注入 Mapper 接口时,建议使用 @Autowired 注解,根据类型注入时一定不会有错。

开发业务(service)层

新建 tk.mybatis.springboot.service 包,然后在该包下面创建 UserService 接口,代码如下。

public interface UserService {

    /**
     * 通过 id 查询用户
     *
     * @param id ID
     * @return 用户信息
     */
    SysUser findById(Long id);

    /**
     * 查询全部用户
     *
     * @return 用户集合
     */
    List<SysUser> findAll();
}

再创建 tk.mybatis.springboot.service.impl 包,在该包下创建 UserServiceImpl 实现类,代码如下。

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public SysUser findById(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public List<SysUser> findAll() {
        return userMapper.selectAll();
    }

}

在该类中注入 simple 项目的 UserMapper 接口。因为前面已经配置好,所以这里可以直接注入。

开发控制(Controller)层

在 tk.mybatis.springboot.controller 包下创建 UserController 类,代码如下。

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("users/{id}")
    SysUser user(@PathVariable("id") Long id) {
        return userService.findById(id);
    }

    @RequestMapping("users")
    List<SysUser> users() {
        return userService.findAll();
    }
}

UserController 中提供了两个方法:通过 id 获取具体的用户信息的方法以及获取全部用户信息的方法。

运行应用查看效果

重新运行 Application 类,然后在浏览器地址栏输入 http://localhost:8080/users ,此时会显示出系统中所有的用户信息,显示内容如下。

如果想要查看 id 为 1001 的用户的信息,可以在浏览器地址栏输入 http://localhost:8080/users/1001 ,此时会显示出用户 1001 的个人信息。

大家可以自行添加 simple 项目中的其他方法进行尝试,关于 Spring Boot 的详细用法可以通过本章开头提到的官方文档进行了解。