JDBC

一、数据库驱动

类似于显卡驱动的东西,应用程序不可以直接与数据库相连,所以要用到驱动;

二、JDBC

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,==是一个规范而不是一个实现==,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package saxon;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
//连接云数据库
public class FirstDemo {
public static void main (String[] args) throws Exception{
//1.加载驱动 驱动的版本一定要和数据库的版本相适应
Class.forName ("com.mysql.cj.jdbc.Driver");//固定写法 com.mysql.cj.jdbc.Driver
//2.用户信息和url
/*
rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/saxon 主机名(外网地址)和端口号 数据库名
useUnicode=true&characterEncoding=utf8:是否使用中中文字符和解码的规则
SSL:SSL VPN是以SSL协议为安全基础的VPN远程接入技术,移动办公人员(在SSL VPN中被称为远程用户)使用SSL VPN可以安全、 访问企业内网资源,提高工作效率。
*/
String url="jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/saxon?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName="saxon";//用户名
String password="19990707";//密码
//3.连接成功 connection:就是一个数据库对象
Connection connection = DriverManager.getConnection (url, userName, password);
//4.获取statement对象,这个对象用于执行sql语句
Statement statement = connection.createStatement ();
//5.执行sql语句 返回一个结果resultSet 是一个链表
String sql="SELECT * FROM student";
ResultSet resultSet = statement.executeQuery (sql);//返回的是全部查询的结果,数据库查询
statement.executeUpdate ();//数据库的增删改
while (resultSet.next ()){
System.out.println ("sno:"+resultSet.getObject ("sno"));
System.out.println ("sname:"+resultSet.getObject ("sname"));
System.out.println ("===========================================");
}
//6 释放连接 避免资源的浪费
resultSet.close ();
statement.close ();
connection.close ();

}
}

步骤总结:

  1. 加载驱动
  2. 连接数据库 DrivrManager
  3. 创建对象 statement
  4. 获得结果,可以对结果进行一个操作
  5. 释放连接

三、关于对象的解释

1.DriverManager
1
2
3
4
5
6
7
8
9
10
11
12
13
Class.forName ("com.mysql.cj.jdbc.Driver")
//加载驱动就会执行下面的动态代码块,就不用再用DriverManager.registerDriver(new Driver()),目的只是为了获得驱动,所以不用在返回对象
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}


//数据库本身,可以执行事务的回滚和提交
Connection connection = DriverManager.getConnection (url, userName, password);
2.URL
1
2
3
4
5
6
7
String url="jdbc:mysql://
rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com
:3306 //端口号
/saxon //用户名
?useUnicode=true//
&characterEncoding=utf8
&useSSL=true";
3.statement
1
2
3
4
5
6
7
8
9
//statement 执行sql语句的对象 
Statement statement = connection.createStatement ();
//***************************************************************
//sql语句
String sql="SELECT * FROM student";
//*********************************
int i = statement.executeUpdate (sql);//执行sql语句,返回值是受影响的行数
ResultSet resultSet = statement.executeQuery (sql);//返回的是全部查询的结果
boolean execute = statement.execute (sql);//返回执行语句是否成功
4.resultSet(结果集)
1
2
3
4
5
6
7
8
9
10
//返回的是全部查询的结果,数据库查询 只有查询语言才有,返回的是一个结果集
ResultSet resultSet = statement.executeQuery (sql);
//具体的数据类型
resultSet.getObject ();//不知道数据类型
//具体你设置的数据类型 varchar=>string
resultSet.getShort ();
resultSet.getString ();
resultSet.getInt ();
resultSet.getTime ();

对于结果的处理:

1
2
3
4
5
resultSet.next ();//跳到下一行
resultSet.previous ();//移动到前一行
resultSet.absolute (int row);//移动到具体的行数
resultSet.afterLast ();//移动到最后面
resultSet.beforeFirst ();//移动到最后面
5.释放资源

消耗资源,关闭节约资源

1
2
3
resultSet.close ();
statement.close ();
connection.close ();

四、statement对象

1.配置文件的设置及使用
1
2
3
4
5
#配置文件不用在末尾加上句号,以行为标准 目录在src目录下
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/saxon?useUnicode=true&characterEncoding=utf8&useSSL=true
userName=saxon
password=19990707
1
2
3
4
5
6
7
8
9
10
11
12
13
//使用类加载器获得一个属性文件的输入流getResourceAsStream
InputStream input = JdbcUtil.class.getClassLoader ().getResourceAsStream ("JDBCUTIL.properties");
Properties properties = new Properties ();
try {
properties.load (input);//将资源加载
//拿出配置文件的信息
String driver = properties.getProperty ("driver");
String url = properties.getProperty ("url");
String userName = properties.getProperty ("userName");
String password = properties.getProperty ("password");
} catch (Exception e) {
e.printStackTrace ();
}
2.工具类的书写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package saxon.util;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtil {
static {
InputStream input = JdbcUtil.class.getClassLoader ().getResourceAsStream ("JDBCUTIL.properties");
Properties properties = new Properties ();
try {
properties.load (input);
String driver = properties.getProperty ("driver");
String url = properties.getProperty ("url");
String userName = properties.getProperty ("userName");
String password = properties.getProperty ("password");
//1.加载驱动程序
Class.forName (driver);
} catch (Exception e) {
e.printStackTrace ();
}
}

/**
* 获得数据库的连接
* @param url 连接信息
* @param userName 用户名
* @param password 密码
* @return 返回的是连接
*/
public static Connection getConnection (String url, String userName, String password) {
Connection connection = null;
try {
connection = DriverManager.getConnection (url, userName, password);
} catch (SQLException throwables) {
throwables.printStackTrace ();
}
return connection;
}

/**
* 对数据库的资源进行释放
* @param connection 数据库的连接
* @param statement 数据库的sql操作实体,操作增删改查的对象
* @param resultSet 如果是数据库的查询语言,那么就会产生这个对象
*/
public static void release (Connection connection, Statement statement, ResultSet resultSet) {
try {
if (connection != null) {
connection.close ();
}
if (statement != null) {
statement.close ();
}
if (resultSet != null) {
resultSet.close ();
}
} catch (Exception e) {
e.printStackTrace ();
}
}

/**
* 对数据库的操作进行反馈
* @param connection 数据库的连接
* @param sql sql 语言
* @param isSelect 是否是数据库查询语言,如果是的话,那么就是statement.executeQuery,否则就是statement.executeUpdate
* @throws Exception 抛出异常,太多了麻烦,一次性全部抛出
*/
public static void Info (Connection connection, String sql, boolean isSelect) throws Exception {
Statement statement = null;
statement = connection.createStatement ();
if (! isSelect) {
int i = statement.executeUpdate (sql);
System.out.println ("Affected rows: " + i);
statement.close ();
} else {
ResultSet resultSet = statement.executeQuery (sql);
while (resultSet.next ()) {
System.out.println ("studentno:" + resultSet.getInt ("studentno"));
System.out.println ("subjectno:" + resultSet.getInt ("subjectno"));
System.out.println ("examdate:" + resultSet.getDate ("examdate"));
System.out.println ("studentresult:" + resultSet.getInt ("studentresult"));
System.out.println ("****************************************************************");
}
statement.close ();
resultSet.close ();
}
}
}


3.测试
1
2
3
4
5
6
7
8
9
10
11
12
public class TestUtil {
public static void main (String[] args)throws Exception {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
String sql = "INSERT INTO result VALUES(\"1005\",\"3\",\"2020-05-14 16:00:00\",\"100\");";
String check="SELECT * FROM result";
JdbcUtil.Info (connection,sql,false);
JdbcUtil.release (connection,null,null);
}
}

五、SQL的注入问题

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息

1
2
//判定的语句中有一些永真式,导致所有的信息都会输出,导致信息泄露
String check="SELECT * FROM `result` WHERE `studentno`='1000' or '1==1'";

六、PreparedStatement对象

1、增
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package saxon.util;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class statementTest {
public static void main (String[] args)throws Exception {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
//**********************************************************
//区别1:可以利用占位符来进行一个对于数据的占位,在后面在开始设置,提高安全性
String sql = "INSERT INTO result VALUES(?,?,?,?);";
//预编译,先编译在执行
PreparedStatement statement = connection.prepareStatement (sql);
/*
* 设置问号的位置 void setInt(int parameterIndex, int x) throws SQLException;
* parameterIndex:问号的位置,从一开始
* */
statement.setInt (1,2000);
statement.setInt (2,10);
statement.setDate (3,new Date (new java.util.Date ().getTime ()));
statement.setInt (4,10);
//在执行一遍 输出改变的行
System.out.println (statement.executeUpdate ());
JdbcUtil.release (connection,null,null);
}
}

2.改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package saxon.util;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class statementTest {
public static void main (String[] args)throws Exception {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
//**********************************************************
//区别1:可以利用占位符来进行一个对于数据的占位,在后面在开始设置,提高安全性
String sql = "update result set `subjectno`=? where `studentno`=?;";
//预编译,先编译在执行
PreparedStatement statement = connection.prepareStatement (sql);
/*
* 设置问号的位置 void setInt(int parameterIndex, int x) throws SQLException;
* parameterIndex:问号的位置,从一开始
* */
statement.setInt (1,100);
statement.setInt (2,2000);
//在执行一遍 输出改变的行
System.out.println (statement.executeUpdate ());
JdbcUtil.release (connection,statement,null);
}
}

3.删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package saxon.util;

import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class statementTest {
public static void main (String[] args)throws Exception {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
//**********************************************************
//区别1:可以利用占位符来进行一个对于数据的占位,在后面在开始设置,提高安全性
String sql = "delete from result where `subjectno`=?";
//预编译,先编译在执行
PreparedStatement statement = connection.prepareStatement (sql);
/*
* 设置问号的位置 void setInt(int parameterIndex, int x) throws SQLException;
* parameterIndex:问号的位置,从一开始
* */
statement.setInt (1,2);
//在执行一遍 输出改变的行
System.out.println (statement.executeUpdate ());
JdbcUtil.release (connection,statement,null);
}
}

4.查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package saxon.util;

import java.sql.*;

public class statementTest {
public static void main (String[] args) throws Exception {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
//**********************************************************
//区别1:可以利用占位符来进行一个对于数据的占位,在后面在开始设置,提高安全性
String sql = "select * from result where `subjectno`=?";
//预编译,先编译在执行
PreparedStatement statement = connection.prepareStatement (sql);
/*
* 设置问号的位置 void setInt(int parameterIndex, int x) throws SQLException;
* parameterIndex:问号的位置,从一开始
* */
statement.setInt (1, 4);
//在执行一遍 输出改变的行
ResultSet resultSet = statement.executeQuery ();
while (resultSet.next ()) {
System.out.println ("studentno:" + resultSet.getInt ("studentno"));
System.out.println ("subjectno:" + resultSet.getInt ("subjectno"));
System.out.println ("examdate:" + resultSet.getDate ("examdate"));
System.out.println ("studentresult:" + resultSet.getInt ("studentresult"));
System.out.println ("****************************************************************");
}
JdbcUtil.release (connection, statement, resultSet);
}
}

prepareStatement可以有效的防止SQL注入的问题

七、ideal处理事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package saxon;

import saxon.util.JdbcUtil;

import java.sql.*;

public class transactionTest {
public static void main (String[] args) {
String url = "jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true";
String userName = "saxon";
String password = "19990707";
Connection connection = JdbcUtil.getConnection (url, userName, password);
PreparedStatement statement=null;
try {
connection.setAutoCommit (false);
String sql="update result set `studentresult`=`studentresult`+50 where `studentno`=1004";
statement = connection.prepareStatement (sql);
statement.executeUpdate ();
int x=1/0;
String sql2="update result set `studentresult`=`studentresult`-50 where `studentno`=2000";
statement = connection.prepareStatement (sql2);
statement.executeUpdate ();
connection.commit ();
System.out.println ("successful");
} catch (Exception throwables) {
try {
connection.rollback ();
System.out.println ("失败回滚");
} catch (SQLException e) {
e.printStackTrace ();
}
throwables.printStackTrace ();
}
try {
connection.close ();
statement.close ();
} catch (SQLException throwables) {
throwables.printStackTrace ();
}
}

}

步骤:

  1. connection.setAutoCommit (false);设置自动提交
  2. 编写事务代码
  3. connection.commit ();或者 connection.rollback ();默认失败回滚,==不用关闭自动提交,在提交后默认关闭==

八、数据库连接池

池化技术:使用完的数据库连接不会消失,会进入池中,等待再次被使用,可以避免重复的回收资源(connection.close)。使用和线程池差不多

一.DBCP

需要导入的包:commons-dbcp2-2.7.0.jar,commons-logging-1.2.jar,commons-pool2-2.8.0.jar;

1.编写配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#连接设置 url:一定要写对
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=true
username=saxon
password=19990707

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=6000
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
2.编写的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package saxon.util;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class dbcpUtil {
private static DataSource dataSource=null;
static {
InputStream input = JdbcUtil.class.getClassLoader ().getResourceAsStream ("dbcp.properties");
Properties properties = new Properties ();
try {
properties.load (input);
//自动获得连接对象
dataSource = BasicDataSourceFactory.createDataSource (properties);
} catch (Exception e) {
e.printStackTrace ();
}
}

public static Connection getConnection () throws SQLException {
return dataSource.getConnection ();
}

}

3.测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package saxon;
import saxon.util.dbcpUtil;

import java.sql.Connection;
import java.sql.Statement;

public class dbcpTest {
public static void main (String[] args)throws Exception {
Connection connection = dbcpUtil.getConnection ();
Statement statement = connection.createStatement ();
System.out.println (statement.executeUpdate ("INSERT INTO result VALUES(\"1005\",\"3\",\"2020-05-14 16:00:00\",\"10086\");"));
connection.close();
statement.close();
}
}

二、C3P0

需要的JAR包:mchange-commons-java-0.2.11.jar,c3p0-0.9.5.1.jar;==需要注意的是,这两个包需要把版本号进行一个匹配,如果一个版本号过高就会失败==

1.配置文件;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<c3p0-config>
<!-- 使用默认的配置读取连接池对象-->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://rm-wz917wbvou67a757quo.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true
</property>
<property name="user">saxon</property>
<property name="password">19990707</property>

<!-- 连接池参数 -->
<!-- 初始连接数 -->
<property name="initialPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">10</property>
<!-- 最大等待时间 -->
<property name="checkoutTimeout">2000</property>
<!-- 最大空闲回收时间 -->
<property name="maxIdleTime">1000</property>
</default-config>
</c3p0-config>

==文件放置在src目录下,前提是src已经被设置为sources root,否则不会成功==

==文件必须用括号包起来,括号里面一定是C3P0-config,否则读不出来==

2.工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package saxon.util;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class c3p0Util {
private static ComboPooledDataSource dataSource=null;
static {
try {
dataSource =new ComboPooledDataSource("MYSQL");
} catch (Exception e) {
e.printStackTrace ();
}
}

public static Connection getConnection () throws SQLException {
return dataSource.getConnection ();
}
}

3.测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package saxon;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import saxon.util.c3p0Util;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

public class c3p0Test {
public static void main (String[] args) {
try {
Connection connection = c3p0Util.getConnection ();
Statement statement = connection.createStatement ();
System.out.println (statement.executeUpdate ("INSERT INTO result VALUES(\"1005\",\"3\",\"2020-05-14 16:00:00\",\"100861\");"));
connection.close ();
} catch (SQLException throwables) {
throwables.printStackTrace ();
}
}
}
三、总结

无论是哪一种最后都实现了DataSource接口,原理不变;感觉和线程池一样;