在Spring Boot中使用MyBatis访问数据库

MyBatis,这个对各位使用Java开发的开发者来说还是蛮重要的,我相信诸位在企业开发项目的时候,大多数采用的是Mybatis。使用MyBatis帮助我们解决各种问题,实际上这篇文章,基本上默认为可以跳过的一篇,但是为了Spring Boot的系列,我还是写了这篇文章。

MyBatis

作为一个在Hibernate之后出现的持久层框架,因为他支持自定义SQL,存储过程以及高级映射,MyBatis免除了几乎所有的JDBC代码,以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或者注解来配置和映射原始类型,接口,和Java POJO为数据库中记录。

MyBatis官网: mybatis.org/mybatis-3/z...

引入依赖

xml 复制代码
		<!--MyBatis依赖-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.1</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
        <dependency>
            <groupId>com.oracle.ojdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>19.3.0.0</version>
            <scope>compile</scope>
        </dependency>

这里说明一下,MyBatis的版本和Java JDK和Spring Boot的版本是有要求的:
2.1.x版本适用于:MyBatis 3.5+、Java 8+、Spring Boot 2.1+
2.0.x版本适用于:MyBatis 3.5+、Java 8+、Spring Boot 2.0/2.1
1.3.x版本适用于:MyBatis 3.4+、Java 6+、Spring Boot 1.5

配置Application文件

接下来,我们配置Spring Boot的自带的application配置文件:

properties 复制代码
# MySQL
#spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
#spring.datasource.username=root
#spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

server.port=1001
# Oracle
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/orcl
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver

这是我的Oracle表结构:

MySQL表结构:

sql 复制代码
CREATE TABLE `User` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

接下来创建User表的映射对象,也是我们通用的实体类对象:

java 复制代码
public class User {
    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 "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

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

    public User() {
    }
}

在有了实体类对象,并且和数据库的字段一一对应后,就可以在接口红定义相关对数据库表的操作了,这里举例,一个是查询,一个是插入,用于后续单元测试的验证。

java 复制代码
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM USER WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    @Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

}

接下来创建一个单元测试类,插入一条数据,然后利用这条数据的相关属性进行查询,并且判断相关属性是否满足。

注意,测试结束后,对数据进行回滚操作,保证单元测试每次运行的数据环境独立。

java 复制代码
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class ApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    @Rollback
    public void test() throws Exception {
        userMapper.insert("AAA", 20);
        User u = userMapper.findByName("AAA");
        Assert.assertEquals(20, u.getAge().intValue());
    }

}

对于注解配置进行说明介绍

@Mapper:用于标注Mapper接口,表示该接口是MyBatis的Mapper映射接口。在Spring Boot项目中,可以使用@MapperScan注解扫描指定包下的Mapper接口,并将其注册为Bean。

@Select:用于标注查询语句,指定SQL语句或调用Mapper XML文件中的对应SQL语句。可以通过value属性指定SQL语句,也可以通过provider属性指定动态SQL提供者类。

@Insert@Update@Delete:分别用于标注插入、更新和删除语句,使用方式与@Select类似。

@Result@Results:用于标注结果集映射关系。@Result用于标注单个字段或属性与结果集列的映射关系,@Results用于标注多个@Result。

@Param:用于标注方法参数,指定参数在SQL语句中的名称。在SQL语句中可以通过#{参数名}的方式引用该参数。

@Options:用于配置一些选项,例如设置主键生成策略、返回自增主键等。

@ResultMap:用于标注结果集映射配置的ID,可以在其他地方通过@ResultMap引用该结果集映射。

@One@Many:用于标注一对一和一对多的关联关系。

@CacheNamespace:用于开启二级缓存,将Mapper的查询结果缓存到内存中,提高查询性能。

注解 使用对象 相对应的 XML 描述
@Insert @Update @Delete @Select 方法 <insert><update><delete><select> 这四个注解分别代表将会被执行的 SQL 语句。它们用字符串数组(或单个字符串)作为参数。如果传递的是字符串数组,字符串之间先会被填充一个空格再连接成单个完整的字符串。这有效避免了以 Java 代码构建 SQL 语句时的"丢失空格"的问题。然而,你也可以提前手动连接好字符串。属性有:value,填入的值是用来组成单个 SQL 语句的字符串数组。
@Options 方法 映射语句的属性 这个注解提供访问大范围的交换和配置选项的入口,它们通常在映射语句上作为属性出现。Options 注解提供了通俗易懂的方式来访问它们,而不是让每条语句注解变复杂。属性有:useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=FORWARD_ONLY, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty="id", keyColumn="", resultSets=""。值得一提的是, Java 注解无法指定 null 值。因此,一旦你使用了 Options 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的预期以外的行为。注意: keyColumn 属性只在某些数据库中有效(如 Oracle、PostgreSQL等)。请在插入语句一节查看更多关于 keyColumn 和 keyProperty 两者的有效值详情。
@MapKey 方法 -- 这是一个用在返回值为 Map 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有: value,填入的是对象的属性名,作为 Map 的 key 值。
@SelectKey 方法 <selectKey> 这个注解的功能与 <selectKey> 标签完全一致,用在已经被 @Insert @InsertProvider@Update @UpdateProvider 注解了的方法上。若在未被上述四个注解的方法上作 @SelectKey 注解则视为无效。如果你指定了 @SelectKey 注解,那么 MyBatis 就会忽略掉由 @Options 注解所设置的生成主键或设置(configuration)属性。属性有:statement 填入将会被执行的 SQL 字符串数组,keyProperty 填入将会被更新的参数对象的属性的值,before 填入 true 或 false 以指明 SQL 语句应被在插入语句的之前还是之后执行。resultType 填入 keyProperty 的 Java 类型和用 Statement、 PreparedStatementCallableStatement 中的 STATEMENT、 PREPARED 或 CALLABLE 中任一值填入 statementType。默认值是 PREPARED。
@Param 参数 N/A 如果你的映射方法的形参有多个,这个注解使用在映射方法的参数上就能为它们取自定义名字。若不给出自定义名字,多参数(不包括 RowBounds 参数)则先以 "param" 作前缀,再加上它们的参数位置作为参数别名。例如 #{param1}, #{param2},这个是默认值。如果注解是 @Param("person"),那么参数就会被命名为 #{person}。
@InsertProvider @UpdateProvider @DeleteProvider @SelectProvider 方法 <insert> <update> <delete> <select> 允许构建动态 SQL。这些备选的 SQL 注解允许你指定类名和返回在运行时执行的 SQL 语句的方法。(自从MyBatis 3.4.6开始,你可以用 CharSequence 代替 String 来返回类型返回值了。)当执行映射语句的时候,MyBatis 会实例化类并执行方法,类和方法就是填入了注解的值。你可以把已经传递给映射方法了的对象作为参数,"Mapper interface type" 和 "Mapper method" 会经过 ProviderContext (仅在MyBatis 3.4.5及以上支持)作为参数值。(MyBatis 3.4及以上的版本,支持多参数传入)属性有: type, method。type 属性需填入类。method 需填入该类定义了的方法名。注意 接下来的小节将会讨论类,能帮助你更轻松地构建动态 SQL。

这里就围绕@Param,Map聊聊。

@Param

我们在实际开发过程中,如果将SQL语句嵌套在接口的时候,我们经常使用的方式就是这种:

java 复制代码
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);

这种方式,我们都很少理解,@Param中定义的name对应了SQL中的#{name},age对应了SQL中的#{age}。

Map

除了以@Param注入,我们也可以通过Map<String,Object> 对象作为传递参数的容器:

java 复制代码
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER})")
int insertByMap(Map<String, Object> map);

得到如上代码,那么我们应该如何注入呢?

java 复制代码
Map<String, Object> map = new HashMap<>();
map.put("name", "CCC");
map.put("age", 40);
userMapper.insertByMap(map);

如上,我们通过对map中填入同名的内容就可以实现了。

使用对象注入

除去Map方式注入值外,我们也可以直接java对象直接作为查询条件的传参,比如我们可以直接使用User对象:

java 复制代码
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insertByUser(User user);

利用MyBatis实现增删改查

MyBatis针对不同的数据库操作,分别提供了不同的注解进行配置:

java 复制代码
public interface UserMapper {

    @Select("SELECT * FROM user WHERE name = #{name}")
    User findByName(@Param("name") String name);

    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);

    @Update("UPDATE user SET age=#{age} WHERE name=#{name}")
    void update(User user);

    @Delete("DELETE FROM user WHERE id =#{id}")
    void delete(Long id);
}

之后我们可以利用创建一个Test测试类,来实现我们的功能是否正常实现:

java 复制代码
@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

	@Autowired
	private UserMapper userMapper;

	@Test
	@Rollback
	public void testUserMapper() throws Exception {
		// insert一条数据,并select出来验证
		userMapper.insert("AAA", 20);
		User u = userMapper.findByName("AAA");
		Assert.assertEquals(20, u.getAge().intValue());
		// update一条数据,并select出来验证
		u.setAge(30);
		userMapper.update(u);
		u = userMapper.findByName("AAA");
		Assert.assertEquals(30, u.getAge().intValue());
		// 删除这条数据,并select验证
		userMapper.delete(u.getId());
		u = userMapper.findByName("AAA");
		Assert.assertEquals(null, u);
	}
}

返回结果绑定

对于增、删、改操作相对的数据也就变化较小,但是对于查我们就需要考虑的比较多了,怎么查,多表查,子查,并且查询的结果也不在是我们经常碰到的实体类对象了,往往需要返回一个与数据库实体不同的包装类,那么对于这样的情况,我们可以通过@Result和@Result注解进行绑定。

java 复制代码
@Results({
    @Result(property = "name", column = "name"),
    @Result(property = "age", column = "age")
})
@Select("SELECT name, age FROM user")
List<User> findAll();

@Result中的property属性对应User对象中的成员名,column对应SELECT出的字段名。在该配置中故意没有查出id属性,只对User对应中的name和age对象做了映射配置,这样可以通过下面的单元测试来验证查出的id为null,而其他属性不为null:

java 复制代码
@Test
@Rollback
public void testUserMapper() throws Exception {
	List<User> userList = userMapper.findAll();
	for(User user : userList) {
		Assert.assertEquals(null, user.getId());
		Assert.assertNotEquals(null, user.getName());
	}
}
相关推荐
seventeennnnn15 分钟前
谢飞机的Java高级开发面试:从Spring Boot到分布式架构的蜕变之旅
spring boot·微服务架构·java面试·分布式系统·电商支付
超级小忍1 小时前
服务端向客户端主动推送数据的几种方法(Spring Boot 环境)
java·spring boot·后端
时间会给答案scidag2 小时前
报错 400 和405解决方案
vue.js·spring boot
Wyc724093 小时前
SpringBoot
java·spring boot·spring
ladymorgana4 小时前
【Spring Boot】HikariCP 连接池 YAML 配置详解
spring boot·后端·mysql·连接池·hikaricp
GJCTYU6 小时前
spring中@Transactional注解和事务的实战理解附代码
数据库·spring boot·后端·spring·oracle·mybatis
风象南7 小时前
SpringBoot敏感配置项加密与解密实战
java·spring boot·后端
写不出来就跑路8 小时前
暑期实习感悟与经验分享:从校园到职场的成长之路
java·开发语言·经验分享·spring boot
程序员张312 小时前
Maven编译和打包插件
java·spring boot·maven
灵犀学长13 小时前
EasyExcel之SheetWriteHandler:解锁Excel写入的高阶玩法
spring boot·excel