通過引入主從數據庫同步系統,可以顯著提升平臺的性能和穩定性,同時保證數據的一致性和安全性。Druid連接池也提供了強大的監控和安全防護功能,使得整個系統更加健壯和可靠。
我們為什么選擇Druid?
-
高效的連接管理:Druid 提供了高效的物理連接復用機制,能夠快速獲取和釋放數據庫連接,減少連接建立和關閉的開銷。
-
異步初始化:支持異步初始化連接池,提高應用啟動速度。
-
詳細的統計信息:Druid 內置了豐富的統計功能,可以收集 SQL 執行情況、慢查詢記錄等詳細數據,幫助我們更好地進行性能調優。
-
防火墻規則:支持配置防火墻規則,限制哪些 IP 地址可以訪問數據庫,增強安全性。
-
SQL 防注入:提供 WallFilter 插件,防止 SQL 注入攻擊,保護數據庫不受惡意 SQL 的影響。
-
多數據源支持:Druid 支持配置多個數據源,非常適合實現讀寫分離等高級場景。在我們的項目中,主從數據庫的配置正是利用了這一特性。
代碼實操
<dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- MyBatis Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><!-- Druid --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><!-- MySQL Connector --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
</dependencies>
application.yml
server:port:8080spring:
datasource:type:com.alibaba.druid.pool.DruidDataSourcedruid:master:url:jdbc:mysql://localhost:3306/master_db?useSSL=false&serverTimezone=UTCusername:rootpassword:rootslave:url:jdbc:mysql://localhost:3307/slave_db?useSSL=false&serverTimezone=UTCusername:rootpassword:rootmybatis-plus:
configuration:log-impl:org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations:classpath*:mybatis-mapper.xml
動態數據源上下文持有者
package?com.example.demo.config;publicclass?DynamicDataSourceContextHolder?{privatestaticfinal?ThreadLocal<String> contextHolder =?new?ThreadLocal<>();public?static?void?setDataSourceType(String dataSourceType)?{contextHolder.set(dataSourceType);}public?static?String?getDataSourceType()?{return?contextHolder.get();}public?static?void?clearDataSourceType()?{contextHolder.remove();}
}
數據源配置
package?com.example.demo.config;import?com.alibaba.druid.pool.DruidDataSource;
import?org.springframework.beans.factory.annotation.Value;
import?org.springframework.context.annotation.Bean;
import?org.springframework.context.annotation.Configuration;
import?org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import?javax.sql.DataSource;
import?java.util.HashMap;
import?java.util.Map;@Configuration
publicclass?DataSourceConfig?{@Value("${spring.datasource.druid.master.url}")private?String masterUrl;@Value("${spring.datasource.druid.master.username}")private?String masterUsername;@Value("${spring.datasource.druid.master.password}")private?String masterPassword;@Value("${spring.datasource.druid.slave.url}")private?String slaveUrl;@Value("${spring.datasource.druid.slave.username}")private?String slaveUsername;@Value("${spring.datasource.druid.slave.password}")private?String slavePassword;@Beanpublic?DataSource?dynamicDataSource()?{Map<Object, Object> targetDataSources =?new?HashMap<>();targetDataSources.put("master", masterDataSource());targetDataSources.put("slave", slaveDataSource());AbstractRoutingDataSource abstractRoutingDataSource =?new?AbstractRoutingDataSource() {@Overrideprotected?Object?determineCurrentLookupKey()?{return?DynamicDataSourceContextHolder.getDataSourceType();}};abstractRoutingDataSource.setDefaultTargetDataSource(masterDataSource());abstractRoutingDataSource.setTargetDataSources(targetDataSources);return?abstractRoutingDataSource;}@Beanpublic?DataSource?masterDataSource()?{DruidDataSource dataSource =?new?DruidDataSource();dataSource.setUrl(masterUrl);dataSource.setUsername(masterUsername);dataSource.setPassword(masterPassword);return?dataSource;}@Beanpublic?DataSource?slaveDataSource()?{DruidDataSource dataSource =?new?DruidDataSource();dataSource.setUrl(slaveUrl);dataSource.setUsername(slaveUsername);dataSource.setPassword(slavePassword);return?dataSource;}
}
實體類
package?com.example.demo.entity;import?com.baomidou.mybatisplus.annotation.IdType;
import?com.baomidou.mybatisplus.annotation.TableId;
import?com.baomidou.mybatisplus.annotation.TableName;@TableName("product")
publicclass?Product?{@TableId(type = IdType.AUTO)private?Long id;private?String name;private?Double price;// getters and setters
}
Mapper接口
package?com.example.demo.mapper;import?com.baomidou.mybatisplus.core.mapper.BaseMapper;
import?com.example.demo.entity.Product;public?interface?ProductMapper?extends?BaseMapper<Product>?{
}
mybatis-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.example.demo.mapper.ProductMapper"><select?id="selectById"?resultType="com.example.demo.entity.Product">SELECT * FROM product WHERE id = #{id}</select><insert?id="insert"?parameterType="com.example.demo.entity.Product">INSERT INTO product (name, price) VALUES (#{name}, #{price})</insert></mapper>
Service接口
package?com.example.demo.service;import?com.example.demo.entity.Product;public?interface?ProductService?{Product?getProductById(Long id);boolean?saveProduct(Product product);
}
Service
package?com.example.demo.service.impl;import?com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import?com.example.demo.config.DynamicDataSourceContextHolder;
import?com.example.demo.entity.Product;
import?com.example.demo.mapper.ProductMapper;
import?com.example.demo.service.ProductService;
import?org.springframework.stereotype.Service;@Service
publicclass?ProductServiceImpl?extends?ServiceImpl<ProductMapper,?Product>?implements?ProductService?{@Overridepublic?Product?getProductById(Long id)?{DynamicDataSourceContextHolder.setDataSourceType("slave");try?{return?getById(id);}?finally?{DynamicDataSourceContextHolder.clearDataSourceType();}}@Overridepublic?boolean?saveProduct(Product product)?{DynamicDataSourceContextHolder.setDataSourceType("master");try?{return?save(product);}?finally?{DynamicDataSourceContextHolder.clearDataSourceType();}}
}
Controller
package?com.example.demo.controller;import?com.example.demo.entity.Product;
import?com.example.demo.service.ProductService;
import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/products")
publicclass?ProductController?{@Autowiredprivate?ProductService productService;@GetMapping("/{id}")public?Product?getProduct(@PathVariable Long id)?{return?productService.getProductById(id);}@PostMapping("/")public?boolean?addProduct(@RequestBody Product product)?{return?productService.saveProduct(product);}
}
Application
package?com.example.demo;import?org.mybatis.spring.annotation.MapperScan;
import?org.springframework.boot.SpringApplication;
import?org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public?class?DemoApplication?{public?static?void?main(String[] args)?{SpringApplication.run(DemoApplication.class,?args);}
}
測試
添加產品
curl -X POST http://localhost:8080/products/ \-H?"Content-Type: application/json"?\-d?'{"name": "Sample Product", "price": 99.99}'
Respons
true
獲取產品
curl -X GET http://localhost:8080/products/1
Respons
{"id":1,"name":"Sample Product","price":99.99}