JDBC与Druid简单介绍及Druid与MyBatis连接数据库
序言
java 程序与数据建立连接,首先要从 jdbc 说起,然后直接上阿里认为宇宙最好的数据库连接池 druid,然后再说上层程序对象与数据源映射关联关系的 orm-mybatis。
JDBC 介绍
JDBC(Java DataBase Connectivity)是 Java 和数据库(关系型数据库)之间的一个桥梁。
- 是一个规范而不是一个实现,能够执行 SQL 语句。
- 它由一组用 Java 语言编写的类和接口组成,各种不同类型的数据库都有相应的实现。
- 它不属于某一个数据库的接口,而是可以用于定义程序与数据库连接规范,通过一整套接口,由各个不同的数据库厂商去完成所对应的实现类,由 sun 公司提出!
执行 sql 过程为:类加载 --> 获取连接 --> 书写 SQL--> 执行语句 ---> 处理结果集。
为什么会有连接池的存在?
因为建立数据库连接是一个非常耗时、耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去,极大的提高了数据库连接的性能问题,节省了资源和时间。
什么是数据源
JDBC2.0 提供了 javax.sql.DataSource 接口,它负责建立与数据库的连接,当在应用程序中访问数据库时 不必编写连接数据库的代码,直接引用 DataSource 获取数据库的连接对象即可。用于获取操作数据 Connection 对象。
数据源与数据库连接池组件
数据源建立多个数据库连接,这些数据库连接会保存在数据库连接池中,当需要访问数据库时,只需要从数据库连接池中
获取空闲的数据库连接,当程序访问数据库结束时,数据库连接会放回数据库连接池中。
常用的数据库连接池技术:
这些连接技术都是在 jdbc 的规范之上建立完成的。有如下:
C3P0、DBCP、Proxool 和 DruidX
Druid 简介及简单使用实例
官方网站文档:https://github.com/alibaba/druid/wiki/Druid%E8%BF%9E%E6%8E%A5%E6%B1%A0%E4%BB%8B%E7%BB%8D
Druid 连接池是阿里巴巴开源的数据库连接池项目。Druid 连接池为监控而生,内置强大的监控功能,监控特性不影响性能。功能强大,能防 SQL 注入,内置 Loging 能诊断 Hack 应用行为。
Druid 不仅仅是一个数据库连接池,它还包含一个 ProxyDriver,一系列内置的 JDBC 组件库,一个 SQL Parser。 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、Derby、Postgresql、SQL Server、H2 等等。
Druid 针对 oracle 和 mysql 做了特别优化,比如 Oracle 的 PS Cache 内存占用优化,MySql 的 ping 检测优化。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 实现对 MSSQL 数据库进行增删查
步骤(由上而下):
- 引入 druid 依赖
- 引入 com.microsoft.sqlserver.sqldjbc4 依赖(由此依赖可以看出 JDBC 与 druid 的关系,druid 是基于 jdbc 规范建立的上层应用)
- 写代码
- 配置 druid 的 datasource
- 建立 Connection
- 创建 Statement 或者 PreparedStatement 接口执行 SQL
- 处理结果
- 释放资源
下面按照步骤上代码。
1-2 步,引入必须依赖。
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.5</version> </dependency> <dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>sqljdbc4</artifactId> <version>4.0</version> </dependency>
3. 配置资源配置文件,建立 druiddatasource
## druid
druid.datasource.enable-monitor=true
yw.order.druid.datasource.url=jdbc:sqlserver://172.16.20.1;DatabaseName=order
yw.order.druid.datasource.username=sa
yw.order.druid.datasource.password=WE+NBOPp+T9peFYfySpsw74OOvAwc095/4v51MUbF35cmECkaZMq7+
yw.order.druid.datasource.pwd-public-key=wSAJBALRv3R64ORcPJAik5KYZz+hxQAZJeSe9Pn8vJIOh8K01tHNk++zQBRQIVl7v+APbsWmPwAxvQ+OApl
yw.order.druid.datasource.initial-size=5
yw.order.druid.datasource.max-active=100
yw.order.druid.datasource.min-idle=5
yw.order.druid.datasource.slowSqlMillis=1000
package trade.user.api.config;import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@author zhanglonghao
@date 2019/7/17 11:13
*/
@ConfigurationProperties(prefix = "yw.order.druid.datasource")
public class MssqDataSourceProperties {
/**
- 数据源名称
*/
private String name;/**
- 数据库连接 url
*/
private String url;/**
- 数据库用户名
*/
private String username;/**
- 数据库密码
*/
private String password;/**
- 用来解密的密码公钥
*/
private String pwdPublicKey;/**
- 连接池初始连接数
*/
private int initialSize = 5;/**
- 连接池最大连接数
*/
private int maxActive = 50;/**
- 空闲的最小连接数量, 相当于线程池的最小连接数
*/
private int minIdle = 5;/**
- 获取连接时最大等待时间, 毫秒
*/
private int maxWait = 60000;/**
- 配置间隔多久才进行一次检测需要关闭的空闲连接,单位是毫秒 , 默认 1 分钟
*/
private int timeBetweenEvictionRunsMillis = 60000;/**
- 配置一个连接在池中最小生存的时间,超过该时间的空闲链接将被关闭, 默认 5 分钟
*/
private int minEvictableIdleTimeMillis = 300000;/**
- 验证链接是否有效的 sql
*/
private String validationQuery = "SELECT'x'";/**
- 空闲时检测链接是否有效
*/
private boolean testWhileIdle = true;/**
- 链接被借出时检查是否有效, 影响性能, 默认关闭
*/
private boolean testOnBorrow = false;/**
- 当链接返还时检查连接是否有效, 影响性能, 默认关闭
*/
private boolean testOnReturn = false;/**
- 是否缓存 preparedStatement,也就是 PSCache。PSCache 对支持游标的数据库性能提升巨大,比如说 oracle,
- 在 mysql 下建议关闭。
*/
private boolean poolPreparedStatements = false;/**
- poolPreparedStatements 为 false 的情况, 该值不起作用
*/
private int maxOpenPreparedStatements = 20;
/**- 是否启用数据源的监控,spring-web 应用建议打开
*/
private boolean enableMonitor = true;/**
- 当启用监控后, 是否打印慢 sql
*/
private boolean logSlowSql = true;
/**- 多少毫秒的 sql 认为是慢 sql, 默认 1 秒
*/
private int slowSqlMillis = 1000;/**
- 是否合并 sql, 同一个 PreparedStatements 但 where 条件不同会被认为是一个 sql
*/
private boolean mergeSql = true;public String getName() {
return name;
}public void setName(String name) {
this.name = name;
}public String getUrl() {
return url;
}public void setUrl(String url) {
this.url = url;
}public String getUsername() {
return username;
}public void setUsername(String username) {
this.username = username;
}public String getPassword() {
return password;
}public void setPassword(String password) {
this.password = password;
}public String getPwdPublicKey() {
return pwdPublicKey;
}public void setPwdPublicKey(String pwdPublicKey) {
this.pwdPublicKey = pwdPublicKey;
}public int getInitialSize() {
return initialSize;
}public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}public int getMaxActive() {
return maxActive;
}public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}public int getMinIdle() {
return minIdle;
}public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}public int getMaxWait() {
return maxWait;
}public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}public int getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}public int getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}public String getValidationQuery() {
return validationQuery;
}public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}public boolean isTestWhileIdle() {
return testWhileIdle;
}public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}public boolean isTestOnBorrow() {
return testOnBorrow;
}public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}public boolean isTestOnReturn() {
return testOnReturn;
}public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}public int getMaxOpenPreparedStatements() {
return maxOpenPreparedStatements;
}public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}public boolean isEnableMonitor() {
return enableMonitor;
}public void setEnableMonitor(boolean enableMonitor) {
this.enableMonitor = enableMonitor;
}public boolean isLogSlowSql() {
return logSlowSql;
}public void setLogSlowSql(boolean logSlowSql) {
this.logSlowSql = logSlowSql;
}public int getSlowSqlMillis() {
return slowSqlMillis;
}public void setSlowSqlMillis(int slowSqlMillis) {
this.slowSqlMillis = slowSqlMillis;
}public boolean isMergeSql() {
return mergeSql;
}public void setMergeSql(boolean mergeSql) {
this.mergeSql = mergeSql;
}
}
package trade.user.api.config;import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
- @author zhanglonghao
- @date 2019/7/17 15:44
*/
@Configuration
@EnableConfigurationProperties({ MssqDataSourceProperties.class })
public class MssqlDataSource {
@Autowired
private MssqDataSourceProperties druidDataSourceProperties;
@Bean(name = "OrderDruidDataSource", initMethod = "init", destroyMethod = "close")
@ConditionalOnMissingBean(name = "OrderDruidDataSource")
public DruidDataSource dashboardDruidDataSource() throws Exception {
DruidDataSource result = new DruidDataSource();
result.setName(druidDataSourceProperties.getName());
result.setUrl(druidDataSourceProperties.getUrl());
result.setUsername(druidDataSourceProperties.getUsername());
result.setPassword(druidDataSourceProperties.getPassword());
result.setConnectionProperties(
"config.decrypt=true;config.decrypt.key=" + druidDataSourceProperties.getPwdPublicKey());
result.setFilters("config");
result.setMaxActive(druidDataSourceProperties.getMaxActive());
result.setInitialSize(druidDataSourceProperties.getInitialSize());
result.setMaxWait(druidDataSourceProperties.getMaxWait());
result.setMinIdle(druidDataSourceProperties.getMinIdle());
result.setTimeBetweenEvictionRunsMillis(druidDataSourceProperties.getTimeBetweenEvictionRunsMillis());
result.setMinEvictableIdleTimeMillis(druidDataSourceProperties.getMinEvictableIdleTimeMillis());
result.setValidationQuery(druidDataSourceProperties.getValidationQuery());
result.setTestWhileIdle(druidDataSourceProperties.isTestWhileIdle());
result.setTestOnBorrow(druidDataSourceProperties.isTestOnBorrow());
result.setTestOnReturn(druidDataSourceProperties.isTestOnReturn());
result.setPoolPreparedStatements(druidDataSourceProperties.isPoolPreparedStatements());
result.setMaxOpenPreparedStatements(druidDataSourceProperties.getMaxOpenPreparedStatements());
if (druidDataSourceProperties.isEnableMonitor()) {
StatFilter filter = new StatFilter();
filter.setLogSlowSql(druidDataSourceProperties.isLogSlowSql());
filter.setMergeSql(druidDataSourceProperties.isMergeSql());
filter.setSlowSqlMillis(druidDataSourceProperties.getSlowSqlMillis());
List<Filter> list = new ArrayList<Filter>();
list.add(filter);
result.setProxyFilters(list);
}
return result;
}
}
note:上面有个小插曲就是根据 druid 生成密码,命令:D:\Maven\repository\com\alibaba\druid\1.1.5> java -cp .\druid-1.1.5.jar com.alibaba.druid.filter.config.ConfigTools 密码
之后版本
public static void main(String[] args) throws Exception {String[] strs = new String[]{"Tuhumima123%]"}; com.alibaba.druid.filter.config.ConfigTools.main(strs); }
5. 余下流程
1. 使用PreparedStatement
@Autowired DruidDataSource dataSource; @RequestMapping(value = "/GetUserDetails") public String GetUserDetails(int userid) { try { // 获得连接: DruidPooledConnection conn = dataSource.getConnection(); // 编写 SQL: String sql = "select * from orderdiscount where pkid=? and orderid=?"; PreparedStatement pstmt = conn.prepareStatement(sql); //索引从 1 开始 pstmt.setLong(1,1L); pstmt.setInt(1,66666666); // 执行 sql: ResultSet rs = pstmt.executeQuery(); while (rs.next()) { System.out.println(rs.getInt("PKID") + "" + rs.getString("OrderID"));} pstmt.close(); conn.close();} catch (SQLException e) {e.printStackTrace(); } return ""; }
2. 使用 Statement
@Autowired DruidDataSource dataSource; @RequestMapping(value = "/GetUserDetails") public String GetUserDetails(int userid) { try { // 获得连接: DruidPooledConnection conn = dataSource.getConnection(); // 编写 SQL: String sql = "select * from orderdiscount where pkid=1"; Statement pstmt = conn.createStatement(); // 执行 sql: ResultSet rs = pstmt.executeQuery(sql); while (rs.next()) { System.out.println(rs.getInt("PKID") + "" + rs.getString("OrderID"));} pstmt.close(); conn.close();} catch (SQLException e) {e.printStackTrace(); } return ""; }
Statement 和 PreparedStatement 的异同及优缺点
同:两者都是用来执 SQL 语句的
异:PreparedStatement 需要根据 SQL 语句来创建,它能够通过设置参数,指定相应的值,不是像 Statement 那样使用字符串拼接的方式。
PreparedStatement 的优点:
1、其使用参数设置,可读性好,不易记错。在 statement 中使用字符串拼接,可读性和维护性比较差。
2、其具有预编译机制,性能比 statement 更快。
3、其能够有效防止 SQL 注入攻击。
execute 和 executeUpdate 的区别
相同点:二者都能够执行增加、删除、修改等操作。
不同点:
1、execute 可以执行查询语句,然后通过 getResult 把结果取出来。executeUpdate 不能执行查询语句。
2、execute 返回 Boolean 类型,true 表示执行的是查询语句,false 表示执行的 insert、delete、update 等。executeUpdate 的返回值是 int,表示有多少条数据受到了影响。
使用 Druid 与 MyBatis 构建程序与数据库关联关系及数据与程序实体映射
mybatis 网上教程很对,这里复制一段直接上代码啦。
MyBatis 最强大的特性之一就是它的动态语句功能。如果您以前有使用 JDBC 或者类似框架的经历,您就会明白把 SQL 语句条件连接在一起是多么的痛苦,要确保不能忘记空格或者不要在 columns 列后面省略一个逗号等。动态语句能够完全解决掉这些痛苦。尽管与动态 SQL 一起工作不是在开一个 party,但是 MyBatis 确实能通过在任何映射 SQL 语句中
@MapperScan(value = { "trade.user.dal.dataobject", "trade.user.dal.mapper" }, sqlSessionFactoryRef = "OrderSqlSessionFactory") @ConditionalOnProperty(name = "yw.order.druid.datasource.url", matchIfMissing = false) public class MssqlDataSource { static final String MAPPER_LOCATION = "classpath*:sqlconfig/*Mapper.xml"; @Bean(name = "OrderSqlSessionFactory") @ConditionalOnMissingBean(name = "OrderSqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("OrderDruidDataSource")DruidDataSource druidDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(druidDataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION)); SqlSessionFactory sqlSessionFactory = sessionFactory.getObject(); sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true); return sqlSessionFactory; } }
@MapperScan("trade.user.**") public class StartMain { public static void main(String[] args) { SpringApplication.run(StartMain.class, args);} }
@Resource OrderDiscountDOMapper orderDiscountDOMapper; @RequestMapping(value = "/getInfo") public String getInfo(int id) { OrderDiscountDO rt=orderDiscountDOMapper.selectByPrimaryKey(1L); return id+"----"+ JSON.toJSONString(rt); }
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.1.6.RELEASE</version> </dependency>
总结
88