通过Java对数据操作的基础方式(+连接池)

非连接池

方式一:自己获取链接并通过硬编码方式获取

步骤:
注册驱动
获取连接
编写sql
创建预编译的语句执行者
设置参数 没有问号不用设置参数
执行sql
处理结果
释放资源
步骤代码

Class.forName("com.mysql.jdbc.Driver");//注册驱动
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/***", "root", "123456");
String  sql="select * from category";
PreparedStatement st=conn.prepareStatement(sql);
ResultSet rs=st.executeQuery();//还有个int的 executeUpdate
//释放资源.
		rs.close();
		st.close();
		conn.close();

方式二:通过自定义工具类和配置文件的方式

先上配置文件jdbc.properties

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/***
user=root
password=123456

通过一个方法读取配置文件信息,然后给工具类中定义的最终变量。说白了没有这个方法我们自己也可以通过io流实现。
代码如下,原理和上面一样。吧一些重复的代码放到了工具类中

JdbcUtils

public class JdbcUtils {
	static final String DRIVERCLASS;
	static final String URL;
	static final String USER;
	static final String PASSWORD;
	
	static {
		// 块编辑 alt+shift +a
		// 变大写 ctrl+shift+x
		// 变小写 ctrl+shift+y
		// 向下复制一行   alt+ctrl+↓
		// 向下添加一个空行 shift + enter
		// 向上添加一个空行 ctrl+shift + enter
		// 删除一行 选中行  ctrl+d
		// 注释或者去掉注释 ctrl+/
		// 向下移动一行 alt + ↓
		
		
		// 获取ResourceBundle ctrl+2 l
		ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
		
		// 获取指定的内容
		DRIVERCLASS = bundle.getString("driverClass");
		URL = bundle.getString("url");
		USER = bundle.getString("user");
		PASSWORD = bundle.getString("password");
	}
	
	static {
		// 注册驱动 ctrl+shift+f格式化代码
		try {
			Class.forName(DRIVERCLASS);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	

	// 获取连接
	public static Connection getConnection() throws SQLException {
		// 获取连接 ctrl+o 整理包
		return  DriverManager.getConnection(URL, USER, PASSWORD);
	}

	/**
	 * 释放资源
	 * 
	 * @param conn
	 *            连接
	 * @param st
	 *            语句执行者
	 * @param rs
	 *            结果集
	 */
	public static void closeResource(Connection conn, Statement st, ResultSet rs) {
		closeResultSet(rs);
		closeStatement(st);
		closeConn(conn);
	}

	/**
	 * 释放连接
	 * 
	 * @param conn
	 *            连接
	 */
	public static void closeConn(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}

	}

	/**
	 * 释放语句执行者
	 * 
	 * @param st
	 *            语句执行者
	 */
	public static void closeStatement(Statement st) {
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			st = null;
		}

	}

	/**
	 * 释放结果集
	 * 
	 * @param rs
	 *            结果集
	 */
	public static void closeResultSet(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}

	}
}

对比两种方法的代码量:

第一种第二种
DriverManager.registerDriver(new Driver());                                          |Connection conn=null;
Connection conn=                                                                     |ResultSet rs=null;
DriverManager.getConnection("jdbc:mysql://localhost:3306/***", "root", "123456");    |PreparedStatement st=null;
String  sql="select * from category";                                                |conn=JdbcUtils.getConnection();
PreparedStatement st=conn.prepareStatement(sql);                                     |String sql="insert into  category values(?,?)";
ResultSet rs=st.executeQuery();                                                      |st=conn.prepareStatement(sql);
rs.close();                                                                          |st.setString(1, "a06");
st.close();                                                                          |int i=st.executeUpdate();
conn.close();                                                                        |JdbcUtils.closeResource(conn, st, rs);

可以看出,代码量并没有减少,方便的只是配置信息的修改方便了。

连接池

通过连接池(DataSource)完成操作:

方式一:自定义连接池

通过上面的类JdbcUtils获取三个conn,然后放入一个list集合中,用的时候removeFirst;
详情:

public class MyDataSource {
	static LinkedList<Connection> pool=new LinkedList<>();
	static{
		//初始化的时候 需要放入3个连接
		for (int i = 0; i < 3; i++) {
			try {
				Connection conn = JdbcUtils.getConnection();
				pool.addLast(conn);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	//从连接池中获取连接
	public static Connection getConnection(){
		//获取连接的时候需要判断list是否为空
		if(pool.isEmpty()){
			//在添加2个连接进去
			for (int i = 0; i < 3; i++) {
				try {
					Connection conn = JdbcUtils.getConnection();
					pool.addLast(conn);
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println("从池中获取一个连接");
		 Connection conn = pool.removeFirst();
		 //将conn进行包装 
		 // ConnectionWarp myConn = new ConnectionWarp(conn);
		 ConnectionWarp myConn = new ConnectionWarp(conn,pool);
		return myConn;
	}
}

归还的时候两种方法

①:在MyDataSource中添加一个方法

public static void addBack(Connection conn){
		//将conn放入到list的最后面即可
		pool.addLast(conn);
		System.out.println("连接已归还");
	}

使用的时候直接ds.addBack。

②:使用装饰者模式自定义一个修饰类ConnectionWarp
这里请看我的另一篇文章:https://blog.csdn.net/yjzq2280044399/article/details/85344277

最后也是在MyDataSource中实例化修饰类ConnectionWarp的对象并将从JdbcUtils那获取的conn传给它

方式二:DBCP连接池

使用步骤:
1.导入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
2.使用api

①硬编码

//创建连接池
		BasicDataSource ds = new BasicDataSource();
		
		//配置信息
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql:///***");
		ds.setUsername("root");
		ds.setPassword("123456");
		Connection conn=ds.getConnection();//这个conn也是被修饰过的
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "a11");
		st.setString(2, "饮料");
		
		int i = st.executeUpdate();
		System.out.println(i);
		JdbcUtils.closeResource(conn, st, null);

②通过配置文件

		Properties prop = new Properties();
		prop.load(new FileInputStream("src/dbcp.properties"));
		//设置
		//prop.setProperty("driverClassName", "com.mysql.jdbc.Driver");
		
		//创建连接池
		DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
		//create  制造的意思
		
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "c012");
		st.setString(2, "饮料1");
		
		int i = st.executeUpdate();
		System.out.println(i);
		JdbcUtils.closeResource(conn, st, null);

方式三:C3P0连接池

使用步骤:
1.导入jar包(c3p0-0.9.1.2.jar)
2.使用api
a.硬编码(不推荐)
new ComboPooledDataSource()

ComboPooledDataSource ds = new ComboPooledDataSource();
		
		//设置基本参数
		ds.setDriverClass("com.mysql.jdbc.Driver");
		ds.setJdbcUrl("jdbc:mysql:///***");
		ds.setUser("root");
		ds.setPassword("123456");
		
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "a12");
		st.setString(2, "神药");
		
		int i = st.executeUpdate();
		System.out.println(i);
		JdbcUtils.closeResource(conn, st, null);

b.配置文件
配置文件的名称:c3p0.properties 或者 c3p0-config.xml
配置文件的路径:src下
编码只需要一句话
new ComboPooledDataSource()//使用默认的配置
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置

ComboPooledDataSource ds =new ComboPooledDataSource("wlf12321");//若查找不到命名的配置 使用默认的配置
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "c124");
		st.setString(2, "解药");
		
		int i = st.executeUpdate();
		System.out.println(i);
		JdbcUtils.closeResource(conn, st, null);

对比DBCP少了两行代码,即不需要自己手动导入配置文件了。

最重要的工具包Dbutils(我觉得用包来理解更贴切)

在这个工具包之前,我们还用了一个自定义工具类,先分析这个

public class DataSourceUtils {
	private static ComboPooledDataSource ds=new ComboPooledDataSource();
	/**
	 * 获取数据源
	 * @return 连接池
	 */
	public static DataSource getDataSource(){
		return ds;
	}
	/**
	 * 获取连接
	 * @return 连接
	 * @throws SQLException
	 */
	public static Connection getConnection() throws SQLException{
		return ds.getConnection();
	}
	/**
	 * 释放资源
	 * 
	 * @param conn
	 *            连接
	 * @param st
	 *            语句执行者
	 * @param rs
	 *            结果集
	 */
	public static void closeResource(Connection conn, Statement st, ResultSet rs) {
		closeResultSet(rs);
		closeStatement(st);
		closeConn(conn);
	}

	/**
	 * 释放连接
	 * 
	 * @param conn
	 *            连接
	 */
	public static void closeConn(Connection conn) {
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
	/**
	 * 释放语句执行者
	 * 
	 * @param st
	 *            语句执行者
	 */
	public static void closeStatement(Statement st) {
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			st = null;
		}
	}
	/**
	 * 释放结果集
	 * 
	 * @param rs
	 *            结果集
	 */
	public static void closeResultSet(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
	}
}

这个工具类下面的和Jdbcutils的内容是一样的,就是归还连接和释放资源的内容。那么重点的就是getDataSource() 这个方法,直接返回的是 ComboPooledDataSource这个类的构造,读取的是c3p0.properties 或者 c3p0-config.xml。

Dbutils是apache组织的一个工具类(包),jdbc的框架,更方便我们使用
使用步骤:
1.导入jar包(commons-dbutils-1.4.jar)
2.创建一个queryrunner类
queryrunner作用:操作sql语句
构造方法:
new QueryRunner(Datasource ds);
3.编写sql
4.执行sql
query(…):执行r操作
update(…):执行cud操作

核心类或接口
QueryRunner:类名
作用:操作sql语句
构造器:
new QueryRunner(Datasource ds);
注意:
底层帮我们创建连接,创建语句执行者 ,释放资源.
常用方法:
query(…):
update(…):

DbUtils:释放资源,控制事务 类
closeQuietly(conn):内部处理了异常
commitAndClose(Connection conn):提交事务并释放连接

所以说这个官方工具类,首先我们来看下这个类的API
在这里插入图片描述
可以看到它内部所含的所有接口和类。这里我们使用的就是QueryRunner类,使用步骤如下:

public void insert() throws SQLException{
		//1.创建queryrunner类
		QueryRunner qr = new QueryRunner(DataSourceUtils.getDataSource());
		//2.编写sql
		String sql="insert into category values(?,?)";
		//3.执行sql
		qr.update(sql, "c201","厨房电器");
	}

还记得连接数据库所需要的操作吗?
注册驱动
获取连接
编写sql
创建预编译的语句执行者
设置参数 没有问号不用设置参数
执行sql
处理结果
释放资源

来看QueryRunner类中的源代码

public class QueryRunner extends AbstractQueryRunner {
    public QueryRunner() {
        super();
    }
    public QueryRunner(boolean pmdKnownBroken) {
        super(pmdKnownBroken);
    }

    public QueryRunner(DataSource ds) {
        super(ds);
    }

    public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
        super(ds, pmdKnownBroken);
    }
    }

英文注释我都删了,这是它的四个构造方法,我们常用的是public QueryRunner(DataSource ds)这个,可以看见是调用了父类构造并将从ComboPooledDataSource那获取的DataSource传了过去,我们再看它的父类AbstractQueryRunner,从名字上可以看出是一个抽象类,API的图上面也有。这个时候的第一步算是完成了,但是第二步我们看不到,只有在用conn的时候才知道,根据上面的update方法,我们找到它的源码

 public int update(String sql, Object param) throws SQLException {
        Connection conn = this.prepareConnection();

        return this.update(conn, true, sql, new Object[]{param});
    }

发现当执行的时候才创建conn
而这个**prepareConnection()**方法,我们发现,是在他的父类中执行的,代码如下

public abstract class AbstractQueryRunner {
 protected Connection prepareConnection() throws SQLException {
        if (this.getDataSource() == null) {
            throw new SQLException("QueryRunner requires a DataSource to be " +
                "invoked in this way, or a Connection should be passed in");
        }
        return this.getDataSource().getConnection();
    }
   }

根据执行步骤往下走,qr.update(sql, "a01","厨房电器");,先调用上面的方法创建conn,再调用下面的方法生成语句执行者等。最后返回结果集。

private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
        if (conn == null) {
            throw new SQLException("Null connection");
        }
        if (sql == null) {
            if (closeConn) {
                close(conn);
            }
            throw new SQLException("Null SQL statement");
        }
        PreparedStatement stmt = null;
        int rows = 0;
        try {
            stmt = this.prepareStatement(conn, sql);//都在抽象方法中生成的。	-
            this.fillStatement(stmt, params);
            rows = stmt.executeUpdate();
        } catch (SQLException e) {
            this.rethrow(e, sql, params);
        } finally {
            close(stmt);
            if (closeConn) {
                close(conn);
            }
        }
        return rows;
    }

总结下,QueryRunner类完成的步骤有:
获取连接
创建预编译的语句执行者
执行sql
返回结果集
释放资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值