SpringBoot 整合Druid、MyBatis
簡介
對於數據訪問層,無論是 SQL(關系型數據庫) 還是 NOSQL(非關系型數據庫),Spring Boot 底層都是采用 Spring Data 的方式進行統一處理。
Spring Boot 底層都是采用 Spring Data 的方式進行統一處理各種數據庫,Spring Data 也是 Spring 中與 Spring Boot、Spring Cloud 等齊名的知名項目。
數據庫相關的啟動器 : 可以參考官方文檔
JDBC
導入依賴
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
連接數據庫
首先我們需要連接數據庫,我們直接使用IDEA連接即可
在SpringBoot中,我們需要連接數據庫,只需要簡單的配置即可
在 application.yaml 中:
spring:
datasource:
username: root
password: root
# serverTimezone=UTC:MySQL 8+以上使用
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=UTC
# mysql 8+以上使用
driver-class-name: com.mysql.cj.jdbc.Driver
配置完這一些東西后,我們就可以直接去使用了,因為SpringBoot已經默認幫我們進行了自動配置;我們去測試類測試一下
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下默認的數據源
System.out.println(dataSource.getClass());
System.out.println("==========");
// 獲得連接
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 歸還連接
connection.close();
}
}
輸出結果:
class com.zaxxer.hikari.HikariDataSource
2020-02-24 16:46:56.963 INFO 19388 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-02-24 16:46:59.953 INFO 19388 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
HikariProxyConnection@1336418989 wrapping com.mysql.cj.jdbc.ConnectionImpl@597f0937
從輸出結果中可以看出,SpringBoot默認的數據源是:com.zaxxer.hikari.HikariDataSource
,我們並沒有手動配置。
Spring Boot 2.1.7 默認使用 com.zaxxer.hikari.HikariDataSource
數據源,而以前版本,如 Spring Boot 1.5 默認使用 org.apache.tomcat.jdbc.pool.DataSource
作為數據源;
HikariDataSource 號稱 Java WEB 當前速度最快的數據源,相比於傳統的 C3P0 、DBCP、Tomcat jdbc 等連接池更加優秀;
CRUD操作
- 有了數據源(
com.zaxxer.hikari.HikariDataSource
),就可以拿到數據庫連接(java.sql.Connection
),有了連接,就可以使用原生的 JDBC 語句來操作數據庫。 - 即使不使用第三方的數據庫操作框架,如MyBatis等,Spring 本身也對原生的 JDBC 做了輕量級的封裝,即:
org.springframework.jdbc.core.JdbcTemplate
- 數據庫操作的所有 CRUD 方法都在 JdbcTemplate 中。
- Spring Boot 不僅提供了默認的數據源,同時默認已經配置好了 JdbcTemplate 放在了容器中,程序員只需自己注入即可使用
- JdbcTemplate 的自動配置原理是依賴
org.springframework.boot.autoconfigure.jdbc
包下的org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
類
JdbcTemplate主要提供以下幾類方法:
- execute方法:可以用於執行任何SQL語句,一般用於執行DDL語句;
- update方法及batchUpdate方法:update方法用於執行新增、修改、刪除等語句;batchUpdate方法用於執行批處理相關語句;
- query方法及queryForXXX方法:用於執行查詢相關語句;
- call方法:用於執行存儲過程、函數相關語句。
測試:
@RestController
public class JdbcController {
// JdbcTemplate 是 core 包的核心類,用於簡化 JDBC操作,還能避免一些常見的錯誤,如忘記關閉數據庫連接
// Spring Boot 默認提供了數據源,默認提供了 org.springframework.jdbc.core.JdbcTemplate
// JdbcTemplate 中會自己注入數據源,使用起來也不用再自己來關閉數據庫連接
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/userList")
public List<Map<String, Object>> userList() {
String sql = "select id, name, pwd from mybatis.user";
List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql);
return list_maps;
}
@GetMapping("/updUser/{id}")
public String updateUser(@PathVariable("id") Integer id) {
String sql = "update mybatis.user set name = ?, pwd = ? where id = " + id;
// 數據
Object[] objects = new Object[2];
objects[0] = "王五";
objects[1] = "helloworld";
jdbcTemplate.update(sql, objects);
return "update-ok";
}
@GetMapping("/delUser/{id}")
public String deleteUser(@PathVariable("id") Integer id) {
String sql = "delete from mybatis.user where id = ?";
jdbcTemplate.update(sql, id);
return "delete-ok";
}
@GetMapping("/addUser")
public String addUser() {
String sql = "insert into mybatis.user (name, pwd) values (?, ?)";
// 數據
Object[] objects = new Object[2];
objects[0] = "李華";
objects[1] = "123klajfld";
jdbcTemplate.update(sql, objects);
return "insert-ok";
}
}
原理探究:
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
數據源配置類作用 :根據邏輯判斷之后,添加數據源;
DataSourceConfiguration
是 DataSourceAutoConfiguration
實際導入的數據源配置類。
tomcat的jdbc連接池:
Hikari:
dbcp2:
自定義數據源:
因為SpringBoot 2.x 默認數據源是:com.zaxxer.hikari.HikariDataSource
,而其他我們沒有導入相應的類,所有會爆紅!
SpringBoot默認支持以下數據源:
-
com.zaxxer.hikari.HikariDataSource (Spring Boot 2.0 以上,默認使用此數據源)
-
org.apache.tomcat.jdbc.pool.DataSource
-
org.apache.commons.dbcp2.BasicDataSource
可以使用 spring.datasource.type 指定自定義的數據源類型,值為 要使用的連接池實現的完全限定名。默認情況下,它是從類路徑自動檢測的。
/**
* 通用數據源配置
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
自定義數據源 DruidDataSource
Druid 簡介
Druid 是阿里巴巴開源平台上一個數據庫連接池實現,結合了 C3P0、DBCP、PROXOOL 等 DB 池的優點,同時加入了日志監控。
Druid 可以很好的監控 DB 池連接和 SQL 的執行情況,天生就是針對監控而生的 DB 連接池。
Spring Boot 2.0 以上默認使用 Hikari 數據源,可以說 Hikari 與 Driud 都是當前 Java Web 上最優秀的數據源,我們來重點介紹 Spring Boot 如何集成 Druid 數據源,如何實現數據庫監控。
配置數據源
Druid依賴:
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
現在我們去切換數據源;
之前已經說過 SpringBoot 2.0以上默認使用 com.zaxxer.hikari.HikariDataSource
數據源,但可以通過 spring.datasource.type
指定數據源
數據源切換之后,同理可以注入 DataSource,測試類不用變,一測便知!
Druid數據庫連接池可以配置一些參數:
spring:
datasource:
username: root
password: root
# serverTimezone=UTC:MySQL 8+以上使用
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=UTC
# mysql 8+以上使用
driver-class-name: com.mysql.cj.jdbc.Driver
# 切換數據源
type: org.springframework.jdbc.datasource.DriverManagerDataSource
#Spring Boot 默認是不注入這些屬性值的,需要自己綁定
#druid 數據源專有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置監控統計攔截的filters,stat:監控統計、log4j:日志記錄、wall:防御sql注入
#如果允許時報錯 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#則導入 log4j 依賴即可,Maven 地址: https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
log4j日志依賴
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
現在需要程序員自己為 com.alibaba.druid.pool.DruidDataSource
綁定到全局配置文件中的參數,再添加到容器中,而不再使用 Spring Boot 的自動生成了;
我們需要 自己添加 DruidDataSource
組件到容器中,並綁定屬性;
package com.rainszj.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DruidConfig {
/*
將自定義的 Druid數據源添加到容器中,不再讓 Spring Boot 自動創建
綁定全局配置文件中的 druid 數據源屬性到 com.alibaba.druid.pool.DruidDataSource從而讓它們生效
@ConfigurationProperties(prefix = "spring.datasource"):作用就是將 全局配置文件中
前綴為 spring.datasource的屬性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名參數中
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
去測試類中測試一下;看是否成功!
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下驅動類
System.out.println(dataSource.getClass());
System.out.println("==========");
Connection connection = dataSource.getConnection();
System.out.println(connection);
DruidDataSource druidDataSource = (DruidDataSource) this.dataSource;
System.out.println("最大連接數為:" + druidDataSource.getMaxActive());
System.out.println("數據源初始化連接數:" + druidDataSource.getInitialSize());
// 歸還連接
connection.close();
}
}
打印結果:
配置 Druid 數據源監控
Druid 數據源具有監控的功能,並提供了一個 web 界面方便用戶查看,類似安裝 路由器 時,人家也提供了一個默認的 web 頁面。
所以第一步需要設置 Druid 的后台管理頁面,比如 登錄賬號、密碼 等;配置后台管理
需要在 DruidConfig
中配置如下內容:
// 配置 Druid 監控管理后台的Servlet;
// 內置 Servler 容器時沒有web.xml文件,所以使用 SpringBoot的注冊 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet() {
// 創建Servlet注冊實體
// /druid/*:后台訪問的路徑
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); //后台管理界面的登錄賬號
initParams.put("loginPassword", "123456"); //后台管理界面的登錄密碼
//后台允許誰可以訪問
//initParams.put("allow", ""127.0.0.1""):表示只有本機可以訪問
//initParams.put("allow", ""):為空或者為null時,表示允許所有訪問
initParams.put("allow", "");
//deny:Druid 后台拒絕誰訪問
//initParams.put("deny", "192.168.1.20");表示禁止此ip訪問
//設置初始化參數
bean.setInitParameters(initParams);
return bean;
}
這些參數可以在 com.alibaba.druid.support.http.StatViewServlet
的父類 com.alibaba.druid.support.http.ResourceServlet
中找到
配置完畢后,我們可以選擇訪問 : http://localhost:8080/druid/login.html
登錄界面:
首頁:
配置 Druid web 監控 filter
這個過濾器的作用就是統計 web 應用請求中所有的數據庫信息,比如 發出的 sql 語句,sql 執行的時間、請求次數、請求的 url 地址、以及seesion 監控、數據庫表的訪問次數 等等
需要在 DruidConfig
中配置如下內容:
//配置 Druid 監控 之 web 監控的 filter
//WebStatFilter:用於配置Web和Druid數據源之間的管理關聯監控統計
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean bean = new FilterRegistrationBean(new WebStatFilter());
// 設置過濾器的過濾路徑
bean.addUrlPatterns("/*");
// bean.setUrlPatterns(Arrays.asList("/*"));
Map<String, String> initParameters = new HashMap<String, String>();
// 過濾這些東西不進行統計
initParameters.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
這些參數在 WebStatFilter
類中
整合MyBatis
版本要求:
第一步:導入Spring Boot 整合 MyBatis的 依賴:
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
第二步:配置數據庫連接信息
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
加載類
com.mysql.jdbc.Driver
。不推薦使用。新的驅動程序類為com.mysql.cj.jdbc.Driver
。通過SPI自動注冊驅動程序,通常不需要手動加載驅動程序類。
這里使用默認的數據源,測試數據庫連接是否成功:
@SpringBootTest
class Springboot03DataApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看一下驅動類
System.out.println(dataSource.getClass());
System.out.println("==========");
Connection connection = dataSource.getConnection();
System.out.println(connection);
System.out.println(connection.getMetaData().getURL());
// 歸還連接
connection.close();
}
}
連接成功,OK!
第三步:創建實體類:
package com.rainszj.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
第四步:配置Mapper接口,也就是dao層:
package com.rainszj.mapper;
import com.rainszj.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Mapper:標記這個類是MyBatis的Mapper,等價於Spring整合MyBatis時的Mapper接口
*/
@Mapper
/**
* @Rpository:將這個接口添加進Spring容器中,和 @Component等價
*/
@Repository
public interface UserMapper {
// 查詢所有用戶
List<User> selectAllUsers();
// 根據id查找用戶
User findUserById(@Param("uid")Integer id);
// 修改用戶
int updateUser(User user);
// 添加用戶
int addUser(User user);
// 刪除用戶
int deleteUser(Integer id);
}
第五步:在application.yaml中添加MyBatis的配置
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
# 整合MyBatis
mybatis:
# 配置別名
type-aliases-package: com.rainszj.pojo
# 綁定Mapper配置文件
# 注意:這里的 classpath代指:resources目錄
mapper-locations: classpath:mybatis/mapper/*.xml
spring boot 官方並沒有提供 myBaits 的啟動器,是 myBatis 官方提供的開發包來適配的 spring boot,從 pom.xml 文件中的依賴包名也能看出來,並非是以 spring-boot 開頭的;
同理上面全局配置文件中的這兩行配置也是以 mybatis 開頭 而非 spring 開頭也充分說明這些都是 myBatis 官方提供的
可以從 org.mybatis.spring.boot.autoconfigure.MybatisProperties
中查看所有配置項
/**
* Configuration properties for MyBatis.
*
* @author Eddú Meléndez
* @author Kazuki Shimizu
*/
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
/**
* Location of MyBatis xml config file.
*/
private String configLocation;
/**
* Locations of MyBatis mapper files.
*/
private String[] mapperLocations;
/**
* Packages to search type aliases. (Package delimiters are ",; \t\n")
*/
private String typeAliasesPackage;
/**
* The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that
* searched from typeAliasesPackage.
*/
private Class<?> typeAliasesSuperType;
/**
* Packages to search for type handlers. (Package delimiters are ",; \t\n")
*/
private String typeHandlersPackage;
/**
* Indicates whether perform presence check of the MyBatis xml config file.
*/
private boolean checkConfigLocation = false;
/**
* Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
*/
private ExecutorType executorType;
/**
* The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
*/
private Class<? extends LanguageDriver> defaultScriptingLanguageDriver;
/**
* Externalized properties for MyBatis configuration.
*/
private Properties configurationProperties;
/**
* A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is
* not used.
*/
@NestedConfigurationProperty
private Configuration configuration;
}
第六步:編寫對應的Mapper.xml
配置文件
<?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.rainszj.mapper.UserMapper">
<select id="selectAllUsers" resultType="user">
select * from mybatis.user
</select>
<select id="findUserById" resultType="user">
select * from mybatis.user where id = #{uid}
</select>
<update id="updateUser" parameterType="user">
update mybatis.user
set name = #{name},
pwd = #{pwd}
where id = #{id}
</update>
<update id="addUser" parameterType="user">
insert into mybatis.user values (#{id},#{name},#{pwd})
</update>
<delete id="deleteUser">
delete from mybatis.user where id = #{id}
</delete>
</mapper>
第七步:service層:
package com.rainszj.service;
import com.rainszj.mapper.UserMapper;
import com.rainszj.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> selectAllUsers() {
return userMapper.selectAllUsers();
}
// 根據id查找用戶
public User findUserById(Integer id) {
return userMapper.findUserById(id);
}
// 修改用戶
public int updateUser(User user) {
return userMapper.updateUser(user);
}
// 添加用戶
public int addUser(User user) {
return userMapper.addUser(user);
}
// 刪除用戶
public int deleteUser(Integer id) {
return userMapper.deleteUser(id);
}
}
第八步:controller層:
package com.rainszj.controller;
import com.rainszj.pojo.User;
import com.rainszj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/selectAllUsers")
public List<User> selectAllUsers() {
return userService.selectAllUsers();
}
@GetMapping("/findUserById/{id}")
public String findUserById(@PathVariable Integer id) {
User user = userService.findUserById(id);
System.out.println(user);
return "ok";
}
@GetMapping("/addUser")
public String addUser() {
userService.addUser(new User(4, "張三", "123"));
return "ok";
}
@GetMapping("/updateUser")
public String updateUser() {
userService.updateUser(new User(4, "李四", "123"));
return "ok";
}
@GetMapping("/deleteUser")
public String deleteUser(@RequestParam("uid") Integer id) {
userService.deleteUser(id);
return "ok";
}
}
測試一下就搞定 OK!