最简单的数据持久化方案是 JdbcTemplate,JdbcTemplate 虽然简单,但是用的并不多,因为它没有 MyBatis 方便,在 Spring + SpringMVC 中整合 MyBatis 步骤还是有点复杂的,要配置多个 Bean,Spring Boot 中对此做了进一步的简化,使 MyBatis 基本上可以做到开箱即用,本文就来看看在 Spring Boot 中 MyBatis 要如何使用。
在 pom.xml
中添加持久层相关依赖,如下:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.0.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.28version>
<scope>runtimescope>
dependency>
在 application.properties
中配置数据库的基本信息:
spring.datasource.url=jdbc:mysql:///test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
然后在 Mapper 接口上面添加 @Mapper
注解,这种方式有一个弊端就是所有的 Mapper 都要手动添加,要是落下一个就会报错,还有一个一劳永逸的办法就是直接在启动类上通过添加 @MapperScan
注解扫描,如下:
@SpringBootApplication
@MapperScan(basePackages = "com.antonio.hello.mybatis.mapper")
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
}
好了,做完这些工作就可以去测试 Mapper 的使用了。
配置完成后,MyBatis 就可以创建 Mapper 来使用了,例如我这里直接创建一个 UserMapper,如下:
public interface UserMapper {
@Select("select * from user")
List<User> getAllUsers();
@Results({
@Result(property = "id", column = "id"),
@Result(property = "username", column = "u"),
@Result(property = "address", column = "a")
})
@Select("select username as u,address as a,id as id from user where id=#{id}")
User getUserById(Long id);
@Select("select * from user where username like concat('%',#{name},'%')")
List<User> getUsersByName(String name);
@Insert({"insert into user(username,address) values(#{username},#{address})"})
@SelectKey(statement = "select last_insert_id()", keyProperty = "id", before = false, resultType = Integer.class)
Integer addUser(User user);
@Update("update user set username=#{username},address=#{address} where id=#{id}")
Integer updateUserById(User user);
@Delete("delete from user where id=#{id}")
Integer deleteUserById(Integer id);
}
这里是通过全注解的方式来写 SQL,不写 XML 文件。
@Select
、@Insert
、@Update
以及 @Delete
四个注解分别对应 XML 中的 select、insert、update 以及 delete 标签,@Results
注解类似于 XML 中的 ResultMap
映射文件(getUserById 方法给查询结果的字段取别名主要是向小伙伴们演示下 @Results
注解的用法)。
另外使用 @SelectKey
注解可以实现主键回填的功能,即当数据插入成功后,插入成功的数据 id 会赋值到 user 对象的id 属性上。
当然,开发者也可以在 XML 中写 SQL,例如创建一个 UserMapper2,如下:
public interface UserMapper2 {
List<User> getAllUser();
Integer addUser(User user);
Integer updateUserById(User user);
Integer deleteUserById(Integer id);
}
然后创建 UserMapper.xml
文件,如下:
<mapper namespace="com.antonio.hello.mybatis.mapper.UserMapper2">
<select id="getAllUser" resultType="com.antonio.hello.mybatis.model.User">
select * from t_user;
select>
<insert id="addUser" parameterType="com.antonio.hello.mybatis.model.User">
insert into user (username,address) values (#{username},#{address});
insert>
<update id="updateUserById" parameterType="com.antonio.hello.mybatis.model.User">
update user set username=#{username},address=#{address} where id=#{id}
update>
<delete id="deleteUserById">
delete from user where id=#{id}
delete>
mapper>
将接口中方法对应的 SQL 直接写在 XML 文件中。那么这个 UserMapper.xml 到底放在哪里呢?有两个位置可以放,第一个是直接放在 UserMapper 所在的包下面:
放在这里的 UserMapper.xml 会被自动扫描到,但是有另外一个 Maven 带来的问题,就是 java 目录下的 xml 资源在项目打包时会被忽略掉,所以,如果 UserMapper.xml 放在包下,需要在 pom.xml
文件中再添加如下配置,避免打包时 java 目录下的 XML 文件被自动忽略掉:
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
resource>
resources>
build>
当然,UserMapper.xml 也可以直接放在 resources 目录下,这样就不用担心打包时被忽略了,但是放在 resources 目录下,必须创建和 Mapper 接口包目录相同的目录层级,这样才能确保打包后 XML 和 Mapper 接口又处于在一起,否则 XML 文件将不能被自动扫描,这个时候就需要添加额外配置。例如我在 resources 目录下创建 mapper 目录用来放 mapper 文件,如下:
此时在 application.properties
中告诉 mybatis 去哪里扫描 mapper:
mybatis.mapper-locations=classpath:mapper/*.xml
如此配置之后,mapper 就可以正常使用了。这种方式不需要再在 pom.xml 文件中配置文件过滤。
在 SSM 整合中,开发者需要自己提供两个 Bean,一个 SqlSessionFactoryBean
,还有一个是 MapperScannerConfigurer
,在 Spring Boot 中,这两个东西虽然不用开发者自己提供了,但是并不意味着这两个 Bean 不需要了,在 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
类中,我们可以看到 Spring Boot 提供了这两个 Bean,部分源码如下:
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
return factory.getObject();
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
}
从类上的注解可以看出,当前类路径下存在 SqlSessionFactory
、SqlSessionFactoryBean
以及 DataSource
时,这里的配置才会生效,SqlSessionFactory
和 SqlTemplate
都被提供了。
更多干货请移步:https://antoniopeng.com