序言
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 epositorycomalibabadruid1.1.5> java -cp .druid-1.1.5.jar com.alibaba.druid.filter.config.ConfigTools 密码
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网上教程很对,这里复制一段直接上代码啦。
@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