没有JDBC的时候,如果现在要开发一套系统,使用Java连接MySQL数据库,那么这时候Java程序员需要了解MySQL驱动API,如果使用Java连接Oracle数据库,那么这个时候Java程序员需要了解Oracle数据库驱动API。
SUN公司提供一套统一的规范(接口)。然后各个数据库生产商提供这套接口的实现。这套接口规范就是JDBC的规范。
快速入门
- 加载jar驱动包
- 获得连接
- 基本操作
- 释放资源
package t_jdbc;
import jdk.internal.instrumentation.ClassInstrumentation;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class jdbc入门 {
public static void main(String[] args) throws Exception {
// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获得连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
// 执行sql
// 获得执行对象
Statement statement = conn.createStatement();
// 编写sql语句
String sql = "select * from class";
// 执行sql
ResultSet rs = statement.executeQuery(sql);
// 遍历结果集
while (rs.next()){
System.out.print(rs.getInt("id") + " ");
System.out.println(rs.getString("name"));
}
// 释放连接
rs.close();
statement.close();
conn.close();
}
}
JDBC的API详解
DriverManager(驱动管理类)
-
注册驱动
这个方法可以完成驱动的注册,但是实际开发中一般不会使用这个方法完成驱动的注册!!!
原因:
如果需要注册驱动,就会使用DriverManager.registerDriver(new Driver());,但是查看源代码发现,在代码中有一段静态代码块,静态代码块已经调用了注册驱动的方法。如果再手动调用该方法注册驱动,就会导致驱动被注册两次 -
获得连接
getConnection(url,user,password);
这个方法就是用来获得与数据库连接的方法:这个方法中有三个参数:
url :与数据库连接的路径
user :与数据库连接的用户名
password :与数据库连接的密码
主要关注的是url的写法:
jdbc:mysql://localhost:3306/web_test3
jdbc :连接数据库的协议
mysql :是jdbc的子协议
localhost :连接的MySQL数据库服务器的主机地址。(连接是本机就可以写成localhost),如果连接不是本机的,就需要写上连接主机的IP地址。
3306 :MySQL数据库服务器的端口号
web_test3 :数据库名称
Connection(接口)
连接对象
- 创建执行sql语句的对象
执行SQL语句对象:
Statement :执行SQL
CallableStatement :执行数据库中存储过程
PreparedStatement :执行SQL.对SQL进行预处理。解决SQL注入漏洞。
- 管理事务
statement
执行sql
- 执行sql语句
执行SQL的方法
boolean execute(String sql);
执行查询,修改,添加,删除的SQL语句。
ResultSet executeQuery(String sql);
执行查询(执行select语句)。
int executeUpate(String sql);
执行修改,添加,删除的SQL语句。
execute 方法执行 SQL 语句如果第一个结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回 false
- 执行批处理
resultSet(结果集)
通过select语句的查询结果。
- 结果集遍历
- 结果集的获取
资源释放
DBC程序执行结束后,将与数据库进行交互的对象释放掉,通常是ResultSet,Statement,Connection。
这几个对象中尤其是Connection对象是非常稀有的。这个对象一定要做到尽量晚创建,尽早释放掉。
将资源释放的代码写入到finally的代码块中。
资源释放的代码应该写的标准:
if(rs !=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(statement !=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
statement = null;
}
if(conn !=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
sql注入漏洞
在早期互联网上SQL注入漏洞普遍存在。有一个网站,用户需要进行注册,用户注册以后根据用户名和密码完成登录。假设现在用户名已经被其他人知道了,但是其他人不知道你的密码,也可以登录到网站上进行相应的操作。
登录功能
String sql = "select * from user where username = ' "+username+" ' and password = ' "+password+" ' ;";
ResultSet rs = statement.executeQuery(sql);
if (rs.next()){
System.out.println("登录成功");
return true;
}
sql注解漏洞
知道用户名
输入
用户名:aaa‘ or '1=1
密码随意
或者用户名:aaa' --
密码随意
原因
select * from user where username = ' aaa' or '1 = 1 ' and password = ' "+password+" ' ;
--在sql语句中表示注释,只执行了前面
select * from user where username = ' aaa' -- ' and password = ' "+password+" ' ;
解决
采用PreparedStatement
对象解决。这个对象将SQL预先进行编译,使用?作为占位符。?所代表内容是SQL所固定。再次传入变量(包含SQL的关键字)。这个时候也不会识别这些关键字。
public static boolean login(String username, String password) throws Exception {
boolean flas = false;
Connection coon = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql:///test","root","root");
//编写sql语句
String sql = "select * from user where username = ? and password = ? ";
//预编译
pstmt = coon.prepareStatement(sql);
//设置参数1是第一个?
pstmt.setString(1,username);
pstmt.setString(2,password);
rs = pstmt.executeQuery();
批处理
mysql默认情况下批处理是关闭的需要在url后添加?rewriteBatchedStatements=true
参数
Connection coon = null;
Statement stmt = null;
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
stmt = coon.createStatement();
String sql1 = "create table user(id int primary key auto_increment,name varchar(20))";
String sql2 = "insert user values (null,'kang')";
String sql3 = "insert user values (null,'wu')";
String sql4 = "insert user values (null,'xiao')";
String sql5 = "insert user values (null,'feng')";
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.addBatch(sql4);
stmt.addBatch(sql5);
stmt.executeBatch();
Connection coon = null;
PreparedStatement stmt = null;
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
String sql = "insert into usear values (null,?)";
stmt = coon.prepareStatement(sql);
for (int i=1;i<100;i++){
stmt.setString(1,"name" + i);
stmt.addBatch();
if(i % 10 == 0 ){
stmt.executeBatch();
stmt.clearBatch();
}
}
事务
Connection coon = null;
PreparedStatement pstm = null;
Class.forName("com.mysql.jdbc.Driver");
coon = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
//开启事务,关闭自动提交
coon.setAutoCommit(false);
String sql = "update account set money + ? where name = ?";
pstm = coon.prepareStatement(sql);
pstm.setDouble(1,-1000);
pstm.setString(2,"kang");
pstm.executeUpdate();
pstm.setDouble(1,1000);
pstm.setString(2,"WU");
pstm.executeUpdate();
//提交事务
coon.commit();
//如果有错,回滚coon.rollback();
连接池
自定义连接池
编写类实现DataSource接口,重写getConnection,初始化多个连接,编写归还方法。
public class jdbc_DataSource implements DataSource {
//定义集合存储连接对象
private List<Connection> connList = new ArrayList();
//初始化获得连接
public jdbc_DataSource() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
for (int i=1;i<3;i++){
connList.add(DriverManager.getConnection("","",""));
}
}
@Override
//从连接池获得连接
public Connection getConnection() throws SQLException {
Connection coon = connList.remove(0);
return coon;
}
//编写归还方法
public void addBack(Connection coon){
connList.add(coon);
}
jdbc_DataSource jd = null;
Connection coon = null;
try{
jd = new jdbc_DataSource();
coon = jd.getConnection();
}
catch (Exception e){
e.printStackTrace();
}
finally {
jd.addBack(coon);
}
Druid连接池
导入jar包
Connection coon = null;
PreparedStatement psmt = null;
ResultSet rs = null;
//使用连接池
DruidDataSource dataSource = new DruidDataSource();
//手动设置参数
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///test");
dataSource.setUsername("root");
dataSource.setPassword("root");
//获得连接
coon = dataSource.getConnection();
String sql = "select * from test";
psmt = coon.prepareStatement(sql);
rs = psmt.executeQuery();
Druid参数可以用属性配置文件的方式配置。文件名没有规定但是属性文件中的key是一定的
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///web_test4?rewriteBatchedStatements=true
username=root
password=abc
Properties properties = new Properties();
properties.load(new FileInputStream("src/druid.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
coon = dataSource.getConnection();
C3P0连接池
导入jiar包
Connection coon = null;
PreparedStatement psmt = null;
ResultSet rs = null;
//使用连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//设置参数
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///test");
dataSource.setUser("root");
dataSource.setPassword("root");
//获得连接
coon = dataSource.getConnection();
String sql = "select * from test";
psmt = coon.prepareStatement(sql);
rs = psmt.executeQuery();
设置配置文件c3p0-config.xml
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///test</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">5</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!-- This app is massive! -->
<!-- <named-config name="oracle">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web_test4</property>
<property name="user">root</property>
<property name="password">abc</property>
</named-config> -->
</c3p0-config>
//使用连接池,创建连接池会默认去查找c3p0-config.xml文件
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle");
Dbutils
对jdbc的简单封装,而且没有影响性能
导入jar包
添加,修改,删除操作
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//创建核心类
QueryRunner queryRunner =new QueryRunner(dataSource);
//添加(insert),修改(update),删除(delete),传入sql和可变参数
queryRunner.update("insert into test values (null,?,?)","ddd",1000);
查询操作
先创建一个类,存储从数据库查到的数据
public class Account {
private Integer id;
private String name;
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;
}
}
查一条
//查询,传入sql,ResultHandler一个结果集的接口,可变参数
Account account = queryRunner.query("select * from test where id = ?", new ResultSetHandler<Account>(){
@Override
public Account handle(ResultSet rs) throws SQLException {
Account account1 =new Account();
while (rs.next()){
account1.setId(rs.getInt("id"));
account1.setName(rs.getString("name"));
}
return account1;
}
},1);
查多条
queryRunner.query("select * from test", new ResultSetHandler<List<Account>>() {
@Override
public List<Account> handle(ResultSet rs) throws SQLException {
List<Account> list = new ArrayList<>();
while (rs.next()){
Account account1 =new Account();
account1.setId(rs.getInt("id"));
account1.setName(rs.getString("name"));
list.add(account1);
}
return list;
}
});
ResultSetHandler实现类
ArrayHandler
将一条记录封装到数组当中。这个数组是Object[]
ArrayListHandler
将多条记录封装到装有Object的list集合中
Object[] objs = queryRunner.query("select * from test where id = ?",new ArrayHandler(),1);
System.out.println(Arrays.toString(objs));
List<Object[]> list = queryRunner.query("select * from test",new ArrayListHandler());
BeanHandler将一条记录装到javaBean中
BeanListHandler将多条记录装到一个装有Javabe的list集合中
Account account1 = queryRunner.query("select * from test where id = ?",new BeanHandler<Account>(Account.class),1);
List<Account> list1 = queryRunner.query("select * from test",new BeanListHandler<Account>(Account.class));
MapHandler将一条记录封装到一个Map集合中,key是列名,value就是表中列的记录值
MapListHandler装到一个装有Map的list集合中
Map<String,Object> map = queryRunner.query("select * from test where id = ?",new MapHandler(),1);
List<Map<String,Object>> list2 = queryRunner.query("select * from test",new MapListHandler());
ColumnListHandler
将数据中的莫列装到list中
ScalarHandler
将单个值封装
KeyedHandler
将一条记录装到一个map中。将多个记录装到一个map集合的map集合中,外面的key可以指定
List<Object> list3 = queryRunner.query("select * from test",new ColumnListHandler("name"));
Object o = queryRunner.query("select count(*) from test",new ScalarHandler());
Map<Object,Map<String,Object>> map1 = queryRunner.query("select * from test",new KeyedHandler("id"));