SpringBoot用Druid整合MyBatis
阿新 • • 發佈:2018-11-25
Druid介紹
Druid是Java語言中最好的資料庫連線池。Druid能夠提供強大的監控和擴充套件功能。
Druid是阿里巴巴開源平臺上的一個專案,整個專案由資料庫連線池、外掛框架和SQL解析器組成。該專案主要是為了擴充套件JDBC的一些限制,可以讓程式設計師實現一些特殊的需求,比如向金鑰服務請求憑證、統計SQL資訊、SQL效能收集、SQL注入檢查、SQL翻譯等,程式設計師可以通過定製來實現自己需要的功能。
Druid首先是一個數據庫連線池,但它不僅僅是一個數據庫連線池,它還包含一個ProxyDriver,一系列內建的JDBC元件庫,一個SQL Parser。 Druid支援所有JDBC相容的資料庫,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等。Druid針對Oracle和MySql做了特別優化,比如Oracle的PS Cache記憶體佔用優化,MySql的ping檢測優化
- 監控SQL的執行時間、ResultSet持有時間、返回行數、更新行數、錯誤次數、錯誤堆疊資訊。
- SQL執行日誌,Druid提供了不同的LogFilter,能夠支援Common-Logging、Log4j和JdkLog,你可以按需要選擇相應的LogFilter,監控你應用的資料庫訪問情況。
- SQL執行的耗時區間分佈。什麼是耗時區間分佈呢?比如說,某個SQL執行了1000次,其中0~1毫秒區間50次,1~10毫秒800次,10~100毫秒100次,100~1000毫秒30次,1~10秒15次,10秒以上5次。通過耗時區間分佈,能夠非常清楚知道SQL的執行耗時情況。
- 監控連線池的物理連線建立和銷燬次數、邏輯連線的申請和關閉次數、非空等待次數、PSCache命中率等。
其次,方便擴充套件。Druid提供了Filter-Chain模式的擴充套件API,可以自己編寫Filter攔截JDBC中的任何方法,可以在上面做任何事情,比如說效能監控、SQL審計、使用者名稱密碼加密、日誌等等。
Druid內建提供了用於監控的StatFilter、日誌輸出的Log系列Filter、防禦SQL注入攻擊的WallFilter。
阿里巴巴內部實現了用於資料庫密碼加密的CirceFilter,以及和Web、Spring關聯監控的DragoonStatFilter。
第三,Druid集合了開源和商業資料庫連線池的優秀特性,並結合阿里巴巴大規模苛刻生產環境的使用經驗進行優化。
- ExceptionSorter。當一個連線產生不可恢復的異常時,例如Oracle error_code_28 session has been killed,必須立刻從連線池中逐出,否則會產生大量錯誤。目前只有Druid和JBoss DataSource實現了ExceptionSorter。
- PSCache記憶體佔用優化對於支援遊標的資料庫(Oracle、SQL Server、DB2等,不包括MySql),PSCache可以大幅度提升SQL執行效能。一個PreparedStatement對應伺服器一個遊標,如果PreparedStatement被快取起來重複執行,PreparedStatement沒有被關閉,伺服器端的遊標就不會被關閉,效能提高非常顯著。在類似“SELECT * FROM T WHERE ID = ?”這樣的場景,效能可能是一個數量級的提升。但在Oracle JDBC Driver中,其他的資料庫連線池(DBCP、JBossDataSource)會佔用記憶體過多,極端情況可能大於1G。Druid呼叫OracleDriver提供管理PSCache內部API。
- LRU是一個性能關鍵指標,特別Oracle,每個Connection對應資料庫端的一個程序,如果資料庫連線池遵從LRU,有助於資料庫伺服器優化,這是重要的指標。Druid、DBCP、Proxool、JBoss是遵守LRU的。BoneCP、C3P0則不是。BoneCP在mock環境下效能可能還好,但在真實環境中則就不好了。
簡單SQL語句用時10微秒以內,複雜SQL用時30微秒。
通過Druid提供的SQL Parser可以在JDBC層攔截SQL做相應處理,比如說分庫分表、審計等。Druid防禦SQL注入攻擊的WallFilter就是通過Druid的SQL Parser分析語義實現的。 Druid的優勢是在JDBC最低層進行攔截做判斷,不會遺漏。
專案下載(專案程式碼+資料庫)
執行結果展示
http://localhost:8080/selectUser.action?id=1
http://localhost:8080/druid/index.html 賬號admin,密碼admin,在程式碼中有體現的
專案構建
專案結構如圖所示
新增依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBoot_MyBatis_Diruid</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.7.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--熱部署.jar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<!-- thmleaf模板依賴. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
修改配置檔案properties
######################################
###spring datasource
######################################
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
###############################下面Spring的配置檔案基本就不用修改了
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
######################################
###spring thymeleaf
######################################
spring.thymeleaf.cache=false
######################################
###MyBatis
######################################
mybatis.mapper-locations=classpath:mapper/*Mapper.xml
DruidConfigBean(重點)(重點)(重點)
druid有一個Servlet和Filter,這裡採用程式設計注入的方式,
package com.example.demo.configBean;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Configuration
public class DruidConfiguration {
private static final Logger log = LoggerFactory.getLogger(DruidConfiguration.class);
@Bean
public ServletRegistrationBean druidServlet() {
log.info("init Druid Servlet Configuration ");
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("loginUsername", "admin");// 使用者名稱
initParameters.put("loginPassword", "admin");// 密碼
initParameters.put("resetEnable", "false");// 禁用HTML頁面上的“Reset All”功能
initParameters.put("allow", ""); // IP白名單 (沒有配置或者為空,則允許所有訪問)
// initParameters.put("deny", "192.168.20.38");// IP黑名單
// (存在共同時,deny優先於allow)
servletRegistrationBean.setInitParameters(initParameters);
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
// 新增過濾規則
filterRegistrationBean.addUrlPatterns("/*");
// 新增不需要忽略的格式資訊.
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
如果沒有配置下面的Bean,它是不會監控到SQL語句的,就是不會有下面箭頭指的地方
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
dao
package com.example.demo.dao;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.entity.User;
@Mapper
public interface UserMapper {
public User selectByPrimaryKey(Integer id);
}
entity
package com.example.demo.entity;
public class User {
private Integer id;
private String name;
private String username;
private String password;
private Integer sign;
public User() {
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", username=" + username + ", password=" + password + ", sign="
+ sign + "]";
}
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 == null ? null : name.trim();
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username == null ? null : username.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public Integer getSign() {
return sign;
}
public void setSign(Integer sign) {
this.sign = sign;
}
}
controller
package com.example.demo.handler;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.dao.UserMapper;
import com.example.demo.entity.User;
@Controller
public class HelloHandler {
@Autowired
private UserMapper userMapper;
@RequestMapping("/selectUser.action")
public String selectUser(int id,Map<String,Object> map){
User user=userMapper.selectByPrimaryKey(id);
map.put("user", user);
return "/success";
}
}
MyBatis中與dao對應的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.example.demo.dao.UserMapper" >
<resultMap id="BaseResultMap" type="com.example.demo.entity.User" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="username" property="username" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
<result column="sign" property="sign" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
id, name, username, password, sign
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from user
where id = #{id,jdbcType=INTEGER}
</select>
</mapper>
啟動類
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootMyBatisDruidApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootMyBatisDruidApplication.class, args);
}
}