本文介绍使用JDBC的使用(MySQL数据库下)。
基本概念
JDBC(Java DataBase Connectivity)就是使用Java语言操作关系型数据库的一套API,通过JDBC可以操作所有类型的关系型数据库(Mysql、oracle、IBM等)。
JDBC本质是sun公司提供的操作关系型数据库的接口,各个数据库厂商实现这个接口,提供操作数据库的驱动(即jar包),从而实现同一套Java代码操作不同的数据库,如果数据库需要切换时不需要改java代码,只需要改驱动就行。
使用方式
- 下载mysql最新驱动包,或者用maven、gradle导入。
- 包导入工程里
Class.forName("com.mysql.jdbc.Driver")
- 撸代码(查询user数据库里所有信息)
//注册驱动,最新版本不用这一步了,SPI会自动注册驱动
Class.forName("com.mysql.jdbc.Driver")
//连接路径,默认语法
val url = "jdbc:mysql://localhost:3306/server_from_zero"
//mysql用户名
val user = "root"
//mysql用户密码
val pwd = "Root111230!"
//获取数据库连接
val conn = DriverManager.getConnection(url,user,pwd)
//定义sql语句
val sql = "select * from user"
//获取数据库操作对象
val stmt = conn.createStatement()
//执行数据库语句
val res = stmt.execute(sql)
println(res)//处理数据库操作结果
//释放资源
stmt.close()
conn.close()
ps:SPI自动加载的类位于jar包下meta-inf
目录里的services
中,靠的是类加载时静态代码块会首先执行实现。
Connection
Connection
用于获取数据库操作对象PreparedStatement
或者Statement
以及执行事务的一些操作,主要有以下几个api:
//创建Statement对象
conn.createStatement()
//创建PrepareStatement对象
conn.prepareStatement(sql)
//开启或关闭事务自动提交
conn.autoCommit = false 或 true
//提交事务
conn.commit()
//回滚事务
conn.rollback()
举个使用事务的例子,java中是使用异常机制来处理事务:
val url = "jdbc:mysql://localhost:3306/server_from_zero"
val user = "root"
val pwd = "Root111230!"
val conn = DriverManager.getConnection(url,user,pwd)
val sql = "update user set age = 17 where user.name = '李四'"
val sql_update = "update user set age = 20 where user.name = '张三'"
val stmt = conn.createStatement()
try {
conn.autoCommit = false
stmt.executeUpdate(sql)
stmt.executeUpdate(sql_update)
//如果这里之前发生异常就会回滚
conn.commit()
}catch (e:Exception){
conn.rollback()
}
stmt.close()
conn.close()
ps:注意异常里事务语句放置的位置
Statement
Statement
用于执行数据库语句,主要有一系列executexxx(sql)
方法。
val set = stmt.executeQuery(sql)//返回一个ResultSet结果集
val count = stmt.executeUpdate(sql)//返回受影响的行数
ResultSet
ResultSet
使用Statement.executeQuery(sql)
后返回的数据,ResultSet使用如下所示:
while (set.next()){
//下标从1开始,也可以传入列名
val id = set.getInt(1)
val name = set.getString(2)
val age = set.getInt(3)
println("$id $name $age")
}
//记得释放资源
set.close()
PreparedStatement
继承自Statement,有两个好处:
- 预编译SQL语句并执行以提升性能,
- 通过转义敏感字符达到预防SQL注入问题。
预编译功能需要在获取连接时在url里加入useServerPrepStmts=true
,如下语句所示:
jdbc:mysql://localhost:3306/server_from_zero&useServerPrepStmts=true
mysql执行sql语句的过程如下:
检查sql语法->编译sql->执行sql
PreparedStatement对于同一个模版的SQL语句只会在第一次编译一次sql,后续直接复用缓存,这个过程可以通过查看mysql日志文件验证。
sql注入
譬如登录流程,一般来说语句会是下面这样:
select * from user where account = '' and password = ''
如果account和password匹配上就可以登录成功,但如果password被注入后即使密码是错的也能登录成功。
//直接在密码输入框输入下面的文案
' or '1'='1'
因为sql语句被拼接后可能是:
select * from user where account = '' and password = '' or '1'='1'
此时语句总会成功执行,密码就形同虚设了。PreparedStatement就是用来解决这个问题的。
//通过问号设置参数
val sql = "select * from user where account = ? and password = ?"
val stmt = conn.prepareStatement(sql)
//第一个问号
stmt.setString(1,"123")
//第二个问号
stmt.setString(2,"123")
val set = stmt.executeQuery()
while (set.next()){
val id = set.getInt(1)
val name = set.getString(2)
val age = set.getInt(3)
println("$id $name $age")
}
PreparedStatement通过对敏感字符进行转义(在敏感字符前加\
),转义后就可以预防sql注入。
数据库连接池
本节介绍数据库连接池的基本信息和Druid(德鲁伊)框架的使用
SUN公司定义了数据库连接池的接口DataSource
,内部有一个Connection getConnection()
方法
- 数据库连接池是个容器,用来负责分配、管理数据库连接
- 允许应用程序重复使用一个现有的数据库连接
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没释放而引起的数据库连接遗漏
数据库连接池的优点有:
- 资源复用
- 提升响应速度
- 避免数据库连接遗漏
Druid使用
如何使用具体看官网