当前位置: 首页 > 工具软件 > JdbcUtils > 使用案例 >

JDBC工具类——JdbcUtils

甄云
2023-12-01

一、JDBC的工具类

JdbcUtils工具类:封装加载驱动,获取连接,释放资源
RowMapper工具类:封装处理结果集
JdbcTemplate工具类:封装查询一条多条结果,和增删改冗余,操作减少dao层代码冗余

二、JdbcUtils工具类的组成

1、类加载时加载驱动

将Class.forName(“com.mysql.jdbc.Driver”);代码放到static静态代码块中,因为static静态代码块只有第一次类加载的时候执行一次就不再执行了,只需要加载一次,不需要重复加载(减少浪费资源)。

2、连接池:db.properties

properties配置文件:
作用:封装加载驱动,获取连接,释放资源等常用变量,避免将代码写死,提高代码复用性
创建方法:先在src下创建一个com.conf配置文件包,再new一个file文件命名以properties结尾,将会变的参数以键值对的形式存到properties文件中
使用方法
①.将properties文件读入流中,使用类名.class获取字节码文件,字节码文件.getResourceAsStream(“文件路径”)来获取文件的流,返回一个输入流。
InputStream is = JdbcUtils2.class.getResourceAsStream("/com/conf/db.properties");
②.创建一个全局的静态properties对象,将读过来的properties文件流使用load(is)方法加载到properties对象中
为什么是静态的:因为加载驱动是放在static静态代码块中,只有静态的才能在静态代码块中使用。
③.Properties里面已经以键值对的形式存放db.properties对象了,可以通过properties.getProperty(“键”)来读取值了
④.关闭inputStream输入流

3、ThreadLocal控制事务

1.什么是事物: 逻辑上的一组操作,组成这组操作的逻辑单元要么一起成功,要么一起失败。
例:银行转账,自己账户扣钱,对方账户加钱
2.事物的特性
①.原子性:强调事务的不可分割.(要么都做,要么都不做)
②.一致性:强调的是事务的执行的前后,数据的完整性要保持一致.
③.隔离性:一个事务的执行不应该受到其他事务的干扰.
④.持久性(永久性):事务一旦结束(提交/回滚)数据就持久保持到了数据库.
3.MySql的TCL(事物控制语言)
默认是自动提交
1.set autocommit=false,设置为手动提交
2.rollback,回滚
3.commit,提交
4.jdbc的TCL(事物控制语言)
1.默认事务自动提交策略:一条命令自成一个完整事务
①.可以一条sql就是一个事务
②.也可以将多条sql组合成一个事务,这组sql要么同时执行成功,要么都失败
2.需求:各个逻辑单元要么一起成功,要么一起失败(比如转账)
3.手动控制事务的API
①.conn.setAutoCommit (false);将jdbc的事务提交改为手动提交
②.conn.commit();提交
③.conn.rollback();回滚
注意:连接提交策略一经设置,永久改变

ThreadLocal使用
1.使用事务控制完成业务功能
2.目的:写项目要分层,为了不造成资源浪费(类与类相互调用,都需要连接数据库,但是它们属于同一个线程,在一个线程的各段代码中,只使用一个连接conn就可以了)
3.功能:为同一个线程保存同一个连接值,为不同线程保存不同的连接值
4.创建对象: ThreadLocal<t> tdl = new ThreadLocal<t >;泛型里面可以放任何东西,字符串,连接
5.方法:
①.set(T t) ;//将t对象添加至当前线程
②.get() ;//获得当前线程中的对象
③.remove () ;//移除当前线程中的对象

4、dbcp连接池提高资源利用率

1.创建
创建多个dbcp.properties文件,存储多个不同的连接池
2.作用
使用连接池技术,避免重复创建和销毁连接资源,减少系统开销。
3.连接池的工作原理:
①使用连接池前:用jdbc连接数据库时,先getConnection获取连接,用完后要release释放连接,再次连接重复前两步,对程序性能影响很大。
②使用连接池后:在程序开始之前,先创建几个连接,将连接放入到连接池中,连接池中缓存了一定量的Connection对象,当用户需要使用连接时,从连接池中获取,使用完毕之后将连接还回连接池。
4.连接池的种类:
DBCP连接池,C3P0,Tmcat内置连接池等
5.使用步骤:
1.单独使用DBCP需要导入三个dbcp相关jar包,Add to Bulid Path依赖到项目中
①.commons-dbcp-1.4.jar
②.commons-collections-3.2.1.jar
③.commons-pool-1.5.4jar
2.导入dbcp的配置文件dbcp.properties,也可以自己编写(相当于之前自己编写的db.properties)
driverclassName=com.mysql.jbcp.Driver
url=jdbc:mysql://localhost:3306/mybase1?useUnicode=true&characterEncoding=utf-8
user=root
password=root123
initialSize=10 //初始连接数量
maxActive=50 //最大活跃连接数量
maxIdle=20 //最大空间连接数
minIdle=5 //最小空间连接数
maxWait=60000 //最大等待时间ms(毫秒),从连接池获取连接,如果连接池空了,等待的最大时间
connectionProperties=useUnicode=true;characterEncoding=utf-8
defaultAutoCommit=true
defaultTransactionIsolation=READ_COMMITTED,默认事务隔离:读已提交?
3.API开发,从连接池获得连接
声明连接池:static DataSource pool=null;
创建连接池:pool=BasicDataSourceFactory.createDataSource(pro);
从连接池里面来获取连接:conn=pool.getConnection();(不需要使用DriverManager.getConnection创建连接了)

三、JDBC工具类的实例演变

1.第一代JdbcUtils工具类
①.工具类
package com.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JdbcUtils {
	//加载驱动
	static{
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//获取连接(自己决定,封装到方法中)
	//static静态,直接使用类名.方法名更方便
	public static Connection getconnection() throws Exception{
		String url="jdbc:mysql://localhost:3306/mybase?userUnicode=true&characterEncoding=utf8";
		String user="root";
		String password="root123";
		Connection conn=DriverManager.getConnection(url,user,password);
		return conn;
	}
	//释放资源
	public static void release(ResultSet rs,PreparedStatement pstm,Connection conn) throws Exception{
		if(rs!=null){
			rs.close();
		}
		if(pstm!=null){
			pstm.close();
		}
		if(conn!=null){
			conn.close();
		}
	}
}
②.测试类
package com.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import com.util.JdbcUtils;

public class testJdbcUtils1 {
	public static void main(String[] args) throws Exception {
		Connection conn = JdbcUtils.getconnection();
		String sql="update emp set job=? where ename=?";
		PreparedStatement pstm = conn.prepareStatement(sql);
		pstm.setString(1, "clerk");
		pstm.setString(2, "夏雪儿");
		pstm.executeUpdate();
		JdbcUtils.release( null,pstm, conn);
		System.out.println(conn);
	}
}
2.第二代JdbcUtils工具类(添加db配置文件)
①.db.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/mybase1
username=root
password=root123
②.工具类
package com.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtils2 {
	static InputStream is =null;
	static Properties properties=new Properties();
	static{
		try {
			is= JdbcUtils2.class.getResourceAsStream("/com/conf/db.properties");
			try {
				//将流加载到文件中
				properties.load(is);
			} catch (Exception e) {
				e.printStackTrace();
			}
			Class.forName(properties.getProperty("driverClassName"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally{
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	//获取连接(自己决定,封装到方法中)
	//static静态,直接使用类名.方法名更方便
	public static Connection getconnection() throws Exception{
		String url=properties.getProperty("url");
		String user=properties.getProperty("username");
		String password=properties.getProperty("password");
		Connection conn=DriverManager.getConnection(url,user,password);
		return conn;
	}
	//释放资源
	public static void release(ResultSet rs,PreparedStatement pstm,Connection conn) throws Exception{
		if(rs!=null){
			rs.close();
		}
		if(pstm!=null){
			pstm.close();
		}
		if(conn!=null){
			conn.close();
		}
	}
}
③.测试类:普通测试类(单条sql为一个事务)
package com.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import com.util.JdbcUtils2;

public class testJdbcUtils2 {
	public static void main(String[] args) throws Exception {
		Connection conn = JdbcUtils2.getconnection();
		String sql="update emp set job=? where ename=?";
		PreparedStatement pstm = conn.prepareStatement(sql);
		pstm.setString(1, "manager");
		pstm.setString(2, "夏雪儿");
		pstm.executeUpdate();
		JdbcUtils2.release( null,pstm, conn);
		System.out.println(conn);
	}
}
set autocommit=false;
insert into dept(deptno,dname,loc) values(50,'后勤部','郑州');
rollback;
commit;
④.测试类:多条sql作为一个事务提交
package com.jdbc;

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

import com.util.JdbcUtils2;

public class TestTrascation {
	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement pstm1=null;
		PreparedStatement pstm2=null;
		try {
			//加载驱动获取连接
			conn = JdbcUtils2.getconnection();
			//设置jdbc事务手动提交
			conn.setAutoCommit(false);
			//事务1:降职,事务2:降薪
			//分析:若白展堂从经理变成了小职员,他的奖金也要减少
			String sql1="update emp set job='clerk' where ename='白展堂'";
			pstm1=conn.prepareStatement(sql1);
			pstm1.executeUpdate();
			//comm=4000不符合comm的数据类型,不大于3位数,所以异常回滚
			//String sql2="update emp set comm=4000 where ename='白展堂'";
			String sql2="update emp set comm=100 where ename='白展堂'";
			pstm2=conn.prepareStatement(sql2);
			pstm2.executeUpdate();
			//提交事务:如果事务1和2都成功执行了就提交事务
			//否则捕捉异常,在catch里面执行事务回滚
			conn.commit();
		} catch (Exception e) {
			try {
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}finally{
			try {
				JdbcUtils2.release(null, pstm2, conn);
				JdbcUtils2.release(null, pstm1, conn);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
3.第三代JdbcUtils工具类(添加ThreadLocal线程)
①.dbcp.properties(同上)
②.工具类

主要改变:获取连接,同一个线程获得相同连接

package com.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtils3 {
	static InputStream is =null;
	static Properties properties=new Properties();
	static ThreadLocal<Connection> tdl=new ThreadLocal<Connection>();
	static{
		try {
			is= JdbcUtils2.class.getResourceAsStream("/com/conf/db.properties");
			try {
				//将流加载到文件中
				properties.load(is);
			} catch (Exception e) {
				e.printStackTrace();
			}
			Class.forName(properties.getProperty("driverClassName"));
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally{
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	//获取连接(自己决定,封装到方法中)
	//static静态,直接使用类名.方法名更方便
	public static Connection getconnection() throws Exception{
		//现获取当前线程中所拥有的连接
		Connection conn = tdl.get();
		//如果没有就创建一个
		if(conn==null){
			String url=properties.getProperty("url");
			String user=properties.getProperty("username");
			String password=properties.getProperty("password");
			conn=DriverManager.getConnection(url,user,password);
			//将创建的线程写入到当前线程中
			tdl.set(conn);
		}
		//如果有就直接返回当前线程拥有的连接,让同一个线程能使用同一个连接
		return conn;
	}
	//释放资源
	public static void release(ResultSet rs,PreparedStatement pstm,Connection conn) throws Exception{
		if(rs!=null){
			rs.close();
		}
		if(pstm!=null){
			pstm.close();
		}
		if(conn!=null){
			//关闭连接
			conn.close();
			//同时将连接从当前线程中移除
			tdl.remove();
		}
	}
}
③.测试类
package com.jdbc;

import java.sql.Connection;

import com.util.JdbcUtils3;

public class TestThreadLocal2 {
	public static void main(String[] args) throws Exception {
		Connection conn1 = JdbcUtils3.getconnection();
		Connection conn2 = JdbcUtils3.getconnection();
		System.out.println("conn1:"+conn1);
		System.out.println("conn2:"+conn2);
		Thread t1=new Thread(){
			public void run(){
				try {
					Connection conn3 = JdbcUtils3.getconnection();
					System.out.println("conn3:"+conn3);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
		t1.start();
	}
}
④.测试结果
conn1:com.mysql.jdbc.JDBC4Connection@64cf5b3
conn2:com.mysql.jdbc.JDBC4Connection@64cf5b3
conn3:com.mysql.jdbc.JDBC4Connection@6c977c1f
4.第四代JdbcUtils工具类(添加dbcp连接池)
①.dbcp.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/mybase1
username=root
password=root123
②.工具类
package com.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class JDBCUtils {
	
	static InputStream is =null;
	//配置文件的操作类Properties类(java.util.Properties)
	static Properties properties=new Properties();
	static DataSource pool=null;
	//会话:登录系统后,可以进行多个操作,但只登录了一次
	//事务:每个操作,如第一次转账和第二次转账是两个事务
	//线程:一个人操作和多个人操作的问题
	//一个会话可以创建多个事务,一个事务只能由一个会话产生
	//一个事务可以产生多个线程,一个线程同一时间内只能由一个事务执行
	//创建事务,类之间互调属于同一个线程,为同一个线程保存同一个连接值conn,为不同线程保存不同的连接值
	//泛型里面可以放任意对象,用事务保存什么就放什么类型
	static ThreadLocal<Connection> tdl=new ThreadLocal<Connection>();
	
	//创建连接池,并将dbcp.properties以键值对的形式读入
	static{
		//这个try是为createDataSource捕获的异常
		try {
			//JDBCUtils.class为JDBCUtils的字节码文件
			//当前class的包路径为基准,获取dbcp.properties文件的流
			is = JDBCUtils.class.getResourceAsStream("/com/conf/dbcp.properties");
			
			//从输入流中以键值对的形式读取dbcp.propertie,要捕捉异常
			try {
				properties.load(is);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			//使用BasicDataSourceFactory数据连接池工厂类创建一个连接池
			//并将从dbcp.propertie读取出的键值对放入连接池中
			pool = BasicDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				//关闭流文件,捕获异常
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	//获取连接
	public static Connection getConnection() throws Exception{
		//获取当前线程中拥有的连接,如果没有就创建一个
		Connection conn = tdl.get();
		if(conn==null){
			//从字节流中取值
			//String url = properties.getProperty("url");
			//String username = properties.getProperty("username");
			//String password = properties.getProperty("password");
			//获取连接,抛出Exception异常
			//conn=DriverManager.getConnection(url, username, password);
			
			//从连接池中取值,并将连接加入当前线程中
			conn=pool.getConnection();
			tdl.set(conn);
		}
		return conn;
	}
	
	//释放资源
	//导包导的都是有关sql的包
	public static void release(ResultSet rs,PreparedStatement pstm,Connection conn) throws Exception{
		if(rs!=null){
			rs.close();
		}
		if(pstm!=null){
			pstm.close();
		}
		//关闭连接,同时将连接从当前线程中移除
		if(conn!=null){
			conn.close();
			tdl.remove();
		}
	}
	
}

③.测试类
package com.jdbc;

import java.sql.Connection;

import com.util.JDBCUtils;

public class testJDBCUtils {
	public static void main(String[] args) throws Exception {
		//由于有static关键字修饰,可以直接使用类名.方法名调用getConnection()方法获取连接
		Connection conn1 = JDBCUtils.getConnection();
		Connection conn2 = JDBCUtils.getConnection();
		//同一线程,获得的是同一个连接对象
		System.out.println("conn1:"+conn1);
		System.out.println("conn2:"+conn2);
		
		//那在创建一个新的线程并将其开启,事务为不同线程保存的就不是同一个连接对象了
		Thread t1=new Thread(){
			public void run(){
				try {
					Connection conn3 = JDBCUtils.getConnection();
					System.out.println("conn3:"+conn3);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		};
		t1.start();
	}
}

④.测试结果
conn1:jdbc:mysql://localhost:3306/mybase, UserName=root@localhost, MySQL Connector Java
conn2:jdbc:mysql://localhost:3306/mybase, UserName=root@localhost, MySQL Connector Java
conn3:jdbc:mysql://localhost:3306/mybase, UserName=root@localhost, MySQL Connector Java

 类似资料: