1. 程式人生 > >SpringBoot用Druid整合MyBatis

SpringBoot用Druid整合MyBatis

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檢測優化
首先,強大的監控特性,通過Druid提供的監控功能,可以清楚知道連線池和SQL的工作情況。
  • 監控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環境下效能可能還好,但在真實環境中則就不好了。
Druid提供了MySql、Oracle、Postgresql、SQL-92的SQL的完整支援,這是一個手寫的高效能SQL Parser,支援Visitor模式,使得分析SQL的抽象語法樹很方便。

簡單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);
	}
}