JDBC

JDBC

JDBC概述

java DataBase Connectivity:java数据库连接,JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,一般驱动由数据库厂商免费提供自己数据库对应的驱动。

JDBC开发步骤

  1. 注册驱动
  2. 获得连接
  3. 获得语句执行者
  4. 执行sql语句
  5. 处理结果
  6. 释放资源

步骤详解

public class TestSelect{
	try{
		//1.注册驱动
			//得到Driver相关的类或接口的class对象(利用了反射)
		Class.forName("com.mysql.jdbc.Driver");
		//2.获取连接
			//利用Driver以三个参数去获得连接conn,三个参数jdbc:mysql://localhost:3306/web08"代表url,"root"是用户名,"007011"是密码
		Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
		//3.获得语句执行者
			//3.1编写sql语句
				//将一些词换成占位符?
			String sql ="select * from table_user where uname=? and upassword=?";
			//3.2.创建预处理对象pstmt(conn创建)
		PreparedStatement pstmt =conn.prepareStatement(sql);
			//3.3用pstmt设置参数(替换占位符)
			pstmt.setString(1, username);
			pstmt.setString(2, upassword);
		//4.执行查询操作
		ResultSet rs = pstmt.executeQuery();
		//5.对结果集进行处理
		if(rs.next()){
			System.out.println("恭喜您,"+username+",登陆成功!");
			System.out.println(sql);
		}else{
			System.out.println("账户或密码错误!");
		}
		//6.释放资源
		if(rs!=null)rs.close();
		if(stmt!=null)stmt.close();
		if(conn!=null)conn.close();
	}
}

封装工具类

获得连接

返回值是一个Connection conn,不需要传入值

public static Connection getConnection(){
		Connection conn =null;
		//1.注册驱动
		try {
			Class.forName("com.mysql.jdbc.Driver");
			//2.获取连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn;
	}

释放资源

传入三个参数中任意个参数

public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
		if(rs!=null)
			try {
				rs.close();
			} catch (Exception 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();
			}
	}

使用properties配置文件

将第二步“获取连接”封装起来,将四个参数都封装到properties配置文件中

  • properties文件的要求:

    1. 文件位置:任意建议src下
    2. 文件名称:任意扩展名为properties
    3. 文件内容:一行一组数据,格式是key=value
  • 创建配置文件
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/day07_db
    jdbc.user=root
    jdbc.password=你的密码

加载配置文件

使用JDK提供的工具类ResourceBundle加载properties文件

public class JDBC_UtilsV2 {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	//静态代码块加载配置文件信息
	static{
		ResourceBundle bundle =ResourceBundle.getBundle("db");//获得工具类ResourceBundle
		driver = bundle.getString("driver");
		url = bundle.getString("url");
		username = bundle.getString("username");
		password =bundle.getString("password");
	}
	public static Connection getConnection(){
	//......
	}
	public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
	//........
	}
}	

除了使用ResourceBundle直接加载,还可以使用类加载器ClassLoader加载properties文件获得输入流,再new一个Properties 对象props,用props加载输入流,


public class JDBC_UtilsV2 {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	//静态代码块加载配置文件信息
	static{
		//1.通过当前类获取器获取类加载器
			ClassLoader classLoader =JDBC_UtilsV3.class.getClassLoader();
			//2.通过类加载器的方法获得一个输入流
			InputStream is = classLoader.getResourceAsStream("db.properties");
			//3.创建一个properties对象
			Properties props =new Properties();
			//4.加载输入流
			props.load(is);
			//5.获取相关参数的值
			driver = props.getProperty("driver");
			url = props.getProperty("url");
			username = props.getProperty("username");
			password =props.getProperty("password");
	}
	public static Connection getConnection(){
	//......
	}
	public static void release(ResultSet rs,PreparedStatement pstmt,Connection conn){
	//........
	}
}	

使用工具类实例

使用工具类后:

public class TestUtils {
	//更新用户信息方法
	@Test
	public void testUpdateById(){
		Connection conn =null;
		PreparedStatement pstmt =null;
		
		try {
			//1.获取连接
			conn =JDBC_UtilsV3.getConnection();
			//2.编写sql语句
			String sql ="update table_user set upassword=? where uid =?";
			//3.获取执行sql语句对象
			pstmt =conn.prepareStatement(sql);
			//4.设置参数
			pstmt.setString(1, "000");
			pstmt.setInt(2, 1);
			
			//5.执行更新操作
			int row =pstmt.executeUpdate();
			if(row>0){
				System.out.println("更新成功!");
			}else{
				System.out.println("更新失败!");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			//6.释放资源
			JDBC_UtilsV3.release(null, pstmt, conn);
		}

使用工具类前:

public class TestSelect{
	try{
	//1.获取连接
	conn =JDBC_UtilsV3.getConnection();
					//1.注册驱动
						//得到Driver相关的类或接口的class对象(利用了反射)
					Class.forName("com.mysql.jdbc.Driver");
					//2.获取连接
						//利用Driver以三个参数去获得连接conn,三个参数jdbc:mysql://localhost:3306/web08"代表url,"root"是用户名,"007011"是密码
					Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/web08","root","007011");
	//2.编写sql语句
	String sql ="update table_user set upassword=? where uid =?";				
				//3.获得语句执行者
					//3.1编写sql语句,将一些词换成占位符?
					String sql ="select * from table_user where uname=? and upassword=?";
	//3.获取执行sql语句对象
	pstmt =conn.prepareStatement(sql);			
					//3.2.创建预处理对象pstmt(conn创建)
				PreparedStatement pstmt =conn.prepareStatement(sql);
	//4.设置参数
	pstmt.setString(1, "000");
	pstmt.setInt(2, 1);			
					//3.3用pstmt设置参数(替换占位符)
					pstmt.setString(1, username);
					pstmt.setString(2, upassword);
	//5.执行更新操作
	int row =pstmt.executeUpdate();
	if(row>0){
		System.out.println("更新成功!");
	}else{
		System.out.println("更新失败!");
	}				
				//4.执行查询操作
				ResultSet rs = pstmt.executeQuery();
				//5.对结果集进行处理
				if(rs.next()){
					System.out.println("恭喜您,"+username+",登陆成功!");
					System.out.println(sql);
				}else{
					System.out.println("账户或密码错误!");
				}
		//6.释放资源
		if(rs!=null)rs.close();
		if(stmt!=null)stmt.close();
		if(conn!=null)conn.close();
	}
}

使用连接池

概念:

解决频繁“获得连接”和“释放资源”消耗资源的问题,——使用池来管理Connection,不用创建Connection,而是通过池来获取Connection对象。使用完Connection之后以归还池来代替关闭Connection

规范:

Java为数据库提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口

自定义连接池步骤

  1. 创建连接池实现(数据源),并实现javax.sql.DataSource,重写其中的getConnectio()方法
  2. 提供一个集合,用于存放连接,选择LinkedList来便捷的完成移除和添加操作
  3. 在静态代码块中为连接池初试化5个链接(本案例)
  4. 调用getConnection()方法,保证当前连接只能提供一个线程使用,所以我们需要将连接先将连接池中移除(避免再提供给其他线程)
  5. 使用完之后,不执行close()方法,而是将其添加到连接池中而不是直接释放

定义连接池的实现

定义一个连接池
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;

import cn.itheima.jdbc.utils.JDBC_UtilsV3;
//1.创建连接池实现(数据源),并实现jDataSource接口
public class MyDataSource implements DataSource{

// 2.创建1个容器用于存储Connection对象
	private static LinkedList<Connection> pool =new LinkedList<Connection>();
//3.创建5个链接放到容器中
	static{
		for(int i=0;i<5;i++){
			Connection conn =JDBC_UtilsV3.getConnection();
			pool.add(conn);
		}
	}
//4.调用getConnection()方法,保证当前连接只能提供一个线程使用,所以我们需要将连接先将连接池中移除(避免再提供给其他线程)
	@Override
	public Connection getConnection() throws SQLException {
		Connection conn = null;
	//4.1使用前先判断
		if(pool.size()==0){
	//4.2池子里面没有,我们再创建一些
			for(int i=0;i<5;i++){
				conn =JDBC_UtilsV3.getConnection();
				//这里的JDBC_UtilsV3是调用了之前定义的工具类
				pool.add(conn);
			}
		}
	//4.3如果有,从池子里获取一个Connection,先将其从连接池中移除
		conn = pool.remove(0);
		return conn;
	}
// 5. 使用完之后,不执行close()方法,而是将其添加到连接池中而不是直接释放	
	/*
	 * 归还连接对象到连接池中
	 */
	public void backConnection(Connection conn){
		pool.add(conn);
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLogWriter(PrintWriter arg0) throws SQLException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setLoginTimeout(int arg0) throws SQLException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public boolean isWrapperFor(Class<?> arg0) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public <T> T unwrap(Class<T> arg0) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Connection getConnection(String arg0, String arg1) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

}

测试连接池
public void testAddUser(){
		Connection conn =null;
		PreparedStatement pstmt =null;
		//1.创建自定义连接池对象
		MyDataSource dataSource =new MyDataSource();
		try {
			//2.从池子中获取连接
			conn =dataSource.getConnection();
			//3.创建sql语句
			String sql ="insert into table_user values(null,?,?)";
			//4.进行操作
			pstmt = conn.prepareStatement(sql);
			pstmt.setString(1, "aa");
			pstmt.setString(2, "bb");
			int rows = pstmt.executeUpdate();
			//5.查看是否操作成功
			if(rows>0){
				System.out.println("添加成功!");
			}else{
				System.out.println("添加失败!");
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}finally{
			//归还连接池
			dataSource.backConnection(conn);
		}

方法增强

方法增强的总结:
1.子类继承父类
2.装饰者设计模式:新类实现接口,并将接口的实现类作为构造参数传入新类中,再实现新的类方法
3.动态代理:在运行时动态创建代理类,与装饰者类似

需求:

用户调用getConnection()之后,必须使用dataSource.backConnection()方法执行归还,若调用conn.close()则丢失了该conn。所以我们需要复写connection类的close方法。
复写MyDataSource对象

//1.实现同一个接口Connection
public class MyConnection implements Connection {
	
	//3.定义一个变量
	private Connection conn;
	private LinkedList<Connection> pool;
	
	//2.编写一个构造方法
	public MyConnection(Connection conn,LinkedList<Connection> pool){
		this.conn =conn;
		this.pool =pool;
	}
	
	//4.书写需要增强的方法
	public void close() throws SQLException{
		pool.add(conn);
	}
	
	//5.此方法必须覆盖,否则会出现空指针异常
	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return conn.prepareStatement(sql);
	}
	
	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// 。。。。。。
		return false;
	}
}	
使用新的connection实现
public class MyDataSource1 implements DataSource{

	// 1.创建1个容器用于存储Connection对象
	private static LinkedList<Connection> pool =new LinkedList<Connection>();
	//2.创建5个链接放到容器中
	static{
		for(int i=0;i<5;i++){
			Connection conn =JDBC_UtilsV3.getConnection();
			//放入池子connection对象已经改造过了
			MyConnection myconn =new MyConnection(conn,pool);
			pool.add(myconn);
		}
	}
	@Override
	public Connection getConnection() throws SQLException {
		Connection conn = null;
		//3.使用前先判断
		if(pool.size()==0){
			//4.池子里面没有,我们再创建一些
			for(int i=0;i<5;i++){
				conn =JDBC_UtilsV3.getConnection();
				pool.add(conn);
			}
		}
		//5.如果有,从池子里获取一个连接对象Connection
		conn = pool.remove(0);
		return conn;
	}
	/*
	 * 归还连接对象到连接池中
	 */
	public void backConnection(Connection conn){
		pool.add(conn);
	}

C3P0连接池

开源免费的连接池
使用时需要导入jar包、配置文件(C3P0-config.xml)、创建C3P0的核心工具类ComboPoolDataSource的的实例对象

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

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Utils {
//使用命名itheima(也可以不写)配置
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource("itheima");
	//获得数据源
	public static DataSource getDataSource(){
		return dataSource;
	}
	//获得Connection连接
	public static Connection getConnection(){
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}	
	}
}

DBCP连接池

也是开源的连接池,企业开发中也比较常见,tomcat内置的连接池
导入jar包、配置文件*.properties、编写工具类

public class DBCPUtils {
	private static DataSource dataSource;
	static{
		try{
			//1.加载配置文件properties获得输入流
			InputStream is =DBCPUtils.class.getClassLoader().getResourceAsStream("db.properties");
			//2.new一个Properties处理输入流
			Properties props =new Properties();
			props.load(is);
			//3.使用工具类创建连接池(数据源)
			dataSource =BasicDataSourceFactory.createDataSource(props);
		}catch(Exception e){
			throw new RuntimeException(e);
		}
	}
	//获得数据源
	public static DataSource getDataSource(){
		return dataSource;
	}
	//获得Connection连接
	public static Connection getConnection(){
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			throw new RuntimeException(e); 
		}
		
	}
}

JavaBean组件

就是一个类,常用于封装数据,具有以下特性:

  1. 需要实现接口:java.io.Serializable,通常偷懒省略
  2. 提供私有字段:private 类型 字段名
  3. 提供getter/setter
  4. 提供无参构造
public class User {
	private int uid;
	private String uname;
	private String upassword;
	public User(){
		//构造器
	}
	public int getUid() {
		return uid;
	}
	public void setUid(int uid) {
		this.uid = uid;
	}
	public String getUname() {
		return uname;
	}
	public void setUname(String uname) {
		this.uname = uname;
	}
	public String getUpassword() {
		return upassword;
	}
	public void setUpassword(String upassword) {
		this.upassword = upassword;
	}
	
}

使用DBUtils完成CRUD

DBUtils简化了Java执行sql的语句,并将结果封装成对象列表。
C3P0和DBCP是存放了一堆Connection对象的连接池。避免了java每次加载驱动和连接数据库

DBUtils核心功能和核心类

核心功能

  • QueryRunner中提供对sql语句操作的API
  • ResultHandler接口,用于定义select操作之后怎样处理结果集
  • DBUtils,它就是一个工具类,定义了关闭资源与事务处理的方法

核心类

  • QueryRunner(DataSource ds),提供数据源(连接池)。DBUtils底层自动维护connection
  • update(String sql,Object…params)执行更新数据,返回int值,表示改变了的行数
  • query(String sql,ResultSetHandler rsh,Object… params)执行查询,结果存入ResultSetHandler中

ResultSetHandler结果集处理类

在这里插入图片描述在这里插入图片描述

实现DBUtils完成CRUD

省去了PreparedStatement pstmt等操作

@Test
	public void testAddUsers(){
		//添加用户方法
		try {
		//1.创建核心类QueryRunner
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		//2.编写SQL语句
		String sql ="insert into table_user values(null,?,?)";
		//3.为占位符设置值,params里面是更新的参数
		Object[] params ={"dd","ee"};
		//4.执行添加操作
		int rows = qr.update(sql, params);
		if(rows>0){
			System.out.println("添加成功");
		}else{
			System.out.println("添加失败");
		}	
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
public void testQueryAll(){
		//查询所有的用户
			try {
			//1.获取核心类queryRunner
			QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
			//2.编写sql语句
			String sql ="select * from table_user";
			//3.执行查询操作 BeanListHandler<User>来存放查询结果
			List<User> users = qr.query(sql, new BeanListHandler<User>(User.class));
			//4.对结果集合进行遍历
//				for(User user:users){
//					System.out.println(user.getUname()+":"+user.getUpassword());
//				}	
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值