SpringBoot整合MyBatis访问MySQL(注解版)

1. 前提说明

如果大家做过很多业务系统的话,相信应该遇到过很多使用MyBatis访问数据库,本文章主要是介绍如何在SpringBoot整合MyBatis并通过注解的方式实现对关系型数据库(MySQL)进行增删改查操作。

2. 技术点整合

  • SpringBoot
  • SpringMVC
  • Spring Core
  • MyBatis
  • Druid | 数据库连接池

3. 整合MyBatis

3.1 介绍mybatis-spring-boot-starter的工作原理
  1. 自动发现一个注册好的DataSource,以前的applicationContext.xml手动配置现在就不用了,@Configuration作用替代掉以前的xml配置文件;
  2. 自动创建一个SqlSessionFactory,并且将DataSource传入SqlSessionFactory中;
  3. 自动基于SqlSessionFactory创建一个SqlSessionTemplate;
  4. 扫描所有的Mapper,将SqlSessionTemplate注入其中,然后将Mapper注册到Spring容器上下文中
3.2 引入依赖

初始化SpringBoot项目,并引入相关依赖,具体如下:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 引入mybatis-spring-boot-starter依赖,用于mybatis与spring boot整合 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- 引入mysql驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <!--<scope>runtime</scope>-->
        </dependency>

        <!-- 引入阿里的druid连接池依赖 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
3.3 application.properties数据库连接配置

SpringBoot在JDBC模块中默认的数据源是HikariCP,我这边使用的是阿里的Druid作为数据库连接池。

# MySQL驱动配置信息
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource  
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/oa?useUnicode=true&characterEncoding=utf-8  
spring.datasource.username=root
spring.datasource.password=123321
spring.datasource.driverClassName=com.mysql.jdbc.Driver

# 连接池的配置信息

## 连接池初始大小
spring.datasource.initialSize=5  
## 连接池最小空闲连接数量
spring.datasource.minIdle=5  
## 连接池最大活跃连接数量
spring.datasource.maxActive=20  
spring.datasource.maxWait=60000  
spring.datasource.timeBetweenEvictionRunsMillis=60000  
spring.datasource.minEvictableIdleTimeMillis=300000  
spring.datasource.validationQuery=SELECT 1 FROM DUAL  
spring.datasource.testWhileIdle=true  
spring.datasource.testOnBorrow=false  
spring.datasource.testOnReturn=false  
spring.datasource.poolPreparedStatements=true  
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20  
# spring.datasource.filters=stat,wall,log4j
spring.datasource.filters=stat,wall,slf4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
3.4 创建一张测试表,比如:employee表,其中包含id(INT)、name(VARCHAR)、age(INT)字段。
CREATE TABLE `employee` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;
3.5 编写Druid连接池配置管理类
/**
 * Druid连接池配置管理类
 *
 * @author bamaw
 * @date 2021-04-11  09:30
 *
 *
 *
 * 拓展:
 * 我们现在要配置一个数据源,是基于开源的数据库连接池的组件来配置,如:c3p0、druid.
 * 在没有springboot出现之前,我们使用传统的spring整合的方式,必然是在applicationContext.xml中去配置一个bean,
 * datasource所有的配置都是放在applicationContext.xml中的
 *
 * 但是,现在springboot的核心思想是将全部的配置放在 application.properties中用于集中管理所有配置,同时将以前
 * xml中的配置bean的形式通过java代码的注解形式配置bean
 *
 * 使用@Configuration标注,表示这是一个配置管理类,在这个类中可以将外部的application.properties中需要的配置加载进来,
 * 使用@Value注解即可加载外部配置
 *
 */
@Configuration
public class DruidConfig {
    
    

    /**
     * 因为spring boot是默认开启了资源过滤的
     * 所以这里的配置,都会自动从application.properties配置文件中加载出来,设置到这个@Configuration类中
     */

    @Value("${spring.datasource.url}")
    private String dbUrl;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driverClassName}")
    private String driverClassName;
    @Value("${spring.datasource.initialSize}")
    private int initialSize;
    @Value("${spring.datasource.minIdle}")
    private int minIdle;
    @Value("${spring.datasource.maxActive}")
    private int maxActive;
    @Value("${spring.datasource.maxWait}")
    private int maxWait;
    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;
    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;
    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;
    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;
    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;
    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;
    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;
    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;
    @Value("${spring.datasource.filters}")
    private String filters;
    @Value("{spring.datasource.connectionProperties}")
    private String connectionProperties;


    /**
     * 在这个配置类中,直接基于配置信息,创建出了一个bean,这个bean就是一个DataSource,
     * 这个DataSource bean就会被纳入spring容器的管理范围之内
     * @return
     */

    /**
     * @Bean
     * 当我们手头拥有所有的配置信息之后,就可以使用@Bean注解 + 方法的形式,在方法中基于加载进来的配置参数来
     * 创建你需要的各种bean,并将其返回,这个bean就会被注册到spring容器中去;
     *
     * @Primary
     * 比如我们返回的datasource类型的bean,spring在进行bean装配的时候,比如有其他某个类用@Autowired来要求
     * 装配一个Datasource bean,但是此时如果你的应用中配置了多个Datasource类型的bean,那么就会装配失败
     * 所以在这个bean中标注一个@Primary 那么在出现多个同一类型的bean的时候,就会选择加了@primary的bean
     *
     */
    @Bean
    @Primary
    public DataSource dataSource(){
    
    
        // 这里就是用外部加载进来的配置信息,创建出来一个Druid连接池
        DruidDataSource datasource = new DruidDataSource();

        datasource.setUrl(this.dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);

        //configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
    
    
            datasource.setFilters(filters);
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
        datasource.setConnectionProperties(connectionProperties);

        return datasource;
    }

}
3.6 修改application类中导入配置管理类
@SpringBootApplication
// 使用@Import就可以将其他的配置管理类导入进来
@Import(DruidConfig.class)
public class SpringbootMybatisApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringbootMybatisApplication.class, args);
    }

}
3.7 编写Domain类,创建Emplyee表映射的对象Emplyee
public class Employee {
    
    

    private Long id;
    private String name;
    private Integer age;

    public Long getId() {
    
    
        return id;
    }

    public void setId(Long id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
3.7 创建Employee对应的Mapper接口
@Mapper
public interface EmployeeMapper {
    
    


    /**
     * 获取所有的员工信息
     * @return 所有员工清单
     */
    @Select("SELECT * FROM employee")
    @Results({
    
    
            @Result(property = "id", column = "id", id = true),
            @Result(property = "name", column = "name"),
            @Result(property = "age", column = "age")
    })
    List<Employee> selectAllEmployee();


    /**
     * 通过员工id查询对应的员工信息
     * @param id 员工id
     * @return 单个员工信息
     */
    @Select("SELECT * FROM employee WHERE id = #{id}")
    @Results({
    
    
            @Result(property = "id", column = "id", id = true),
            @Result(property = "name", column = "name"),
            @Result(property = "age", column = "age")
    })
    Employee selectEmployeeById(@Param("id") Long id);


    /**
     * 新增员工信息
     * @param employee 员工信息
     */
    @Insert("INSERT INTO employee(name,age) VALUES(#{name},#{age})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insertEmployee(Employee employee);


    /**
     * 修改员工信息
     * @param employee 员工信息
     */
    @Update("UPDATE employee SET `name` = #{name}, age = #{age} WHERE id = #{id}")
    void updateEmployeeById(Employee employee);

    /**
     * 通过员工id 删除信息
     * @param id 员工id
     */
    @Delete("DELETE FROM employee WHERE id = #{id}")
    void deleteEmployeeById(@Param("id") Long id);

}
3.7.1 注解配置说明
  1. 使用@Param

@Param的作用是给参数命名,根据名字得到参数值,并将参数值传入sql语句中

  1. 使用@Results及@Result注解

@Result注解中的属性进行介绍:

  • id:是否是主键字段
  • column:数据库的字段名
  • property:对象的成员名
  1. 增删改查操作提供的不同注解
  • @Insert:实现新增
  • @Update:实现更新
  • @Delete:实现删除
  • @Select:实现查询
3.8 创建Employee对应的DAO接口及其实现类
public interface EmployeeDAO {
    
    

    /**
     * 查询所有员工的信息
     *
     * @return 员工list集合
     */
    List<Employee> getAllEmployee();

    /**
     * 通过员工id 查询对应的员工信息
     * @param id 员工id
     * @return 员工实体类
     */
    Employee findEmployeeById(Long id);

    /**
     * 保存员工信息
     *
     * @param employee 员工实体类
     */
    void saveEmployee(Employee employee);

    /**
     * 根据员工id 更新员工信息
     *
     * @param employee 员工实体类
     */
    void updateEmployeeById(Employee employee);

    /**
     * 删除对应的员工信息
     *
     * @param id 员工id
     */
    void removeEmployeeById(Long id);
}
@Repository
public class EmployeeDAOImpl implements EmployeeDAO {
    
    


    /**
     * 员工管理模块的mapper组件
     */
    @Autowired
    private EmployeeMapper employeeMapper;


    @Override
    public List<Employee> getAllEmployee() {
    
    
        return employeeMapper.selectAllEmployee();
    }

    @Override
    public Employee findEmployeeById(Long id) {
    
    
        return employeeMapper.selectEmployeeById(id);
    }

    @Override
    public void saveEmployee(Employee employee) {
    
    
        employeeMapper.insertEmployee(employee);

    }

    @Override
    public void updateEmployeeById(Employee employee) {
    
    
        employeeMapper.updateEmployeeById(employee);

    }

    @Override
    public void removeEmployeeById(Long id) {
    
    
        employeeMapper.deleteEmployeeById(id);

    }
}
3.9 创建Employee对应的Service接口及其实现类
public interface EmployService {
    
    


    /**
     * 查询所有员工的信息
     *
     * @return 员工list集合
     */
    List<Employee> getAllEmployee();

    /**
     * 通过员工id 查询对应的员工信息
     * @param id 员工id
     * @return 员工实体类
     */
    Employee findEmployeeById(Long id);

    /**
     * 保存员工信息
     *
     * @param employee 员工实体类
     */
    void saveEmployee(Employee employee);

    /**
     * 根据员工id 更新员工信息
     *
     * @param employee 员工实体类
     */
    void updateEmployeeById(Employee employee);

    /**
     * 删除对应的员工信息
     *
     * @param id 员工id
     */
    void removeEmployeeById(Long id);
}

@Service
public class EmployServiceImpl implements EmployService {
    
    


    /**
     * 员工管理模块DAO组件
     */
    @Autowired
    private EmployeeDAO employeeDAO;


    /**
     * 查询所有员工的信息
     *
     * @return 员工list集合
     */
    @Override
    public List<Employee> getAllEmployee() {
    
    
        return employeeDAO.getAllEmployee();
    }

    /**
     * 通过员工id 查询对应的员工信息
     * @param id 员工id
     * @return 员工实体类
     */
    @Override
    public Employee findEmployeeById(Long id) {
    
    
        return employeeDAO.findEmployeeById(id);
    }

    /**
     * 保存员工信息
     *
     * @param employee 员工实体类
     */
    @Override
    public void saveEmployee(Employee employee) {
    
    
        employeeDAO.saveEmployee(employee);
    }

    /**
     * 根据员工id 更新员工信息
     *
     * @param employee 员工实体类
     */
    @Override
    public void updateEmployeeById(Employee employee) {
    
    
        employeeDAO.updateEmployeeById(employee);
    }

    /**
     * 删除对应的员工信息
     *
     * @param id 员工id
     */
    @Override
    public void removeEmployeeById(Long id) {
    
    
        employeeDAO.removeEmployeeById(id);
    }
}

3.10 创建Employee对应的Controller类
@RestController
@RequestMapping(value = "/api/v1.0/employee")
public class EmployeeController {
    
    


    /**
     * 员工管理模块Service组件
     */
    @Autowired
    private EmployService employService;


    /**
     * 查询所有员工的信息
     *
     * @return 员工list集合
     */
    @GetMapping
    public List<Employee> getAllEmployee() {
    
    
        return employService.getAllEmployee();
    }

    /**
     * 通过员工id 查询对应的员工信息
     * @param id 员工id
     * @return 员工实体类
     */
    @GetMapping(value = "/find/{employeeId}")
    public Employee findEmployeeById(@PathVariable(name = "employeeId") Long id) {
    
    
        return employService.findEmployeeById(id);
    }

    /**
     * 保存员工信息
     *
     * @param employee 员工实体类
     */
    @PostMapping("/")
    public String saveEmployee(@RequestBody Employee employee) {
    
    
        employService.saveEmployee(employee);
        return "success";

    }

    /**
     * 根据员工id 更新员工信息
     *
     * @param employee 员工实体类
     */
    @PutMapping(value = "update/{employeeId}")
    public String updateEmployeeById(@PathVariable(value = "employeeId") Long employeeId,Employee employee) {
    
    
        employee.setId(employeeId);
        employService.updateEmployeeById(employee);
        return "success";
    }

    /**
     * 删除对应的员工信息
     *
     * @param id 员工id
     */
    @DeleteMapping(value = "remove/{employeeId}")
    public String removeEmployeeById(@PathVariable(value = "employeeId") Long id) {
    
    
        employService.removeEmployeeById(id);
        return "success";
    }

}
3.11 启动程序并访问执行

我们可以通过postman对Employee表做增删改查的操作。如:我们进行删操作

在这里插入图片描述

4. 梳理总结

  1. 系统在启动的时候,首先会先扫描DruidConfig类,将外部的Druid连接池配置加载进来,同时初始化出来一个Druid连接池的DataSource bean对象出来;

  2. 接着通过mybatis-spring-boot-starter发现了一个DataSource bean对象,就会将其注入到SqlSessionFactory,再创建SqlSessionTemplate。通过扫描Mapper接口,将SqlSessionTemplate注入每个Mapper,然后将Mapper放入spring容器中来管理;

  3. Spring Boot的@ComponantScan注解会自动扫描所有的bean,依次初始化实例以及注入依赖,EmployeeServiceImpl(将EmployeeMapper注入其中),EmployeeCongtroller(将EmployeeServiceImpl注入其中);

  4. 此时浏览器发送请求后,会先由controlller处理,接着调用service,再调用dao,接着调用mapper,mapper底层会基于druid连接池访问到数据库,进行数据操作。

5. 代码示例

本文的相关例子可以查看下面仓库中的目录:

-Github:https://github.com/bamaw/springboot2.x-examples

-Gitee: https://gitee.com/bamaw/springboot2.x-examples

如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!

猜你喜欢

转载自blog.csdn.net/weixin_43980975/article/details/115604899