添加依赖
这里需要添加mybatis-spring-boot-starter依赖跟mysql依赖
<!--最新版本,匹配spring Boot1.5 or higher-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
MyBatis-Spring-Boot-Starter依赖将会提供如下:
- 自动检测现有的DataSource
- 将创建并注册SqlSessionFactory的实例,该实例使用SqlSessionFactoryBean将该DataSource作为输入进行传递
- 将创建并注册从SqlSessionFactory中获取的SqlSessionTemplate的实例。
- 自动扫描您的mappers,将它们链接到SqlSessionTemplate并将其注册到Spring上下文,以便将它们注入到您的bean中。
就是说,使用了该Starter之后,只需要定义一个DataSource即可(application.properties中可配置),它会自动创建使用该DataSource的SqlSessionFactoryBean以及SqlSessionTemplate。会自动扫描你的Mappers,连接到SqlSessionTemplate,并注册到Spring上下文中。
数据源配置
在src/main/resources/application.properties中配置数据源信息。
spring.datasource.url = jdbc:mysql://localhost:3306/spring?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
自定义数据源
Spring Boot默认使用tomcat-jdbc数据源,如果你想使用其他的数据源,比如这里使用了阿里巴巴的数据池管理,除了在application.properties
配置数据源之外,你应该额外添加以下依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.19</version>
</dependency>
修改Application.java
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import javax.sql.DataSource;
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private Environment env;
//destroy-method="close"的作用是当数据库连接不使用的时候,就把该连接重新放到数据池中,方便下次使用调用.
@Bean(destroyMethod = "close")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));//用户名
dataSource.setPassword(env.getProperty("spring.datasource.password"));//密码
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setInitialSize(2);//初始化时建立物理连接的个数
dataSource.setMaxActive(20);//最大连接池数量
dataSource.setMinIdle(0);//最小连接池数量
dataSource.setMaxWait(60000);//获取连接时最大等待时间,单位毫秒。
dataSource.setValidationQuery("SELECT 1");//用来检测连接是否有效的sql
dataSource.setTestOnBorrow(false);//申请连接时执行validationQuery检测连接是否有效
dataSource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。
dataSource.setPoolPreparedStatements(false);//是否缓存preparedStatement,也就是PSCache
return dataSource;
}
}
ok这样就算自己配置了一个DataSource,Spring Boot会智能地选择我们自己配置的这个DataSource实例。
脚本初始化
CREATE DATABASE /*!32312 IF NOT EXISTS*/`spring` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `spring`;
DROP TABLE IF EXISTS `learn_resource`;
CREATE TABLE `learn_resource` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`author` varchar(20) DEFAULT NULL COMMENT '作者',
`title` varchar(100) DEFAULT NULL COMMENT '描述',
`url` varchar(100) DEFAULT NULL COMMENT '地址链接',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1029 DEFAULT CHARSET=utf8;
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (999,'官方SpriongBoot例子','官方SpriongBoot例子','https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1000,'龙果学院','Spring Boot 教程系列学习','http://www.roncoo.com/article/detail/124661');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1001,'嘟嘟MD独立博客','Spring Boot干货系列','http://tengj.top/');
insert into `learn_resource`(`id`,`author`,`title`,`url`) values (1002,'后端编程嘟','Spring Boot视频教程','http://www.toutiao.com/m1559096720023553/');
注解方式跟XML配置方式共同的模块编码
不管是注解方式还是XML配置的方式,以下代码模块都是一样的
实体对象
public class LearnResouce {
private Long id;
private String author;
private String title;
private String url;
// SET和GET方法
}
Controller层
@Controller
public class HelloController {
@Autowired
LearnMapper learnMapper;
@RequestMapping("hello")
public ModelAndView hello(HttpServletResponse response) throws IOException {
// response.getWriter().println("hello");
LearnResouce learnResouce = new LearnResouce();
learnResouce.setAuthor("zhangsan");
learnMapper.add(learnResouce);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("hello");
modelAndView.addObject("username","zhangsan");
return modelAndView;
}
}
Mybatis集成
XML配置方式
xml配置方式保持映射文件的老传统,优化主要体现在不需要实现dao的是实现层,系统会自动根据方法名在映射文件中找对应的sql,具体操作如下:
编写Dao层的代码
新建LearnMapper接口,无需具体实现类。
package com.dudu.dao;
@Mapper
@Component
public interface LearnMapper {
int add(LearnResouce learnResouce);
int update(LearnResouce learnResouce);
int deleteByIds(String[] ids);
LearnResouce queryLearnResouceById(Long id);
public List<LearnResouce> queryLearnResouceList(Map<String, Object> params);
}
修改application.properties 配置文件
#指定bean所在包
mybatis.type-aliases-package=com.dudu.domain
#指定映射文件
mybatis.mapperLocations=classpath:mapper/*.xml
添加LearnMapper的映射文件
在src/main/resources目录下新建一个mapper目录,在mapper目录下新建LearnMapper.xml文件。
通过mapper标签中的namespace属性指定对应的dao映射,这里指向LearnMapper。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dudu.dao.LearnMapper">
<resultMap id="baseResultMap" type="com.dudu.domain.LearnResouce">
<id column="id" property="id" jdbcType="BIGINT" />
<result column="author" property="author" jdbcType="VARCHAR"/>
<result column="title" property="title" jdbcType="VARCHAR"/>
<result column="url" property="url" jdbcType="VARCHAR"/>
</resultMap>
<sql id="baseColumnList" >
id, author, title,url
</sql>
<select id="queryLearnResouceList" resultMap="baseResultMap" parameterType="java.util.HashMap">
select
<include refid="baseColumnList" />
from learn_resource
<where>
1 = 1
<if test="author!= null and author !=''">
AND author like CONCAT(CONCAT('%',#{author,jdbcType=VARCHAR}),'%')
</if>
<if test="title != null and title !=''">
AND title like CONCAT(CONCAT('%',#{title,jdbcType=VARCHAR}),'%')
</if>
</where>
</select>
<select id="queryLearnResouceById" resultMap="baseResultMap" parameterType="java.lang.Long">
SELECT
<include refid="baseColumnList" />
FROM learn_resource
WHERE id = #{id}
</select>
<insert id="add" parameterType="com.dudu.domain.LearnResouce" >
INSERT INTO learn_resource (author, title,url) VALUES (#{author}, #{title}, #{url})
</insert>
<update id="update" parameterType="com.dudu.domain.LearnResouce" >
UPDATE learn_resource SET author = #{author},title = #{title},url = #{url} WHERE id = #{id}
</update>
<delete id="deleteByIds" parameterType="java.lang.String" >
DELETE FROM learn_resource WHERE id in
<foreach item="idItem" collection="array" open="(" separator="," close=")">
#{idItem}
</foreach>
</delete>
</mapper>
使用MyBatis3提供的注解可以逐步取代XML,例如使用@Select注解直接编写SQL完成数据查询,使用@SelectProvider高级注解还可以编写动态SQL,以应对复杂的业务需求。
一. 基础注解
MyBatis 主要提供了以下CRUD注解:
@Select
@Insert
@Update
@Delete
增删改查占据了绝大部分的业务操作,掌握这些基础注解的使用还是很有必要的,例如下面这段代码无需XML即可完成数据查询:
@Mapperpublic interface UserMapper { @Select("select * from t_user") List<User> list();}
使用过Hibernate的同学可能会好奇,这里为什么没有配置映射关系也能完成属性注入?在传统项目中使用过Mybatis的童鞋可能很快就反应过来,是因为在配置文件中开启了全局驼峰映射,SpringBoot中同样能够做到,并且更为简单快捷。
虽然开启了全局驼峰映射,但你可能还会质疑,如果不符合下划线转驼峰规则的字段,拿查询回来的实体对象属性将获取为null,比如上述User对象属性mobileNum和对应的数据库字段phoneNum,则查询结果为:
[ { "userId": "1", "username": "admin", "password": "admin", "mobileNum": null }, { "userId": "2", "username": "roots", "password": "roots", "mobileNum": null }]
为了解决对象属性和字段驼峰不一致的问题,我们可以使用映射注解@Results来指定映射关系。
二. 映射注解
Mybatis主要提供这些映射注解:
@Results 用于填写结果集的多个字段的映射关系.
@Result 用于填写结果集的单个字段的映射关系.
@ResultMap 根据ID关联XML里面<resultMap>.
例如上面的list方法,我们可以在查询SQL的基础上,指定返回的结果集的映射关系,其中property表示实体对象的属性名,column表示对应的数据库字段名。
@Results({ @Result(property = "userId", column = "USER_ID"), @Result(property = "username", column = "USERNAME"), @Result(property = "password", column = "PASSWORD"), @Result(property = "mobileNum", column = "PHONE_NUM") }) @Select("select * from t_user") List<User> list();
查询结果如下:
[ { "userId": "1", "username": "admin", "password": "admin", "mobileNum": "15011791234" }, { "userId": "2", "username": "roots", "password": "roots", "mobileNum": "18812342017" }]
为了解决对象属性和字段驼峰不一致的问题,我们可以使用映射注解@Results来指定映射关系。
为了方便演示和免除手工编写映射关系的烦恼,这里提供了一个快速生成映射结果集的方法,具体内容如下:
/** * 1.用于获取结果集的映射关系 */ public static String getResultsStr(Class origin) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("@Results({\n"); for (Field field : origin.getDeclaredFields()) { String property = field.getName(); //映射关系:对象属性(驼峰)->数据库字段(下划线) String column = new PropertyNamingStrategy.SnakeCaseStrategy().translate(field.getName()).toUpperCase(); stringBuilder.append(String.format("@Result(property = \"%s\", column = \"%s\"),\n", property, column)); } stringBuilder.append("})"); return stringBuilder.toString(); }
在当前Main方法执行效果如下:然后我们将控制台这段打印信息复制到接口方法上即可。
三. 高级注解
MyBatis-3 主要提供了以下CRUD的高级注解:
@SelectProvider
@InsertProvider
@UpdateProvider
@DeleteProvider
见名知意,这些高级注解主要用于动态SQL,这里以@SelectProvider 为例,主要包含两个注解属性,其中type表示工具类,method 表示工具类的某个方法,用于返回具体的SQL。
@Mapperpublic interface UserMapper {
@SelectProvider(type = UserSqlProvider.class, method = "list222")
List<User> list2();
}
工具类代码如下:
public class UserSqlProvider {
public String list222() {
return "select * from t_user ;
}
四. 详细教程
对上述注解有所了解之后,我们以具体项目案例来进一步巩固这些注解的实际使用。
1. 引入依赖
为了方便演示,首选搭建Web环境,另外数据库选择Mysql 5.5+。
<dependencies>
<dependency>
<!--添加Web依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<!--添加Mybatis依赖 -->
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version>
</dependency>
<dependency>
<!--添加MySQL驱动依赖 -->
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <scope>runtime</scope>
</dependency>
<dependency>
<!--添加Test依赖 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 添加配置
这里主要是添加数据源,配置驼峰映射和开启SQL日志的控制台打印。在项目的资源目录中,添加 application.yml 配置如下:
spring: datasource:
#连接MySQL
url: jdbc:mysql://localhost:3306/socks?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Drivermybatis: configuration:
#配置项:开启下划线到驼峰的自动转换.
作用:将数据库字段根据驼峰规则自动注入到对象属性。
map-underscore-to-camel-case: truelogging: level:
#打印SQL信息
com.hehe.mapper: debug
3. 编写数据层代码
这里以我们熟悉的用户信息为例,编写UserMapper接口和本案例使用的UserSqlProvider。
3.1 UserMapper
添加UserMapper接口用于数据查询:
package com.hehe.mapper;
@Mapperpublic interface UserMapper {
/** * 方式1:使用注解编写SQL。 */
@Select("select * from t_user") List<User> list();
/** * 方式2:使用注解指定某个工具类的方法来动态编写SQL. */
@SelectProvider(type = UserSqlProvider.class, method = "listByUsername")
List<User> listByUsername(String username);
/** * 延伸:上述两种方式都可以附加@Results注解来指定结果集的映射关系.
* * PS:如果符合下划线转驼峰的匹配项可以直接省略不写。 */
@Results({@Result(property = "userId", column = "USER_ID"),
@Result(property = "username", column = "USERNAME"),
@Result(property = "password", column = "PASSWORD"),
@Result(property = "mobileNum", column = "PHONE_NUM") })
@Select("select * from t_user") List<User> listSample();
/** * 延伸:无论什么方式,如果涉及多个参数,则必须加上@Param注解,否则无法使用EL表达式获取参数。 */
@Select("select * from t_user where username like #{username} and password like #{password}")
User get(@Param("username") String username, @Param("password") String password);
@SelectProvider(type = UserSqlProvider.class, method = "getBadUser")
User getBadUser(@Param("username") String username, @Param("password") String password);}
3.2 UserSqlProvider
添加UserSqlProvider,用于生成SQL的工具类 。
/*** 主要用途:根据复杂的业务需求来动态生成SQL.* <p>* 目标:使用Java工具类来替代传统的XML文件.(例如:UserSqlProvider.java
<-- UserMapper.xml)*/
public class UserSqlProvider {
/** * 方式1:在工具类的方法里,可以自己手工编写SQL。 */
public String listByUsername(String username) {
return "select * from t_user where username =#{username}";
}
/** * 方式2:也可以根据官方提供的API来编写动态SQL。 */
public String getBadUser(@Param("username") String username, @Param("password") String password) {
return new SQL() {{SELECT("*");FROM("t_user");
if (username != null && password != null) {
WHERE("username like #{username} and password like #{password}");
} else {
WHERE("1=2");
}
}}.toString();
}}
3.3 实体类User
添加实体类User
public class User {
private String userId;
private String username;
private String password;
private String mobileNum;
//Getters & Setters
}
3.4 添加数据库记录
打开Navicat 查询窗口,然后只需下面这段脚本。
USE `SOCKS`;DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` ( `USER_ID` varchar(50) , `USERNAME` varchar(50) , `PASSWORD` varchar(50) , `PHONE_NUM` varchar(15) )
;
INSERT INTO `t_user` VALUES ('1', 'admin', 'admin','15011791234');INSERT INTO `t_user` VALUES ('2', 'roots', 'roots','18812342017');
4. 编写控制层代码
package com.hehe.controller;
@RestController@RequestMapping("/user/*")
public class UserController {
@SuppressWarnings("all")
@Autowired
UserMapper userMapper;
@GetMapping("list")
public List<User> list() {
return userMapper.list();
}
@GetMapping("list/{username}")
public List<User> listByUsername(@PathVariable("username") String username) { return userMapper.listByUsername(username);
}
@GetMapping("get/{username}/{password}") public User get(@PathVariable("username") String username, @PathVariable("password") String password) {
return userMapper.get(username, password);
}
@GetMapping("get/bad/{username}/{password}")
public User getBadUser(@PathVariable("username") String username, @PathVariable("password") String password) {
return userMapper.getBadUser(username, password);
}}
5. 启动和测试
启动工程后,访问 http://localhost:8080/user/list 可以查看用户列表如下:
访问 http://localhost:8080/user/list/admin 可以查询用户名为admin的信息: