侧边栏

JDBC

概述

JDBC,Java Database Connectivity(Java数据库连接),Sun公司退出的Java访问数据库的标准规范(接口),JDBC是一种用于执行SQL语句的Java API。

Sun公司提供访问数据库的规范叫做JDBC,而生产厂商提供规范的实现类叫做驱动。

JUnit

image-20220826115046614

image-20220826115304368

JUnit叫做单元测试,记得得先引入jar包,才能使用@Test注解,选中哪个方法就运行哪个方法,如果都不选,那么就运行所有具备@Test注解的方法

注意:单元测试的方法的格式必须是
@Test
public void 方法名(){

}

体验JDBC

image-20220826114353501

image-20220826114529129

package com.darksnow;

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

import org.junit.Test;

public class JDBCTest {
	
	@Test
	public void testQueryAll() {
		
		ResultSet rs = null;
		Statement stmt = null;
		Connection conn = null;
		
		try {
			/*
			 * JDBC规范了定义驱动接口 ---> java.sql.Driver
			 * MySQL驱动包提供了上述规范的实现类  ---> com.mysql.jdbc.Driver
			 * 
			 * 在java.sql.DriverManager中提供了注册驱动的方法:
			 * 		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
			 * 但是以上的方式不推荐使用,原因:
			 * 		1.硬编码,因为直接new的是对象,后期不便于扩展和维护
			 * 		2.驱动被注册了两次
			 * 
			 * 综上所述,我们通常使用Class.forName的方式将驱动类加载到内存
			 */
			//1.注册驱动  你不明白原理,你就把它当成记死的
			Class.forName("com.mysql.jdbc.Driver");
			
			/*
			 *  public static Connection getConnection(String url, String user, String password) 
			 *  
			 *  url参数:
			 *  	连接mysql的地址,由三部分组成,每个部分用冒号(:)隔开
			 *  	第一个部分,jdbc,这个是固定的
			 *  	第二个部分,属于什么数据库,因为你用的是mysql的驱动,所以是连接mysql的,因此这里固定写mysql
			 *  	第三个部分,一般都是“ //ip地址:端口号/要访问的数据库名 ” 这样的格式
			 *  	注意:如果你要连接的数据库是本地数据库(自己电脑上),且端口号是默认的3306,那么可以简写为
			 *  			jdbc:mysql:///darksnow
			 *  	其实就是省略掉了ip地址和端口号
			 *  
			 *  user参数:数据库的账号
			 *
			 *	password参数:数据库的密码
			 */
			//2.获得mysql连接
			String url = "jdbc:mysql://127.0.0.1:3306/darksnow";
			String user = "root";
			String password = "root";
			conn = DriverManager.getConnection(url,user,password);
			
			//3.通过连接对象Connection获得sql语句执行对象Statement
			stmt = conn.createStatement();
			
			//4.通过Statement对象执行sql语句  ResultSet的意思是"结果集"  executeQuery的意思是"执行查询"
			String sql = "select * from user";
			rs = stmt.executeQuery(sql);
			
			//5.处理结果集
			while(rs.next()) {
				/*
				 *  boolean next() 类似于迭代器中的方法,判断是否还有下一条记录
				 *  
				 *  注意:字段的索引从1开始计数
				 *  String getString(int columnIndex):通过表中字段的索引来获取对应数据
				 *  String getString(String columnLabel):通过表中字段的名称来获取对应数据
				 *  int getInt(int columnIndex):作用和getString一样,只不过字段的类型int,所以用getInt,如果字段的类型是char或者varchar用getString
				 *  int getInt(String columnLabel):通过表中字段的名称来获取对应数据
				 *  
				 */
				//System.out.println("id:" + rs.getInt(1) + " --- name:" + rs.getString(2) + " --- age:" + rs.getInt(3));
				System.out.println("id:" + rs.getInt("id") + " --- name:" + rs.getString("name") + " --- age:" + rs.getInt("age"));
			}
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}finally {
			//6.释放资源 不管程序是否正常执行,最后我们都要释放资源,但是有资源我们就释放,没资源就不管
			if(rs != null) {
				try {
					rs.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if(stmt != null) {
				try {
					stmt.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}	
}

模拟登陆

package com.darksnow;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

import org.junit.Test;

/**
 * 模拟登陆
 * @author Administrator
 */
public class JDBCTestLogin {
	
	@Test
	public void testLogin() {
		Scanner sc = new Scanner(System.in);
		
		System.out.print("请输入账号:");
		String username = sc.nextLine();
		
		System.out.print("请输入密码:");
		String password = sc.nextLine();
		
		//调用登陆方法
		//如果账号密码都是darksnow' or '1'='1也能够登录成功,这叫做SQL注入漏洞
		//loginOne(username,password);
		
		//调用登陆方法,已解决SQL主图
		loginTwo(username,password);
		
		sc.close();
	}
	
	
	/**
	 * 登陆的业务逻辑  此代码存在SQL注入的问题
	 * @param username 账号
	 * @param password 密码
	 */
	public static void loginOne(String username,String password){
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try {
			//1.注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql:///darksnow","root","root");
			//3.创建执行SQL语句的对象
			stmt = conn.createStatement();
			//4.执行SQL语句
			String sql = "select * from t_user where username = '" + username + "' and password = '" + password + "'";
			rs = stmt.executeQuery(sql);
			//5.处理结果集  next刚开始是从0开始的,如果你的username和password正确,那么就会得到一条记录,next的结果为true,因为从0到1了
			if(rs.next()) {  
				System.out.println("恭喜您," + rs.getString("name") + "登录成功!");
			}else {
				System.out.println("账号或密码错误!");
			}
			System.out.println(sql);
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		} finally {
			//6.释放资源
			if(rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(stmt != null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	
	/**
	 * 登陆的业务逻辑  此代码可以解决SQL注入的问题
	 * @param username 账号
	 * @param password 密码
	 */
	public static void loginTwo(String username,String password){
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			//1.注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql:///darksnow","root","root");
			//3.创建执行SQL语句的对象,下面SQL语句中的问号就是占位符
			String sql = "select * from t_user where username = ? and password = ?";
			pstmt = conn.prepareStatement(sql);  //预编译处理对象
			//4.设置参数,给占位符设置值
			//参数一是占位符所在的位置(也就是问号所在的位置,从1开始计数),参数二是给占位符指定值
			pstmt.setString(1, username);
			pstmt.setString(2, password);
			//5.执行查询操作
			rs = pstmt.executeQuery();
			//6.处理结果集  next刚开始是从0开始的,如果你的username和password正确,那么就会得到一条记录,next的结果为true,因为从0到1了
			if(rs.next()) {  
				System.out.println("恭喜您," + rs.getString("name") + "登录成功!");
			}else {
				System.out.println("账号或密码错误!");
			}
			System.out.println(sql);
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		} finally {
			//7.释放资源
			if(rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

封装工具类

1.JDBCUtilVersion1

package com.darksnow.utils;

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

/**
 * 我们发现每次都会去注册驱动,获取连接,释放资源 感觉比较重复
 * 所以我们将这些操作封装成了一个工具类,以便于提高代码的复用性
 */
public class JDBCUtilVersion1 {

	private JDBCUtilVersion1() {}

	/**
	 * 获取连接的方法
	 * @return 连接对象
	 */
	public static Connection getConnection() {
		Connection conn = null;
		try {
			//注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//获取连接
			conn = DriverManager.getConnection("jdbc:mysql:///darksnow","root","root");
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 释放资源的方法
	 * @param conn  连接对象
	 * @param pstmt 预编译处理对象
	 * @param rs	结果集对象
	 */
	public static void close(Connection conn, Statement pstmt, ResultSet rs) {
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

2.JDBCUtilVersion2

package com.darksnow.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * JDBCUtilVersion1 虽然提高了代码的复用性
 * 但是,里面的链接信息,驱动信息都是写死的(存在硬编码问题)
 * 所以在JDBCUtilVersion2 我们主要解决的就是硬编码问题
 * 
 * 思路:通过配置文件来获取配置,不写死在代码中
 */
public class JDBCUtilVersion2 {
	
	private JDBCUtilVersion2() {}
	
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	
	//静态代码块加载配置文件信息,随着类的加载,而只加载一次
	static {
		//读取db.properties文件
		ResourceBundle bundle = ResourceBundle.getBundle("db");
		driver = bundle.getString("driver");
		url = bundle.getString("url");
		username = bundle.getString("username");
		password = bundle.getString("password");
	}
	
	/**
	 * 获取连接的方法
	 * @return 连接对象
	 */
	public static Connection getConnection() {
		Connection conn = null;
		try {
			//注册驱动
			Class.forName(driver);
			//获取连接
			conn = DriverManager.getConnection(url,username,password);
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 释放资源的方法
	 * @param conn  连接对象
	 * @param pstmt 预编译处理对象
	 * @param rs	结果集对象
	 */
	public static void close(Connection conn, Statement pstmt, ResultSet rs) {
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

3.JDBCUtilVersion3

package com.darksnow.utils;

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

/**
 * 在JDBCUtilVersion2 这个工具类中properties配置文件是通过ResourceBundle来加载
 * 现在我们使用另外一种方式来加载配置文件。
 * 
 * JDBCUtilVersion3和JDBCUtilVersion2只是加载配置文件的方式不同,效果是一样的。
 */
public class JDBCUtilVersion3 {

	private JDBCUtilVersion3() {
	}

	private static String driver;
	private static String url;
	private static String username;
	private static String password;

	// 静态代码块加载配置文件信息,随着类的加载,而只加载一次
	static {
		try {
			//通过当前类获取类加载器
			ClassLoader classLoader = JDBCUtilVersion3.class.getClassLoader();
			//通过类加载器获得一个输入流 ---> 用来读
			InputStream is = classLoader.getResourceAsStream("db.properties");
			//创建一个Properties对象
			Properties props = new Properties();
			//加载输入流
			props.load(is);
			//获取相关的配置信息
			driver = props.getProperty("driver");
			url = props.getProperty("url");
			username = props.getProperty("username");
			password = props.getProperty("password");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取连接的方法
	 * @return 连接对象
	 */
	public static Connection getConnection() {
		Connection conn = null;
		try {
			//注册驱动
			Class.forName(driver);
			//获取连接
			conn = DriverManager.getConnection(url,username,password);
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}
		return conn;
	}
	
	/**
	 * 释放资源的方法
	 * @param conn  连接对象
	 * @param pstmt 预编译处理对象
	 * @param rs	结果集对象
	 */
	public static void close(Connection conn, Statement pstmt, ResultSet rs) {
		if(rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(pstmt != null) {
			try {
				pstmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

db.properties

这个是配置文件,这个配置文件需要放到src下面

driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///darksnow
username=root
password=root

结合工具类做CRUD操作

package com.darksnow;

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

import org.junit.Test;

import com.darksnow.utils.JDBCUtilVersion3;

/**
 * 结合前面写的工具类,作增删改查的操作
 * CRUD(增查改删):增(Create),检索(Retrieve),更新(Update),删除(Delete)
 * 
 * 增删改用的都是executeUpdate方法
 * 查询用的是executeQuery方法
 */
public class JDBCTestCrud {

	/**
	 * 根据id更新用户名称
	 */
	@Test
	public void testUpdateById() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		
		try {
			//获取连接
			conn = JDBCUtilVersion3.getConnection();
			//编写SQL语句
			String sql = "update t_user set username=? where id=?";
			//获取执行sql语句的对象
			pstmt = conn.prepareStatement(sql);
			//设置参数
			pstmt.setString(1, "admin");
			pstmt.setInt(2, 2);
			//执行更新操作
			int row = pstmt.executeUpdate(); //返回值表示更新了多少行
			if(row > 0) {
				System.out.println("更新成功~");
			}else {
				System.out.println("更新失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//释放资源
			JDBCUtilVersion3.close(conn, pstmt, null);
		}
	}
	
	/**
	 * 根据id删除信息
	 */
	@Test
	public void testDeleteById() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		
		try {
			//获取连接
			conn = JDBCUtilVersion3.getConnection();
			//编写SQL语句
			String sql = "delete from t_user where id=?";
			//获取执行sql语句的对象
			pstmt = conn.prepareStatement(sql);
			//设置参数
			pstmt.setInt(1, 2);
			//执行删除操作
			int row = pstmt.executeUpdate(); //返回值表示删除了多少行
			if(row > 0) {
				System.out.println("删除成功~");
			}else {
				System.out.println("删除失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//释放资源
			JDBCUtilVersion3.close(conn, pstmt, null);
		}
	}
	
	/**
	 * 添加用户信息
	 */
	@Test
	public void testInsert() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		
		try {
			//获取连接
			conn = JDBCUtilVersion3.getConnection();
			//编写SQL语句
			String sql = "insert into t_user values(null,?,?,?)";
			//获取执行sql语句的对象
			pstmt = conn.prepareStatement(sql);
			//设置参数
			pstmt.setString(1, "maliu");
			pstmt.setString(2, "12580");
			pstmt.setString(3, "马六");
			//执行新增操作
			int row = pstmt.executeUpdate(); //返回值表示新增了多少行
			if(row > 0) {
				System.out.println("新增成功~");
			}else {
				System.out.println("新增失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//释放资源
			JDBCUtilVersion3.close(conn, pstmt, null);
		}
	}
	
	/**
	 * 根据id查询用户信息
	 */
	@Test 
	public void testFindById() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;

		try {
			//获取连接
			conn = JDBCUtilVersion3.getConnection();
			//编写sql语句  
			String sql = "select * from t_user where id=?";
			//获取执行SQL语句的对象
			pstmt = conn.prepareStatement(sql);
			//设置参数
			pstmt.setInt(1, 3);
			//执行查询操作
			rs = pstmt.executeQuery();
			//处理结果集
			while(rs.next()) {
				System.out.println(rs.getString("username") + " --- " + rs.getString("password") + " --- " + rs.getString("name"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//关闭资源
			JDBCUtilVersion3.close(conn, pstmt, rs);
		}
	}
}

连接池

1.存在的问题

原来我们写的程序,获取连接和释放资源是非常消耗系统资源的,所以我们可以使用连接池技术来解决这个问题。

image-20220829112942716

2.实现自定义连接池

package com.darksnow.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class JDBCPoolUtil {
	
	private JDBCPoolUtil() {
		
	}
	
	//1.创建一个容器(连接池),用于存放Connection
	private static LinkedList<Connection> pool = new LinkedList<>();
	
	//2.初始化连接池的连接  让连接池一开始就具备一些连接
	static {
		try {
			//注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			
			for(int i=1; i<=5; i++) {
				//生成连接
				Connection conn = DriverManager.getConnection("jdbc:mysql:///darksnow","root","root");
				//将生成的连接放入到连接池中
				pool.add(conn);
			}
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 从连接池中获取连接
	 * @return 连接对象
	 */
	public static Connection getConnection() throws InterruptedException {
		
		//如果连接池有连接的情况下
		//pool.isEmpty() == true,连接池是空的;pool.isEmpty() == false,连接池不是空的。
		if(!pool.isEmpty()) {
			//连接池不是空的才能够进到if内部
			//使用get方法时不合理的,因为get方法虽然能够拿到连接,但是拿到的连接依旧还在连接池
			//removeFirst()  删除集合中的第一个元素,并且将这个第一个元素给返回
			return pool.removeFirst();
		}
		
		//如果连接池没有连接的情况下 等5秒再获取
		Thread.sleep(5000);
		
		return getConnection();
	}
	
	/**
	 * 将使用完的连接放回连接池
	 * @param conn
	 */
	public static void backConn(Connection conn) {
		if(conn != null) {
			pool.add(conn); 
		}
	}
}
package com.darksnow;

import java.sql.Connection;

import com.darksnow.utils.JDBCPoolUtil;

class ThreadTest extends Thread{
	@Override
	public void run() {
		try {
			Connection conn = JDBCPoolUtil.getConnection();
			System.out.println(conn + "---" + Thread.currentThread().getName());
			JDBCPoolUtil.backConn(conn);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class TestPool {
	public static void main(String[] args) {
		for(int x=1; x<=15; x++) {
			new ThreadTest().start();
		}
	}
}

C3P0连接池

image-20220830091305067

1.配置文件

此配置文件名字必须叫做c3p0-config.xml,别乱改

此配置文件目前放在src下

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
		<property name="user">root</property>
		<property name="password">root</property>
		<!-- 指定最开始连接池的连接数量 -->
		<property name="initialPoolSize">5</property>
		<!-- 指定最最大连接数 -->
		<property name="maxPoolSize">20</property>
	</default-config>

	<named-config name="darksnow">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
		<property name="user">root</property>
		<property name="password">root</property>
	</named-config>

</c3p0-config>

2.封装工具类

package com.darksnow.utils;

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

/**
 * C3P0的相关工具类 
 * 
 * 你可以把数据源看成是连接池
 */
public class C3P0Util {
	//私有构造函数,不让其创建对象
	private C3P0Util() {
		
	}
	
	
	/*
	 * 这里ComboPooledDataSource的构造函数传入了一个"darksnow"的字符串参数,那么就会去使用c3p0-config.xml配置文件中的
	 * <named-config name="darksnow">下面的配置
	 * 
	 * 如果ComboPooledDataSource用的是无参构造,那么就会去使用c3p0-config.xml配置文件中的
	 * <default-config>下面的配置
	 */
	// private static ComboPooledDataSource dataSource = new ComboPooledDataSource("darksnow");
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();   //设置数据源
	
	/**
	 * javax.sql.DataSource 是Java提供的原生接口,各大厂商实现连接池需要实现这个接口
	 * 获取数据源的方法
	 */
	public static DataSource getDataSource() {
		return dataSource;
	}
	
	/**
	 * 获取连接的方法
	 */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
}

3.编写测试类

package com.darksnow.test;

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

import org.junit.Test;

import com.darksnow.utils.C3P0Util;

public class C3P0Test {

	@Test
	public void testC3P0() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		try {
			//从连接池里获取连接
			conn = C3P0Util.getConnection();
			//编写SQL
			String sql = "insert into t_user values(null,?,?,?)";
			//获取预编译处理对象
			pstmt = conn.prepareStatement(sql);
			//给占位符设置值
			pstmt.setString(1, "laoba");
			pstmt.setString(2, "laoba666");
			pstmt.setString(3, "老八");
			//执行SQL语句
			int rows = pstmt.executeUpdate();
			if(rows > 0) {
				System.out.println("数据添加成功~");
			}else {
				System.out.println("数据添加失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			if(pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
}

DBCP连接池

image-20220830103304275

1.配置文件

这个配置文件的名字可以任意命名,但是要以properties结尾

properties文件不支持中文

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///darksnow
username=root
password=root

2.封装工具类

package com.darksnow.utils;

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

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

public class DBCPUtil {

	private DBCPUtil() {
		
	}
	
	private static DataSource dataSource = null;
	
	static {
		try {
			//将db.properties配置加载到文件输入流中
			InputStream is = DBCPUtil.class.getClassLoader().getResourceAsStream("db.properties");
			
			//加载文件输入流来获取配置信息
			Properties properties = new Properties();
			properties.load(is);
			
			//得到数据源  BasicDataSourceFactory ---> 数据源工厂
			dataSource = BasicDataSourceFactory.createDataSource(properties);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取数据源的方法
	 */
	public static DataSource getDataSource() {
		return dataSource;
	}
	
	/**
	 * 获取连接的方法
	 */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException();
		}
	}
}

3.编写测试类

package com.darksnow.test;

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

import org.junit.Test;

import com.darksnow.utils.DBCPUtil;

public class DBCPTest {
	@Test
	public void testDBCP() {
		Connection conn = null;
		PreparedStatement pstmt = null;
		try {
			//获取连接
			conn = DBCPUtil.getConnection();
			//编写SQL语句
			String sql = "update t_user set name=? where id=?";
			//获取预编译处理对象
			pstmt = conn.prepareStatement(sql);
			//给占位符设置值
			pstmt.setString(1, "龙霸天");
			pstmt.setInt(2, 4);
			//执行SQL语句
			int rows = pstmt.executeUpdate();
			if(rows > 0) {
				System.out.println("老八下线,龙霸天上线~");
			}else {
				System.out.println("龙霸天谋朝串位失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally {
			//释放资源
			if(pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}		
	}
}

DBUtils工具

1.概述

作用:简化书写,提升开发效率,顾名思义它是针对JDBC相关开发的。

三大核心:

QueryRunner:该类提供了对SQL进行操作的一些API

ResultSetHandler:它是一个接口,跟结果处理有关,提供了好几种结果处理的方式

DBUtils还提供了关闭资源与事物处理的方法

用于做增删改的API

image-20220830105745406

2.利用DBUtils做增删改

package com.darksnow.test;

import java.sql.SQLException;

import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;

import com.darksnow.utils.C3P0Util;

public class TestDBUtilsOne {
	/**
	 * 添加用户
	 */
	@Test
	public void testAddUser() {
		try {
			//创建QueryRunner对象
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			//编写SQL
			String sql = "insert into t_user values(null,?,?,?)";
			//给占位符设置值
			Object[] params = {"fastjson","tomcat","程序猿"};
			//执行SQL语句
			int rows = qr.update(sql,params);
			if(rows > 0) {
				System.out.println("添加成功~");
			}else {
				System.out.println("添加失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 修改用户的真实名称(name字段)
	 */
	@Test
	public void testUpdateByName() {
		try {
			//创建QueryRunner对象
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			//编写SQL
			String sql = "update t_user set name=? where id=?";
			//给占位符设置值
			Object[] params = {"随便", 5};
			//执行SQL语句
			int rows = qr.update(sql,params);
			if(rows > 0) {
				System.out.println("修改成功~");
			}else {
				System.out.println("修改失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 删除用户
	 */
	@Test
	public void testDeleteById() {
		try {
			//创建QueryRunner对象
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			//编写SQL
			String sql = "delete from t_user where id=?";
			//执行SQL语句
			int rows = qr.update(sql, 5);
			if(rows > 0) {
				System.out.println("删除成功~");
			}else {
				System.out.println("删除失败~");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

3.利用DBUtils做查询

Ctrl + T 可以查看一个接口的实现类

image-20220830113029252

(1)KeyedHandler:可以将结果集中的每一条数据都封装到一个Map<String,Object>集合中,再将这个map作为另外一个map的value,而另一个map的key是指定的字段名(Map<String,Map<String,Object>>)

(2)ArrayListHandler:将结果集的每一条数据都封装到一个Object数组中,再将这些数组封装到List集合中(List<Object[]>)

(3)ColumnListHandler:将结果集中指定的列的字段值封装到一个List集合中

(4)MapListHandler:将结果集中的每一条数据都封装到一个Map<String,Object>集合中,再将Map集合封装到List集合中(List<Map<String,Object>>),key为字段名,value为字段的值

(5)ArrayHandler:将结果集中的第一条数据封装到一个Object数组中,数组中的每一个元素是这条数据的每个字段的值

(6)BeanHandler(重点):将结果集中的每一条数据封装到一个指定的JavaBean中

(7)BeanListHandler(重点):将结果集中的每一条数据封装到一个指定的JavaBean中,再把JavaBean封装到List集合中

(8)MapHandler:将结果集中的每一条数据都封装到一个Map<String,Object>集合中,key为字段名,value为字段的值

(9)ScalarHandler(重点):它一般用于单个数据的处理,比如:select count(*) from user;

用于做查询的API

image-20220830115750878

JavaBean实体

package com.darksnow.bean;

/**
 * 这就是JavaBean  对象实体
 * 此对象的成员变量名和数据库的字段名是一一对应的,数据类型也是对应的
 * 
 * 一个实体对象基本上对应一条数据
 */
public class User {
	private int id;
	private String username;
	private String password;
	private String name;
	
	public User() {
		
	}
	
	public User(int id, String username, String password, String name) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + ", name=" + name + "]";
	}
}

查询操作

package com.darksnow.test;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;

import com.darksnow.bean.User;
import com.darksnow.utils.C3P0Util;

/**
 * 利用DBUtils做查询
 */
public class TestDBUtilsTwo {

	/**
	 * 查询所有用户
	 */
	@Test
	public void testFindAll() {
		try {
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			
			String sql = "select * from t_user";
			
			//BeanListHandler的泛型为指定的JavaBean的类型,参数为指定的JavaBean的字节码文件对象
			List<User> result = qr.query(sql, new BeanListHandler<User>(User.class));
			
			//遍历结果
			for(User u : result) {
				System.out.println(u);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 查询单条用户数据
	 */
	@Test
	public void testFindById() {
		try {
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			String sql = "select * from t_user where id=?";
			User result = qr.query(sql, new BeanHandler<User>(User.class), 1);
			System.out.println(result);
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 统计用户的个数
	 */
	@Test
	public void testCountUser() {
		try {
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			String sql = "select count(id) from t_user";
			/*
			 * 如果对于重载的query觉得太难看懂了,那么你就记住调用query方法的时候,
			 * 第一个传sql,第二个传结果集处理对象,第三个传参数(可选项)
			 * 如果不需要传参数,那么你第一个传sql,第二个传结果集处理对象就行了
			 */
			Long result = (Long) qr.query(sql, new ScalarHandler()); //返回值本来是Object,但一般我们把它转为包装类型Long
			System.out.println("用户的总数为:" + result);
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 自己动手处理结果集  了解
	 * 
	 * 需求:根据id查询出一条数据,查出来的数据,对name字段的值加上一个"开发部"
	 */
	@Test
	public void testHandler() {
		try {
			QueryRunner qr = new QueryRunner(C3P0Util.getDataSource());
			String sql = "select * from t_user where id=?";
			User user = qr.query(sql, new ResultSetHandler<User>() {
				@Override
				public User handle(ResultSet rs) throws SQLException {
					try {
						//编写自己的处理逻辑
						User u = new User();
						if(rs.next()) {
							u.setId(rs.getInt("id"));
							u.setName(rs.getString("username"));
							u.setPassword(rs.getString("password"));
							u.setName("开发部" + rs.getString("name"));
							return u;
						}
					}finally {
						if(rs != null) {
							rs.close();
						}
					}
					return null;
				}
			}, 1);
			System.out.println(user);
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
posted @ 2023-04-09 00:53  lkjlwq  阅读(53)  评论(0)    收藏  举报
// 侧边栏目录 // https://blog-static.cnblogs.com/files/douzujun/marvin.nav.my1502.css