【自学笔记】 Java基础 - JDBC
# JDBC基础阅读这篇笔记你需要掌握反射和Mysql这些知识。
## 概念
JDBC ( Java DataBase Connectivity java数据库连接)是-种用于执行SQL语句的JavaAPI ,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。其实就是java官方提供的一套规范(接口),用于帮助开发人员快速实现不同关系型数据库的连接。
## 快速入门
- 导入jar包
- 注册驱动
- 获取数据库连接
- 获取执行者对象
- 执行sql语句并返回结果
- 处理结果
- 释放资源
```Java
public class JDBCDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// -导入jar包
// - 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// - 获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/qq", "root", "root");
// - 获取执行者对象
Statement statement = connection.createStatement();
// - 执行sql语句并返回结果
String sql = "SELECT * FROM user";
ResultSet resultSet = statement.executeQuery(sql);
// - 处理结果
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
System.out.print(id + "");
System.out.println(username);
}
// - 释放资源
resultSet.close();
statement.close();
connection.close();
}
}
```
## 功能类解释
### DriverManager
DriverManager取得管理对象
1. 注册驱动
- 注册给定的驱动程序:`static void registerDriver(Driver driver)`
- 写代码使用:`Class.forName("com.mysql.jdbc.Driver")`
- 在`com.mysqlJdbc.Driver`类中存在静态代码块,里面就已经实现注册驱动
```Java
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
```
- 注意事项
1. 我们不需要提供`DriverManager`调用静态方法`registerDriver()`,以为只要Driver类被使用,则会执行其静态代码块完成驱动注册
2. mysql5之后可以省略注册驱动步骤。在jar包中,存在一个`java.sql.Driver`配置文件,文件中指定了`com.mysql.jdbc.Driver`
2. 获取数据库连接
获取数据库连接对象:`static Connection getConnection(String url,String user,String password)`
参数:
url:指定连接的路径。语法:`"jdbc:mysql://ip地址(域名):端口号/数据库名称"`
user:用户名
password:密码
返回值:`Connection`数据库连接对象
### Connection
Connection数据库连接对象
1. 获取执行者对象
获取普通执行者对象:`Statement createStatement();`
获取预编译执行者对象:`PreparedStatement prepareStatement(Sting sql);`
2. 管理事务
开启事务:`setAutoCommit(boolean autoCommit); `参数为false,则开启事务
提交事务:commit();
回滚事务:rollback();
3. 释放资源
`void close()`
### Statement
Statement执行sql语句对象
1. 执行DML(增删改)语句
`int executeUpdate(String sql)`:
返回值int,返回影响的行数
参数sql:可以执行insert、update、delete语句
2.执行DQL语句
`ResultSet executeQuery(String sql)`
返回值ResultSet:封装查询结果
参数sql:可以执行select语句
3.释放资源
`void close()`
### ResultSet
ResultSet结果集对象
1. 判断结果集当作是否还要数据`boolean next()`;
有数据返回true,并将索引向下移动一行
没有数据返回false
2.获取结果集中的数据:XXX getXXX("列名")
xxx代表数据类型(要获取某列数据,这一列的数据类型)
例如:`String getString("name)`; `int getInt("age")`
3.释放资源
`void close()`
## 工具类
1. 编写配置文件,在src目录下创建config.properties配置文件
config.properties:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/qq
username=root
password=root
2.编写jdbc工具类
```Java
public class JDBCUtils {
// 1.私有化构造方法
private JDBCUtils(){
}
// 2.声明所需要的配置变量
private static String driverClass;
private static String url;
private static String username;
private static String password;
private static Connection con;
// 3.提供静态代码块,读取配置文件的信息为变量赋值,注册驱动
static {
// 读取配置文件的信息为变量赋值
try {
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties");
Properties properties = new Properties();
properties.load(is);
driverClass= properties.getProperty("driverClass");
url= properties.getProperty("url");
username= properties.getProperty("username");
password= properties.getProperty("password");
// 注册驱动
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
}
}
// 4.提供获取数据库连接方法
public static Connection getConnection(){
try {
con = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
// 5.提供释放资源的方法
public static void close(Connection con, Statement stat, ResultSet rs){
if (con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 5.提供释放资源的方法
public static void close(Connection con, Statement stat){
if (con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stat!=null){
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
```
## SQL注入攻击解决
- PreparedStatement预编译执行者对象
1. SQL语句之前,将SQL语句进行提取编译。明确SQL语句的格式后,就不会在改变,剩余的内容都会认为时参数。
2. SQL语句中的参数使用?占位符
- 为?占位符赋值的方法:setXxx(参数1,参数2)
Xxx:数据类型
参数1:?的位置编号(编号从1开始)
参数2:?的实际参数
```Java
string sq1 = "DELETE FROM user WHERE name=?"
pstm = conn.preparestatement(sq1) ;
pstm. setstring(1,"张三");
```
- 执行SQL语句
1. 执行inset、update、delete语句:int executeUpdate();
2. 执行select语句:ResultSet executeQuery();
```Java
public class JDBCDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// -导入jar包
// - 注册驱动
// Class.forName("com.mysql.jdbc.Driver");
// - 获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/qq", "root", "root");
// - 获取执行者对象
// - 执行sql语句并返回结果
String sql = "SELECT * FROM user WHERE name = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"张三");
ResultSet resultSet = preparedStatement.executeQuery(sql);
// - 处理结果
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
System.out.print(id + "");
System.out.println(username);
}
// - 释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
```
## 事务管理
管理事务功能类:Connection
- 开启事务:`setAutoCommit(boolean autoCommit):参数为false,则开始事务
- 提交事务:commit();
- 回滚事务:rollback();
## 连接池
### 概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。可以有效的提升数据库操作的性能
### 自定义数据库连接池
#### DataSource
##### 概述
`Javax.sql.DataSource`接口:数据源(数据库连接池)。Java官方提供的数据库连接池规范(接口)
##### 步骤
① 定义一一个类,实现DataSource接口。
② 定义一个容器,用于保存多个Connection连接对象。
③ 定义静态代码块,通过JDBC工具类获取10个连接保存到容器中。
④ 重写getConnection方法,从容器中获取一个连接并返回。
⑤ 定义getSize方法,用于获取容器的大小并返回。
```Java
/**
* 自定义数据库连接池
*/
public class MyDataSource implements DataSource {
// 准备容器,用于保存多个连接对象
// 通过Collections.synchronizedList获取线程安全的集合对象
private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());
// 定义静态代码块,通过工具类获取10个连接对象
static {
for (int i = 0; i < 10; i++) {
Connection connection = JDBCUtils.getConnection();
pool.add(connection);
}
}
// 重写getConnection方法,用于获取连接对象
@Override
public Connection getConnection() throws SQLException {
if (pool.size()>0){
Connection con = pool.remove(0);
return con;
}else{
throw new RuntimeException("连接数量已用尽");
}
}
// 定义getSize方法,获取连接池容器的大小
public int getSize(){
return pool.size();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
public class DataSourceTest {
public static void main(String[] args) throws SQLException {
// 创建连接池对象
MyDataSource dataSource = new MyDataSource();
// 通过连接池对象获取连接对象
Connection connection = dataSource.getConnection();
// 查询学学生表的信息
String sql = "SELECT * FROM user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
System.out.print(resultSet.getInt("id"));
System.out.println(resultSet.getString("username"));
}
JDBCUtils.close(connection,preparedStatement);
}
}
```
##### 归还连接
###### 继承方式(不能用,理解思想即可)
- 通过打印连接对象,发现DriverManager获取的连接实现类是JDBC4Connection
- 那我们就可以自定义一个类,继承JDBC4Connection这个类,重写close(方法,完成连接对象的归还
- 实现步骤
①定义一个类,继承JDBC4Connection.
②定义Connection连接对象和连接池容器对象的成员变量。
③通过有参构造方法完成对成员变量的赋值。
④重写close方法,将连接对象添加到池中。
```Java
public class MyConnection1 extends JDBC4Connection {
private Connection connection;
private List<Connection> pool;
public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url, Connection connection, List<Connection> pool) throws SQLException {
super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
this.connection = connection;
this.pool = pool;
}
@Override
public void close() throws SQLException{
pool.add(connection);
}
}
```
###### 装饰设计模式
- 我们可以自定义-个类实现Connection接口。这样就具备了和JDBC4Connection相同的行为了
- 重写close(方法,完成连接的归还。其余的功能还调用mysql驱动包实现类原有的方法即可
- 实现步骤:
①定义一个类,实现Connection接口
②定义Connection连接对象和连接池容器对象的成员变量
③通过有参构造方法完成对成员变量的赋值
④重写close(方法,将连接对象添加到池中
⑤剩余方法,只需要调用mysql驱动包的连接对象完成即可
⑥在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装
注:这里把开头的几个做了个模板,后面的没做
```Java
package com.BJ.JDBC.JDBCPool.Demo3;
import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class MyConnection2 implements Connection {
private Connection connection;
private List<Connection> pool;
public MyConnection2(Connection connection, List<Connection> pool) {
this.connection = connection;
this.pool = pool;
}
@Override
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return connection.prepareCall(sql);
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
}
@Override
public void close() throws SQLException {
pool.add(connection);
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
}
@Override
public String getSchema() throws SQLException {
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
```
###### 适配器设计模式
- 我们可以提供一个适配器类,实现Connection接口,将所有方法进行实现(除了close方法)
- 自定义连接类只需要继承这个适配器类,重写需要改进的close()方法即可
- 实现步骤
①定义一个适配器类,实现Connection接口。
②定义Connection连接对象的成员变量。
③通过有参构造方法完成对成员变量的赋值。
④重写所有方法(除了close ) ,调用mysq驱动包的连接对象完成即可.
⑤定义一一个连接类,继承适配器类。
⑥定义Connection连接对象和连接池容器对象的成员变量,并通过有参构造进行赋值。
⑦重写close0方法,完成归还连接。
⑧在自定义连接池中,将获取的连接对象通过自定义连接对象进行包装。
###### 动态代{过}{滤}理模式
- 我们可以通过Proxy来完成对Connection实现类对象的代{过}{滤}理
- 代{过}{滤}理过程中判断如果执行的是close方法,就将连接归还池中。如果是其他方法则调用连接对象原来的功能即可
- 动态代{过}{滤}理:在不改变目标对象方法的情况下对方法进行增强
- 组成:
被代{过}{滤}理的对象:真实的对象
代{过}{滤}理对象:内存中的一个对象
- 要求:
代{过}{滤}理对象必须和被代{过}{滤}理对象实现相同的接口
- 实现
Proxy.newProxyInstance()
- 实现步骤
①定义一个类,实现DataSource接口
②定义一个容器,用于保存多个Connection连接对象
③定义静态代码块,通过JDBC工具类获取10个连接保存到容器中
④重写getConnection方法,从容器中获取一个连接
⑤通过Proxy代{过}{滤}理,如果是close方法,就将连接归还池中。如果是其他方法则调用原有功能
⑥定义getSize方法,用于获取容器的大小并返回
### C3P0
#### 步骤
①导入jar包。
②导入配置文件到src目录下。
③创建C3PO连接池对象。
④获取数据库连接进行使用。
注:C3P0的配置文件会自动加载,但是必须交c3p0-config.xml或c3p0-config.properties(配置文件可以去百度找,基本上都是一样的)
```Java
public class C3P0Test1 {
public static void main(String[] args) throws SQLException {
// 创建c3p0的数据库连接池对象
DataSource dataSource = new ComboPooledDataSource();
// 通过连接池对象获取数据库连接
Connection connection = dataSource.getConnection();
// 执行操作
String sql = "SELECT * FROM user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery(sql);
// - 处理结果
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
System.out.print(id + "");
System.out.println(username);
}
// - 释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
<c3p0-config>
<!--使用默认的配置读取连接池对象-->
<default-config>
<!--连接参数-->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost/qq</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!-- 初始化的连接数量 -->
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<!-- 最大连接数量 -->
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</default-config>
</c3p0-config>
```
### Druid
①导入jar包。
②编写配置文件,放在src目录下。
③通过Properties集合加载配置文件。
④通过Druid连接池工厂类获取数据库连接池对象。
⑤获取数据库连接进行使用。
注:Druid不会自动加载配置文件,需要我们手动加载,但是文件的名称可以自定义
```Java
/**
* 1.通过Properties集合,加载配置文件
* 2.通过Druid连接池工厂类获取数据库连接池对象
* 3.通过连接池对象获取数据库连接
*/
public class DruidTest1 {
public static void main(String[] args) throws Exception {
// 获取配置文件
InputStream is = DruidTest1.class.getClassLoader().getResourceAsStream("druid.properties");
// 1.通过Properties集合,加载配置文件
Properties properties = new Properties();
properties.load(is);
// 通过Druid连接池工厂类获取数据库连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 3.通过连接池对象获取数据库连接
Connection connection = dataSource.getConnection();
// 执行操作
String sql = "SELECT * FROM user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery(sql);
// - 处理结果
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
System.out.print(id + "");
System.out.println(username);
}
// - 释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
配置文件:(文件名:druid.properties)
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/qq
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数量
maxActive=10
# 超时时间
maxWait=3000
```
### 数据库连接池工具类抽取
```Java
/**
* 数据库连接池的工具类
*/
public class DataSourceUtils {
// 1.私有化构造方法
private DataSourceUtils(){}
// 2.声明数据源变量
private static DataSource dataSource;
// 3.提供静态代码块,完成配置文件的加载和获取数据库连接池对象
static {
try{
// 获取配置文件
InputStream is = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
// 通过Properties集合,加载配置文件
Properties properties = new Properties();
properties.load(is);
// 通过Druid连接池工厂类获取数据库连接池对象
dataSource= DruidDataSourceFactory.createDataSource(properties);
}catch (Exception e){
e.printStackTrace();
}
}
// 4.提供一个获取数据库连接的方法
public static Connection getConnection(){
Connection con = null;
try {
con = dataSource.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return con;
}
// 5.提供一个获取数据连接池对象的方法
public staticDataSource getDataSource(){
returndataSource;
}
// 6.释放资源
public static void close(Connection con, Statement stat, ResultSet rs){
if (con!=null){
try {
con.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stat!=null){
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Connection con, Statement stat){
if (con!=null){
try {
con.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stat!=null){
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
``` 666666{:1_899:} 可以,内容要点基本提到,具体内容还要个人自学一下 好东西了,我收藏了。 谢谢分享 16du 发表于 2021-7-24 13:21
大佬写的好,排版怎么这么工整的呢?
{:301_998:}排版用markdown啊 感谢分享,刚好需要。 感谢分享,学习中。 谢谢了 我要多看看
页:
[1]
2