ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring 采用纯注解整合 MyBatis 和 Junit

2022-03-20 20:36:12  阅读:242  来源: 互联网

标签:Spring id public import MyBatis org com Junit


Spring 整合 MyBatis 和 Junit 等第三方组件,可以通过 XML 配置方式,也可以通过纯注解的配置方式。这里仅仅提供纯注解的配置方式,因为绝大多数情况下,企业开发都采用注解配置方式,因为注解配置比较简单方便,我个人也比较喜欢注解配置方式。

本篇博客不会详细介绍所用到的 Spring 注解,网上资料一大堆,初级开发人员可以自行查找相关资料学习。这里主要是介绍 Spring 如何通过纯注解的方式整合 MyBatis 和 Junit,在本篇博客的最后会提供 Demo 的源代码。


一、搭建工程

新建一个 maven 项目,导入相关 jar 包,我导入的都是最新版本的 jar 包,内容如下:

有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。

<dependencies>
    <!--
    Spring 相关的 jar 包:
    spring-context 基础核心 jar 包
    spring-jdbc 提供数据库访问的基础核心 jar 包
    spring-test 提供测试的基本核心 jar 包,用于整合 junit
    -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.17</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.17</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.17</version>
        <scope>test</scope>
    </dependency>

    <!--
    连接操作数据库相关的 jar 包:
    mysql-connector-java 连接 mysql 的 jar 包
    druid 阿里巴巴的数据库连接池的 jar 包
    -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>

    <!--
    MyBatis 的相关 jar 包:
    mybatis-spring 这个由 Mybatis 提供,让 Spring 整合 Mybatis 的 jar 包
    -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version>
    </dependency>

    <!--
    其它相关 jar 包:
    junit 单元测试方法编写所需要的 jar 包
    commons-lang3 这个是 apache 提供的实用的公共类工具 jar 包
    -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.9</version>
    </dependency>
</dependencies>

打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。

搭建好的项目工程整体目录比较简单,具体如下图所示:

image

项目工程结构简单介绍:

config 文件夹中存放的是 Spring 的配置类

dao 文件夹存放的是 Mybatis 操作数据库的接口类,以及复杂 SQL 语句的方法实现类

domain 文件夹存放是具体的 Java Bean 实体对象类

service 文件夹存放的是业务处理实现类

EmployeeApp 这个是该 demo 的 main 入口所在类,使用 Spring 访问数据库,验证搭建成果

resources 目录下存放的是连接数据库的相关参数的配置文件

test 目录下 EmployeeTest 这个是测试方法类,里面编写了测试 EmployeeService 使用 MyBatis 访问数据库的所有方法


二、Spring 整合 MyBatis 的相关配置细节

本 demo 采用只采用一张表 employee 员工表进行演示,数据库是 testdb ,具体创建的 sql 语句内容如下:

# 创建数据库 testdb
CREATE DATABASE IF NOT EXISTS `testdb`;
USE `testdb`;

# 创建数据表 employee
# 字段为:主键id,员工姓名,薪水
CREATE TABLE IF NOT EXISTS `employee` (
  `e_id` int(11) NOT NULL AUTO_INCREMENT,
  `e_name` varchar(50) DEFAULT NULL,
  `e_salary` int(11) DEFAULT NULL,
  PRIMARY KEY (`e_id`)
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8;

# 添加相关的测试数据
INSERT INTO `employee` (`e_id`, `e_name`, `e_salary`) VALUES
	(1, '侯胖胖', 25000),
	(2, '杨磅磅', 23000),
	(3, '李吨吨', 33000),
	(4, '任肥肥', 35000),
	(5, '乔豆豆', 32000),
	(6, '任天蓬', 38000),
	(7, '任政富', 40000);

resources 下的 jdbc.properties 配置 mysql 数据库的连接信息

mysql.driver=com.mysql.cj.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/testdb?useSSL=false
mysql.username=root
mysql.password=123456

在 config 目录下,JdbcConfig 类是采用 druid 数据库连接池配置数据库连接的类,具体内容如下:

package com.jobs.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

//这里采用 @PropertySource 注解,获取 resources 目录下的 jdbc.properties 文件内容
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {

    //这里采用 @Value 注解获取 jdbc.properties 文件中每个 key 的配置值
    @Value("${mysql.driver}")
    private String driver;

    @Value("${mysql.url}")
    private String url;

    @Value("${mysql.username}")
    private String userName;

    @Value("${mysql.password}")
    private String password;

    //这里采用 @Bean 注解,表明该方法返回连接数据库的数据源对象
    //由于我们只有这一个数据源,因此不需要使用 BeanId 进行标识
    @Bean
    public DataSource getDataSource(){
        //采用阿里巴巴的 druid 数据库连接池的数据源
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

在 Config 目录下 MybatisConfig 类用来配置 SqlSession 工厂对象和定义的访问数据库的 Mapper 接口,具体内容如下:

package com.jobs.config;

import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {

    //这里的 Bean 由 Spring 根据类型自动调用,因此不需要指定 BeanId
    //使用 @Autowired 注解,Spring 自动根据类型将上面的 druid 的数据源赋值到这里
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //这里配置,将 com.jobs.domain 下的所有 JavaBean 实体类的名字作为别名
        //这样 MyBatis 中可以直接使用类名,而不需要使用完全限定名
        ssfb.setTypeAliasesPackage("com.jobs.domain");
        ssfb.setDataSource(dataSource);

        //这里配置,让 MyBatis 在运行时,控制台打印 sql 语句,方便排查问题
        Configuration mybatisConfig = new Configuration();
        mybatisConfig.setLogImpl(StdOutImpl.class);
        ssfb.setConfiguration(mybatisConfig);

        return ssfb;
    }

    //配置 MyBatis 使用 com.jobs.dao 下所有的接口,生成访问数据库的代理类
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.jobs.dao");
        return msc;
    }
}

在 Config 目录下,SpringConfig 类即为 Spring 的配置类,其将 JdbcConfig 类和 MybatisConfig 类整合在一起。

需要注意的是 SpringConfig 类最好采用 @Import 注解导入其它配置类,不要将相关的配置方法写到这里,否则会出现无法获取配置信息的问题。

package com.jobs.config;

import org.springframework.context.annotation.*;

//@Configuration 这个注解,表明此类是 Spring 的配置类
@Configuration
@ComponentScan("com.jobs")
@Import({JdbcConfig.class, MybatisConfig.class})
public class SpringConfig {
}

三、Spring 采用 MyBatis 操作数据库细节

有了上面 Spring 对 MyBatis 的整合配置,下面 Spring 采用 MyBatis 操作数据库就很简单了,开发人员根本不需要编写访问数据库的任何代码,只需要定义访问数据库的接口,配置接口所需要采用的 SQL 语句即可,大大提高了开发效率。

Employee 实体类的具体内容如下:

package com.jobs.domain;

//注意:为了贴合实际开发场景,这里故意定义的【对象字段名称】与【数据库表字段名称】完全不相同
//在下面的 Dao 接口中,采用 @Results 注解,建立【对象字段名称】与【数据库表字段名称】的对应关系
public class Employee {

    private Integer id;
    private String name;
    private Integer salary;

    //建议空参构造函数,必须要有,因为项目中的各种框架一般都会使用
    public Employee() {
    }

    public Employee(Integer id, String name, Integer salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    //这里省去了 3 个字段的 get 和 set 方法.....

    //重写 toString 方法,目的在于能够把实体对象打印出来,方便查看
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }
}

在 EmployeeDao 类中,定义访问数据库的方法,MyBatis 会自动根据其定义的接口生成代理类。

这里采用注解的方式直接在 EmployeeDao 类中定义的各个方法上编写 SQL 语句,对于比较复杂的 SQL 语句,可以采用额外的方法进行返回,具体内容如下:

package com.jobs.dao;

import com.jobs.domain.Employee;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface EmployeeDao {

    //添加员工
    @ResultMap("employee_map")
    @Insert("insert into employee(e_id,e_name,e_salary) values(#{id},#{name},#{salary})")
    Integer add(Employee emp);

    //修改员工信息
    @ResultMap("employee_map")
    @Update("update employee set e_name=#{name},e_salary=#{salary} where e_id=#{id}")
    Integer update(Employee emp);

    //查询出所有员工,按照id升序排列
    //在 select 方法上定义 employee_map
    //建立 Employee 实体类的属性与数据库表 employee 的字段对应关系
    @Results(id = "employee_map", value = {
            @Result(column = "e_id", property = "id"),
            @Result(column = "e_name", property = "name"),
            @Result(column = "e_salary", property = "salary")})
    @Select("select e_id,e_name,e_salary from employee order by e_id")
    List<Employee> selectAll();

    //根据条件,查询员工
    //这里故意让传递的参数名称不一样,只要使用 @Parm 进行规范即可
    @ResultMap("employee_map")
    @SelectProvider(type = EmployeeSQL.class, method = "getSelectByConditionSQL")
    List<Employee> selectByCondition(@Param("ename") String aaa,
                                     @Param("salaryStart") Integer bbb,
                                     @Param("salaryEnd") Integer ccc);

    //传入一个或多个id,删除指定的员工
    //这里故意让传递的参数名称不一样,只要使用 @Parm 进行规范即可
    @DeleteProvider(type = EmployeeSQL.class, method = "getDeleteByIdsSQL")
    Integer deleteByIds(Integer... empids);
}

注意:为了贴合实际开发场景,上面故意将接口中最后 2 个方法的 SQL 编写难度提升,这样就不能直接在接口方法中编写 SQL 语句,必须通过其它类的方法获取相关的 SQL 语句。

对于从其它类的方法获取相关的 SQL 语句的方法,为了贴合实际开发场景,这里故意定义【接口方法的参数名称】与【获取SQL语句的方法的参数名称】完全不一样,从而可以在两个方法上同时使用 @Param 注解,建议参数传递的对应关系。

最后 2 个方法获取相关 SQL 语句的 EmployeeSQL 类内容如下:

package com.jobs.dao;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;

public class EmployeeSQL {

    //这里故意让传递的参数名称不一样,只要使用 @Parm 进行规范即可
    //尽量使用 @Parm 中的参数名称,这样可以防止 SQL 注入攻击
    public String getSelectByConditionSQL(@Param("ename") String xxx,
                                          @Param("salaryStart") Integer yyy,
                                          @Param("salaryEnd") Integer zzz) {
        StringBuilder sql = new StringBuilder();
        sql.append(" select e_id,e_name,e_salary from employee where 1=1");

        if (StringUtils.isNotBlank(xxx)) {
            sql.append(" and (e_name like CONCAT('%',#{ename},'%'))");
        }
        if (yyy != null) {
            sql.append(" and e_salary >= #{salaryStart}");
        }
        if (zzz != null) {
            sql.append(" and e_salary <= #{salaryEnd}");
        }

        /*if (StringUtils.isNotBlank(xxx)) {
            sql.append(" and (e_name like '%" + xxx + "%')");
        }
        if (yyy != null) {
            sql.append(" and e_salary >=" + yyy);
        }
        if (zzz != null) {
            sql.append(" and e_salary <=" + zzz);
        }*/

        sql.append(" order by e_id");
        return sql.toString();
    }

    //这里故意让传递的参数名称不一样
    //如果不需要防止 sql 注入攻击的话,使用普通参数即可
    public String getDeleteByIdsSQL(Integer... ppp) {
        StringBuilder sql = new StringBuilder();
        sql.append("delete from employee");

        if (ppp != null && ppp.length > 0) {
            String idsql = StringUtils.join(ppp, ",");
            sql.append(" where e_id in (").append(idsql).append(")");
        } else {
            sql.append(" where 1=2");
        }

        return sql.toString();
    }
}

可以发现上面根本没有编写访问数据库的具体代码,只是定义接口,以及在接口上定义 SQL 语句即可。

然后我们编写 EmployeeService 类中对应的方法,调用接口中的相关方法,实现对数据库的访问。实际场景中 Service 中的一个方法,有可能根据业务需要调用好多 Dao 层中的方法,这里没有什么复杂的业务,所以就每个 Service 方法调用相应的 Dao 方法了,具体内容如下:

package com.jobs.service;

import com.jobs.dao.EmployeeDao;
import com.jobs.domain.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

//采用 @Service 注解将 EmployeeService 的实例化对象添加到 Spring 容器中
//默认是单例模式
@Service("employeeService")
public class EmployeeService {

    //采用 @Autowired 将 MyBatis 根据接口动态生成的代理对象注入到这里
    @Autowired
    private EmployeeDao employeeDao;

    public Integer add(Employee emp) {
        return employeeDao.add(emp);
    }

    public Integer update(Employee emp) {
        return employeeDao.update(emp);
    }

    public List<Employee> selectAll() {
        return employeeDao.selectAll();
    }

    public List<Employee> selectByCondition(String name, Integer start, Integer end) {
        return employeeDao.selectByCondition(name, start, end);
    }

    public Integer deleteByIds(Integer... eids) {
        return employeeDao.deleteByIds(eids);
    }
}

最后我们编写 EmployeeApp 类中的 main 方法,测试一下 Spring 采用纯注解的方式整合 MyBatis 的成果。

package com.jobs;

import com.jobs.config.SpringConfig;
import com.jobs.domain.Employee;
import com.jobs.service.EmployeeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;

public class EmployeeApp {
    public static void main(String[] args) {
        //通过注解配置类获取 Spring 容器
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        //从 Spring 容器中获取 EmployeeService 对象
        EmployeeService empService = (EmployeeService) ctx.getBean("employeeService");
        //调用其获取所有员工的方法,并打印获取的结果
        List<Employee> emplist = empService.selectAll();
        for (Employee emp : emplist) {
            System.out.println(emp);
        }
    }
}

执行后的结果如下图所示:

image


四、Spring 整合 Junit 相关细节

Spring 工程搭建完毕后,想要测试其中的一些方法,确实不太方便,比如本 demo 就必须得在 EmployeeApp 中的 main 方法中进行编码测试。为了能够更好的编写测试方法,我们就必须让 Spring 整合 Junit ,这样就能够在独立的测试类中,通过 Spring 容器注入相关的对象,从而测试对象的方法调用。

Spring 整合 Junit 非常简单,主要就两步:导入 2 个 jar 包 和 使用 2 个注解。

第一步:导入 2 个 jar 包。

在本篇博客的最开始,已经在 pom.xml 文件中导入 2 个相应的 jar 包,具体内容为:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.17</version>
    <scope>test</scope>
</dependency>

第二步:使用 2 个注解。

@RunWith(SpringJUnit4ClassRunner.class) :这个是固定写法,这里不做过多解释。

@ContextConfiguration(classes = Spring 的配置类) :指定 Spring 的配置类。
在本 demo 中 Spring 的配置类是 SpringConfig 类。

根据上面 2 个步骤,我们编写 Spring 整合 Junit 的测试类 EmployeeTest,具体内容如下:

package com.jobs;

import com.jobs.config.SpringConfig;
import com.jobs.domain.Employee;
import com.jobs.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class EmployeeTest {

    //由于 Spring 已经整合了 Junit
    //因此这里可以直接使用 @Autowired 注解根据类型注入 EmployeeService 对象
    @Autowired
    private EmployeeService employeeService;

    //添加三个新员工信息
    @Test
    public void add() {
        Employee emp1 = new Employee(100, "候菲特", 50000);
        Integer result1 = employeeService.add(emp1);
        System.out.println(result1);

        Employee emp2 = new Employee(101, "任盖茨", 60000);
        Integer result2 = employeeService.add(emp2);
        System.out.println(result2);

        Employee emp3 = new Employee(102, "李政富", 70000);
        Integer result3 = employeeService.add(emp3);
        System.out.println(result3);
    }

    //修改一名员工的信息
    @Test
    public void update() {
        Employee emp = new Employee(100, "任首富", 80000);
        Integer result = employeeService.update(emp);
        System.out.println(result);
    }

    //查询所有员工
    @Test
    public void selectAll() {
        List<Employee> emplist = employeeService.selectAll();
        System.out.println(emplist);
    }

    //根据相关条件查询筛选员工
    @Test
    public void selectByCondition() {
        String name = "任";
        int start = 30000;
        int end = 60000;
        List<Employee> emplist = employeeService.selectByCondition(name, start, end);
        System.out.println(emplist);
    }

    //删除指定id的一个或多个员工
    @Test
    public void deleteByIds() {
        Integer result = employeeService.deleteByIds(100, 101, 102);
        System.out.println(result);
    }
}

到此为止,已经完成了 Spring 采用纯注解的方式整合 MyBatis 和 Junit,并且在方法执行过程中能够打印出所执行的 SQL 语句,方便查看和排查问题。

本 demo 贴合实际开发,故意制造了一些麻烦,并进行解决,Demo 代码经过测试没有任何问题,方便大家参考。

最后提供本 Demo 的源代码:https://files.cnblogs.com/files/blogs/699532/spring_mybatis_junit.zip



标签:Spring,id,public,import,MyBatis,org,com,Junit
来源: https://www.cnblogs.com/studyjobs/p/16031344.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有