二、MyBatis 使用注解配置 SQL 总结

本篇博客介绍 MyBatis 基于注解配置 SQL 语句的实现方式,这种实现方式非常简单方便,我个人也比较喜欢这种方式。在实际的企业开发中,注解的实现方式也比 XML 的实现方式要多一些。还是那句话:具体采用哪种方式取决于公司的开发规定,建议两种开发方式都要掌握。

MyBatis 的官网地址为:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

本篇博客以上篇博客中的 demo 为基础,把基于 XML 配置 SQL 语句的实现方式,改造成基于注解配置 SQL 的实现方式,最终实现的效果一模一样。在本篇博客的最后面,将会提供源代码。


一、导入 jar 包

新建一个 Maven 项目,在 pom.xml 文件中导入 4 个 jar 包:

mysql 的 jar 包:https://mvnrepository.com/artifact/mysql/mysql-connector-java

mybatis 的 jar 包:https://mvnrepository.com/artifact/org.mybatis/mybatis

log4j 的 jar 包:https://mvnrepository.com/artifact/log4j/log4j

junit 的 jar 包:https://mvnrepository.com/artifact/junit/junit

我在 pom.xml 配置的都是当前最新版本的 jar 包,如下所示:

<!--导入 mysql 的 jar 包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

<!--导入 mybatis 的 jar 包-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.9</version>
</dependency>

<!--导入 log4j 的 jar 包-->
<!--主要是为了查看 mybatis 生成的最终 sql 语句-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

<!--导入 junit 的 jar 包-->
<!--方便编写测试方法,对 mybatis 操作数据库进行测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

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


二、搭建整个工程

整个项目工程的结构和整体内容,如下图所示,

image

项目工程结构简单介绍:

com.jobs.bean 包下存放的是实体类
com.jobs.mapper 包下存放的是 Mybatis 操作数据库的接口
com.jobs.mapper.sql 包下存放的是给注解提供 SQL 语句的方法实现类
com.jobs.service 包下存放的是使用 Mybatis 的类库操作数据的业务方法

resources 目录下的配置文件介绍:
jdbc.properties 是数据库连接参数的文件
log4j.properties 是配置 log4j 日志输出参数的文件
MyBatisConfig.xml 是 MyBatis 的核心配置文件

test 目录下的文件介绍:
com.jobs.employeeTest 类是专门用来编写 junit 单元测试方法的,用来测试 MyBatis 是否搭建成功


三、编写配置文件内容

jdbc.properties 文件配置数据库连接信息,内容如下:

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

log4j.properties 文件配置控制台日志输出的信息,内容如下:

# 输出 debug 以上的所有类型的日志信息(DEBUG INFO WARN ERROR FATAL)
# stdout 配置项的名称(表明使用下面的 log4j.appender.stdout 对应的配置)
log4j.rootLogger=DEBUG, stdout
# log4j.appender.stdout 配置的是控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 该项配置表示要自定义日志显示的格式
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 自定义日志显示格式:
# %p 表示输出每条日志的级别信息(DEBUG INFO WARN ERROR FATAL)
# %t 表示输出产生日志的线程的名称
# %m 表示输出具体的日志信息
# %n 表示根据当前操作系统类型,输出一个回车换行
log4j.appender.stdout.layout.ConversionPattern=%p [%t] - %m%n

MyBatisConfig.xml 文件是 MyBatis 的核心配置文件,配置内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!--引入MyBatis 的 DTD 约束-->
<!DOCTYPE configuration PUBLIC
        "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!--引入数据库连接的配置文件-->
    <properties resource="jdbc.properties"/>

    <!--引入 log4j 日志,方便查看运行过程中所生成的 sql 语句-->
    <settings>
        <setting name="logImpl" value="log4j"/>
    </settings>

    <!--为实体类 bean 起别名,这样就可以在 SQL 配置文件中直接使用 bean 的类名代替全限定类名-->
    <typeAliases>
        <!--使用 package 将指定包下的所有类进行自动取别名-->
        <package name="com.jobs.bean"/>
    </typeAliases>

    <!--environments 配置数据库环境,环境可以有多个。default 属性指定使用的是哪个-->
    <environments default="mysql">
        <!--environment配置数据库环境,id属性唯一标识-->
        <environment id="mysql">
            <!--transactionManager 事务管理,这里采用 JDBC 默认的事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--dataSource 数据源信息,这里采用连接池-->
            <dataSource type="POOLED">
                <!--通过引入的 jdbc.properties 文件中的 key 获取数据库连接的配置信息-->
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>

    <!-- mappers 引入操作数据库的接口映射配置文件 -->
    <mappers>
        <!--使用 package 将指定包下的所有类进行自动映射-->
        <package name="com.jobs.mapper"/>
    </mappers>
</configuration>

相比于 XML 配置 SQL 语句的实现方式,基于注解配置 SQL 语句的实现方式,对 MyBatis 核心配置文件的更改,只在最后面的 mappers 配置上有差别,其配置的是具体的 mapper 接口所在的包。


四、数据库表和实体类

MySQL 数据库下创建了 testdb 数据库,里面创建了一张 employee 表,具体 SQL 语句如下:

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

USE `testdb`;

-- 创建表
CREATE TABLE IF NOT EXISTS `employee` (
  `e_id` int(11) NOT NULL AUTO_INCREMENT,
  `e_name` varchar(50) DEFAULT NULL,
  `e_age` int(11) DEFAULT NULL,
  PRIMARY KEY (`e_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

-- 向表中添加数据
INSERT INTO `employee` (`e_id`, `e_name`, `e_age`)
VALUES(1, '侯胖胖', 25),(2, '杨磅磅', 23),
(3, '李吨吨', 33),(4, '任肥肥', 35),(5, '乔豆豆', 32);

com.jobs.bean.employee 实体类的内容如下:(这里故意让【实体类的字段】与【数据库的字段】名称不一样

package com.jobs.bean;

public class employee {
    //主键id
    private Integer id;
    //姓名
    private String name;
    //年龄
    private Integer age;

    public employee() {
    }

    public employee(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer 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 +
                '}';
    }
}

五、编写提供 SQL 语句类

对于一些比较复杂的 SQL 语句,不太方便使用注解进行直接编写,最佳方案就是通过调用一个方法进行获取,因此这里在 com.jobs.mapper.sql 包下的 employeeMapperSQL 类中编写两个方法,分别用于获取【根据条件查询】和【根据多 id 查询】两个比较复杂的 SQL 语句,内容如下:

package com.jobs.mapper.sql;

import com.jobs.bean.employee;
import java.util.List;

public class employeeMapperSQL {

    //获取条件查询的动态拼接 SQL 语句
    public String getSelectConditionSQL(employee ele) {
        StringBuilder sb = new StringBuilder();
        sb.append("select e_id,e_name,e_age from employee");
        if (ele != null) {
            sb.append(" where 1=1");
            if (ele.getId() != null) {
                sb.append(" and e_id=#{id}");
            }

            if (ele.getName() != null) {
                sb.append(" and e_name like CONCAT('%',#{name},'%')");
            }

            if (ele.getAge() != null) {
                sb.append(" and e_age = #{age}");
            }
        }

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

    //获取多个id查询条件拼接后的 SQL 语句
    public String getSelectByIdsSQL(List<Integer> ids) {
        StringBuilder sb = new StringBuilder();
        sb.append("select e_id,e_name,e_age from employee");
        if (ids != null && ids.size() > 0) {
            sb.append(" where e_id in (");
            for (int i = 0; i < ids.size(); i++) {
                if (i == 0) {
                    sb.append(ids.get(0));
                } else {
                    sb.append(",").append(ids.get(i));
                }
            }

            sb.append(")");
        }

        return sb.toString();
    }
}

六、在 Mapper 接口上通过注解配置 SQL 语句

下面列出 com.jobs.mapper.employeeMapper 这个 Mapper 接口 的内容:

package com.jobs.mapper;

import com.jobs.bean.employee;
import com.jobs.mapper.sql.employeeMapperSQL;
import org.apache.ibatis.annotations.*;
import java.util.List;

public interface employeeMapper {

    //查询全部
    @Results(id="employee_map",value = {
            @Result(column = "e_id",property = "id"),
            @Result(column = "e_name",property = "name"),
            @Result(column = "e_age",property = "age")})
    @Select("select e_id,e_name,e_age from employee order by e_id")
    List<employee> selectAll();

    //根据id查询
    @ResultMap("employee_map")
    @Select("select e_id,e_name,e_age from employee where e_id = #{id}")
    employee selectById(Integer id);

    //新增数据
    @Insert("insert into employee(e_id,e_name,e_age) values(#{id},#{name},#{age})")
    Integer insert(employee ele);

    //修改数据
    @Update("update employee SET e_name=#{name},e_age=#{age} where e_id = #{id}")
    Integer update(employee ele);

    //删除数据
    @Delete("delete FROM employee where e_id = #{id}")
    Integer delete(Integer id);

    //根据条件查询
    @ResultMap("employee_map")
    @SelectProvider(type = employeeMapperSQL.class , method = "getSelectConditionSQL")
    List<employee> selectCondition(employee ele);

    //根据多个id查询
    @ResultMap("employee_map")
    @SelectProvider(type = employeeMapperSQL.class , method = "getSelectByIdsSQL")
    List<employee> selectByIds(List<Integer> ids);
}

由于我们故意让数据库中的 employee 表字段名称与 java 的实体类 bean 中的 employee 字段名称不一致,所以这里需要使用 @Results 注解建立数据库字段与实体类字段的对应关系,并给与一个 id 值 employee_map 。建立好关系之后,其它方法如果返回结果也是 employee 类型的话,可以通过 @ResultMap("employee_map") 引用建立好的对应关系,非常方便。

对于 selectCondition 和 selectByIds 两个接口方法,由于其使用的 SQL 语句比较复杂,因此不太方便直接在注解上进行编写,所以通过 @SelectProvider 注解去引用 employeeMapperSQL 类中的对应方法,非常方便。


六、编写 Service 方法访问数据库

Service 里面的方法定义可以与 Mapper 接口中的方法不一样,这里为了方便,就保持一致了。
com.jobs.service.employeeService 的内容如下所示:

package com.jobs.service;

import com.jobs.bean.employee;
import com.jobs.mapper.employeeMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class employeeService {

    //查询所有
    public List<employee> selectAll() {
        List<employee> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            //加载核心配置文件
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            //获取SqlSession工厂对象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            //通过工厂对象获取SqlSession对象,openSession 的参数为 true 表示自动提交事务
            //对于增删改操作,如果 openSession 为 false,
            //在程序代码最后需要通过 sqlSession.commit() 提交事务,才能使写操作生效
            sqlSession = sqlSessionFactory.openSession(true);
            //通过动态代理获取StudentMapper接口的实现类对象
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            //调用接口方法
            list = mapper.selectAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

    //根据id查询单个员工
    public employee selectById(Integer id) {
        employee ele = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            ele = mapper.selectById(id);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return ele;
    }

    //新增
    public Integer insert(employee ele) {
        Integer result = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            result = mapper.insert(ele);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    //修改
    public Integer update(employee ele) {
        Integer result = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            result = mapper.update(ele);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    //删除
    public Integer delete(Integer id) {
        Integer result = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            result = mapper.delete(id);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    //根据条件查询
    public List<employee> selectCondition(employee ele) {
        List<employee> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            list = mapper.selectCondition(ele);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

    //根据多个id查询
    public List<employee> selectByIds(List<Integer> ids) {
        List<employee> list = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream("MyBatisConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
            sqlSession = sqlSessionFactory.openSession(true);
            employeeMapper mapper = sqlSession.getMapper(employeeMapper.class);
            list = mapper.selectByIds(ids);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }
}

上面有很多重复代码,有兴趣的话,可以自己进行一下封装。实际上也没有封装的必要,因为后面使用 Spring 框架集成了 MyBatis 后,上面的很多代码根本就不需要写了。


七、使用 junit 测试验证

com.jobs.employeeTest 类编写的测试方法内容如下所示:

package com.jobs;

import com.jobs.bean.employee;
import com.jobs.service.employeeService;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class employeeTest {

    private employeeService service = new employeeService();

    //查询全部测试
    @Test
    public void selectAll() {
        List<employee> elist = service.selectAll();
        for (employee ele : elist) {
            System.out.println(ele);
        }
    }

    //根据id查询测试
    @Test
    public void selectById() {
        employee ele = service.selectById(3);
        System.out.println(ele);
    }

    //新增测试
    @Test
    public void insert() {
        employee ele = new employee(6, "任天蓬", 26);
        Integer result = service.insert(ele);
        System.out.println(result);
    }

    //修改测试
    @Test
    public void update() {
        employee ele = new employee(6, "任天蓬", 16);
        Integer result = service.update(ele);
        System.out.println(result);
    }

    //根据条件动态生成SQL语句查询测试
    @Test
    public void selectCondition() {
        //可以同时启用多个条件,根据条件动态拼接 SQL 语句
        employee ele = new employee();
        //ele.setId(4); //通过 id 查询
        ele.setName("任"); //通过姓名模糊查询
        //ele.setAge(35); //通过年龄查询
        List<employee> elelist = service.selectCondition(ele);
        for (employee empl : elelist) {
            System.out.println(empl);
        }
    }

    //根据多个 id 查询测试
    @Test
    public void selectByIds() {
        ArrayList<Integer> ids = new ArrayList<>(List.of(1,2,3));
        List<employee> elelist = service.selectByIds(ids);
        for (employee empl : elelist) {
            System.out.println(empl);
        }
    }

    //删除测试
    @Test
    public void delete() {
        Integer result = service.delete(6);
        System.out.println(result);
    }
}

OK,到此为止, Mybatis 通过注解配置 SQL 语句的实现方式,已经快速介绍完毕,希望对大家有用,源代码下载地址如下:
https://files.cnblogs.com/files/blogs/699532/MybatisAnnoCode.zip



posted @ 2022-02-27 14:47  乔京飞  阅读(9728)  评论(0编辑  收藏  举报