mapengpeng1999@163.com JDBC,java连接数据库的标准

JDBC

JDBC:Java DataBase Connection ,是Java连接数据库的标准,数据库工具、数据库框架的底层原理是jdbc。

它提供了一系列的接口,目前市场上的数据库系统有很多,不管是何种数据库,如果想要使用Java进行连接的话,就必须对JDBC提供的这些接口进行实现,从而可以使用Java进行连接。比如MySQL数据库,需要使用Java进行连接的话,就必须提供实现了JDBC一系列接口的程序,而往往这些程序会进行打包成jar文件,也就是常说的架包或者叫做类库,而通常我们也称这些程序为连接数据库的驱动程序。

JDBC分类

JDBC分类有三类:

​ - JDBC-ODBC:sun公司推出的最早的Java连接数据库的标准,使用是桥连接的模式,想要使用Java连接数据库,就必须经过ODBC这个桥梁,效率就比较低了。所以现在来说在开发中是绝对不会使用的。

​ - JDBC连接:sun公司推出的第二代Java连接数据库的技术,提供了一套标准的Java连接数据库的标准接口,数据库想要使用Java进行连接,就必须提供实现了这套标准接口的类库。

​ - JDBC网络连接:表示的是连接网络上的数据库。JDBC网络连接和 JDBC连接是一回事,数据库连接别人的,要求在同一局域网内,并关闭防火墙。真正项目开发中,是连接网络上的数据库。教学才会连接本地的。

java连接数据库

JDBC的核心接口:

Connection : 此接口的对象是数据库连接对象
Statement:数据库操作对象
PreaparedStatement : 预处理(预编译)对象
ResultSet : 查询结果集对象
DriverManager : 驱动操作类(驱动管理器)

使用JDBC连接数据库的步骤:

1.将驱动程序添加编译到当前的工程中,将jar包放到lib下并Build Path编译它。动态WEB工程会自动编译jar包。

右击java project工程名,new个Folder并命名为lib,将jar包放入lib,右击jar包,Build Path,Add to Build Path

编译后会在Referenced Libraries目录下有刚才那个放到lib目录下的驱动jar包。

2.加载驱动程序,Class.forName(“com.mysql.jdbc.Driver”);

3.使用DriverManager(驱动管理器)获取数据库连接,

conn=DriverManager.getConnection(url,user,password);

先得定义conn对象,Connection conn = null;在方法中局部变量没有默认值,要赋初始值null

4.进行数据库增删改查操作(定义sql语句)(Statement(有sql注入问题)|PreparedStatement(预编译的形式,可能需要向sql中的占位符传递值)|CallableStatement),

如果是查询需要使用ResultSet接收查询的结果集。
遍历查询结果集,用if(res.next())只能遍历一笔数据,用while(res.next())可以循环遍历多笔数据,一般用while。如查询登录,是查询一笔数据(2个结果,要么1笔要么0笔)

5.关闭数据库,数据库的连接资源非常有限,使用完成之后需要关闭。

初学者容易忘记将jar包编译到当前工程中。

  • java.sql包是JDBC操作包,其中定义的大部分是接口,如果说想要想要连接到MySQL数据库,那么MySQL数据库肯定要对JDBC的标准接口进行实现,那么实现程序呢?需要加入jar包到应用程序中。
    -如果大家现在用的是MySQL8.0X 版本,使用这个jar包mysql-connector-java-8.0.13.jar。
  • 连接参数的问题,使用JDBC连接MySQL数据库查询结果可能乱码,可能连接出现时区问题等等,都需要加上连接参数。
  • MySQL8.0级以上版本的驱动程序不再是com.mysql.jdbc.Driver,而是com.mysql.cj.jdbc.Driver,8.0及以上版本还必须加上时区的参数jdbc:mysql://127.0.0.1:3306/ssm?useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
  • 查询结果集乱码需要加的参数是:jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf-8
package com.wanbangee.j;
/*1.加载驱动, Class.forName("com.mysql.jdbc.Driver");
2.获取数据库连接,Connection conn=DriverManager.getConnection(url,user,password);
conn = DriverManager.getConnection("jdbc:mysql:///ssm","root","3306");*/
import java.sql.Connection; //全部是SQL包下的
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCDemo01 {
	public static void main(String[] args) {
	//Connection 是数据库连接的接口,其对象conn就是数据库连接对象
		Connection conn = null;
		//先得定义conn对象,在方法中局部变量没有默认值,要赋初始值null
		try {//反射中,class对象获取有三种方式,1对象.class 2类名.class 3Class.forName(全类名)
			 Class.forName("com.mysql.jdbc.Driver");//加载驱动程序
			 //驱动jar包的全类名,com.mysql.jdbc包名+Driver驱动类名
			 //这行代码会出现受检查的异常,需要throws抛异常或try catch捕获异常,这里用捕获异常
			 conn = DriverManager.getConnection("jdbc:mysql:///ssm","root","3306");
			 //获取数据库连接,DriverManager是驱动管理器的意思
			 //这里要将Connection conn变量定义到try外面,主方法里面,否则后面conn.close();就无效
			 //因为conn变量定义在try中,它的作用域也只有在try后面花括号中
			 System.out.println(conn);//测试连接
		} catch (Exception e) { //上面两行代码都会出现不同异常,所以用最高异常捕获Exception e
			e.printStackTrace();
		} finally { 
			try {//关闭数据库是受检查的异常,用try catch捕获异常
				conn.close();//关闭数据库
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
package com.wanbangee.j;

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

public class JDBCDemo02 {
/*先定义几个常量,常量字母全部要大写,DRIVERCLASS就是加载驱动程序的路径
URL就是要访问哪个数据库,jdbc:mysql://localhost:3306/ssm,如果访问的是本地数据库,端口号默认3306,
这里localhost:3306(或者127.0.0.1:3306)也可省略不写。
USER 就是使用者统一用root。PASSWORD就是数据库登入密码,3306*/
	public static final String DRIVERCLASS = "com.mysql.jdbc.Driver";
	public static final String URL = "jdbc:mysql://localhost:3306/ssm";
	public static final String USER = "root";
	public static final String PASSWORD = "3306";
	
	public static void main(String[] args) {
		Connection conn = null;
		try {
			Class.forName(DRIVERCLASS);
			conn = DriverManager.getConnection(URL, USER, PASSWORD);
			System.out.println(conn);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

数据库连接工具类DBUtil:

package com.wanbangee.j;

import java.sql.Connection;
import java.sql.DriverManager;

public class DBUtil {
	public static final String DRIVERCLASS = "com.mysql.jdbc.Driver";
	public static final String URL = "jdbc:mysql:///ssm";
	public static final String USER = "root";
	public static final String PASSWORD = "3306";
	//定义个静态方法,通过类名DBUtil就可访问
	//这个方法要返回数据库连接对象,所以用Connection类型接收
	public static Connection getConnection() {
		Connection conn = null;
		try {
			Class.forName(DRIVERCLASS); //1.加载驱动
			conn = DriverManager.getConnection(URL, USER, PASSWORD);//2.获得库连接
		} catch (Exception e) {
			e.printStackTrace();
		}
		return conn; //3.返回数据库连接对象
	}
}

测试类:

package com.wanbangee.j;

import java.sql.Connection;

public class JDBCDemo03 {
	public static void main(String[] args) {
		Connection conn = null;
		try {
			conn = DBUtil.getConnection();
			System.out.println(conn);
		} catch (Exception e) {
		}finally { //关闭才用到finally
			try {
				conn.close();
			} catch (Exception e) {
			e.printStackTrace();
		}
		}
	}
}

基本的增删该查操作:

使用Statement这个接口进行增删改查操作,如果是查询的需要使用ResultSet接收查询的结果集。如果是进行增删改操作,还需要进行事务的管理。

java代码中的字符串拼接:往字符串里加变量(这个变量是事先定义好的)的操作,先打一对双引号,
在引号里打两个加号,在加号中间加上变量,"+变量+"
String s = "+变量+";  String s = "abcdef";  String s = "abc"+变量+"def";
String name = "lll";
double balanceAfter = 50;
String sql = "update account set balance = "+balanceAfter+" where name = '"+name+"'";

新增:

package com.wanbangee.j;

import java.sql.Connection;
import java.sql.Statement;

public class JDBCDemo03 {
	public static void main(String[] args) {
		String userName = "jjm";
		String passWord = "jjm123";
		
		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "insert into admin (username,password) values ('"+userName+"','"+passWord+"')"; //2编写sql语句
	//String sql = "insert into admin values (1,'m','1')";
			state = conn.createStatement();//3取得数据库操作对象
			state.execute(sql); //4执行sql语句
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//5关闭顺序,先声明后关闭,后声明先关闭。
				state.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}


		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "insert into account values('wzx',100)";//2编写sql
			state = conn.createStatement();//3获得数据库操作对象
			state.execute(sql);//4执行sql
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//5关闭,先用后关,后用先关
				state.close();
				conn.close();

修改:只需要在数据新增的程序进行sql的修改

package com.wanbangee.j;

import java.sql.Connection;
import java.sql.Statement;

public class JDBCDemo03 {
	public static void main(String[] args) {
		String userName = "姜建民";
		String passWord = "jjm123";
		
		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "update admin set username = '"+userName+"' where username = 'jjm'";//2编写sql
			state = conn.createStatement();//3取得数据库操作对象
			state.execute(sql);//4执行sql语句
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//5关闭顺序,先声明后关闭,后声明先关闭。
				state.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "update account set balance = 200 where name = 'wzx'";//2编写sql
			state = conn.createStatement();//3获得数据库操作对象
			state.execute(sql);//4执行sql
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//5关闭,先用后关,后用先关
				state.close();
				conn.close();

删除:只需要在数据修改的程序进行sql的修改

package com.wanbangee.j;

import java.sql.Connection;
import java.sql.Statement;

public class JDBCDemo03 {
	public static void main(String[] args) {
		
		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "delete from admin where username = '姜建民'";//2编写sql
			state = conn.createStatement();//3取得数据库操作对象
			state.execute(sql);//4执行sql语句
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//5关闭顺序,先声明后关闭,后声明先关闭。
				state.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "delete from account where name = 'wzx'";//2编写sql
			state = conn.createStatement();//3获得数据库操作对象
			state.execute(sql);//4执行sql	
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//5关闭,先用后关,后用先关
				state.close();
				conn.close();

查询:需要使用ResultSet接收查询的结果集。

package com.wanbangee.j;

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

public class JDBCDemo03 {
	public static void main(String[] args) {
		
		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		ResultSet res = null;//声明数据库查询操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "select username,password from admin";//2编写sql
			state = conn.createStatement();//3取得数据库操作对象
			res = state.executeQuery(sql);//4执行查询,并返回查询结果集
			while(res.next()) { //5遍历结果集,取得并且判断是否有下一个结果
				//通过查询结果集中的数据列名称获取值,如果查询的列有别名的话,则使用别名获取。
System.out.println(res.getString("username")+"======"+res.getString("password"));
				//通过查询结果集中数据列的位置获取值,索引从1开始,这种方式的sql语句不建议使用
				//System.out.println(res.getString(1)+"======"+res.getString(2));
				//数据库中索引是从1开始的,这里查询两列信息select username,password 所以下标为1和2
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//关闭顺序,先声明后关闭,后声明先关闭。
				res.close();
				state.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		ResultSet res = null;//声明数据库查询操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "select name,balance from account";//2编写sql
			state = conn.createStatement();//3获得数据库操作对象
			res = state.executeQuery(sql);//4执行sql,并返回查询结果集
			while(res.next()) {//5遍历结果集,取得并且判断是否有下一个结果
				//System.out.println(res.getString(1)+"====="+res.getString(2));
				//通过查询结果集中数据列的位置获取值,索引从1开始,这种方式的sql语句不建议使用
				//数据库中索引是从1开始的,这里查询两列信息select name,balance 所以下标为1和2
			System.out.println(res.getString("name")+"======"+res.getString("balance"));
				//通过查询结果集中的数据列名称获取值,如果查询的列有别名的话,则使用别名获取。
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//6关闭,先用后关,后用先关
				res.close();
				state.close();
				conn.close();
				
        String name = "mpp";
		Connection conn = null;//声明数据库连接对象
		Statement state = null;//声明数据库操作对象
		ResultSet res = null;//声明数据库查询操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "select name,balance from account where name = '"+name+"'";//2编写sql
			state = conn.createStatement();//3获得数据库操作对象
			res = state.executeQuery(sql);//4执行sql,并返回查询结果集
			while(res.next()) {//5遍历结果集,取得并且判断是否有下一个结果
				if(res.getDouble("balance") > 60) {
					System.out.println("余额充足");
				}else {
					System.out.println("余额不足");
				}
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//6关闭,先用后关,后用先关
				res.close();
				state.close();
				conn.close();

以上完成了增删改查操作,注意的是:查询需要使用ResultSet接收查询的结果集,对于Connection、Statement和ResultSet 都需要进行关闭。

预处理操作(重点,核心)

预处理:预先执行sql语句,而后将sql语句中需要传入的值一个一个的设置上。如果sql语句中有需要传入的参数,我们使用?作为占位符,执行了sql语句之后,再单独的设置每个问号的值。

预处理的操作对象是PreparedStatement
遍历查询结果集,用if(res.next())只能遍历一笔数据,用while(res.next())可以循环遍历多笔数据,一般用while。如查询登录,是查询一笔数据(2个结果,要么1笔要么0笔)

使用PreparedStatement这个接口进行增删改查操作,如果是查询的需要使用ResultSet接收查询的结果集。如果是进行增删改操作,还需要进行事务的管理。

登录的查询:

package com.wanbangee.j;

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

public class JDBCDemo03 {
	public static void main(String[] args) {
		String userName = "mpp";
		//String passWord = "mpp123";
		String passWord = "'mpp12' or 1=1";
		//这就是sql注入问题,或 or 0 = 0 只要后面跟上个恒等的表达式,
		//无论输入什么错误密码,只要用户名对了,它就能登入成功。反之用户名也一样。
		Connection conn = null;
		Statement state = null;
		ResultSet res = null;
		try {
			conn = DBUtil.getConnection();
			//String sql = "select count(*) from admin where username = '"+userName+"' and password = '"+passWord+"'";
			String sql = "select count(*) from admin where username = '"+userName+"' and password = "+passWord+"";  
			//这里"+passWord+"没有单引号,"'mpp12' or 1=1"中错误密码'mpp12'用了单引号
			state = conn.createStatement();//创建数据库操作对象
			res = state.executeQuery(sql);//执行查询,并返回查询结果集
			while(res.next()) {
				if(res.getInt(1) > 0) {
					System.out.println("登入成功");
				}else {
					System.out.println("登入失败");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//关闭顺序,先声明后关闭,后声明先关闭。
				res.close();
				state.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

以上的程序存在sql注入的问题,也就是说使用Statement存在安全的问题,所以在开发中几乎不会使用Statement,而是使用PreparedStatement,表示的是预处理,可以防止sql注入,使用?作为占位符,执行了sql语句之后再一个一个给?赋值。
以上的程序存在sql注入的问题,比如用户输入 100 or 0=0,拼接到sql中是一个恒成立的,能够查询所有数据,这就是sql注入,比如登录程序中,要求用户输入用户名和密码,用户名是明文(***)显示的,其他人获取用户名是很简单的,登录的sql:select * from user where user_name = jjm and password = 123 or 1=1 ,这种情况下,我们不能使用Statement进行处理了,应该使用预编译的形式:sql语句中的参数不再是通过字符串拼接的形式传递,而是使用?作为参数的占位符,先执行sql,再设置参数。这个使用就需要使用PreparedStatement 预编译操作对象。

package com.wanbangee.j;

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

public class JDBCDemo03 {
	public static void main(String[] args) {
		String userName = "mpp";
		//String passWord = "'mpp12' or 1=1"; 失败,只有正确的密码才行
		String passWord = "mpp123";
		
		Connection conn = null;
		PreparedStatement pstate = null;
		ResultSet res = null;
		try {
			conn = DBUtil.getConnection();
			String sql = "select count(*) from admin where username = ? and password = ?";
			pstate = conn.prepareStatement(sql);//1.获取数据库预处理对象,并且执行sql
			pstate.setString(1, userName);//设置第一个?号的值
			pstate.setString(2, passWord);//设置第二个?号的值
			res = pstate.executeQuery();//2.接收查询的结果集
			while(res.next()) {
				if(res.getInt(1) > 0) {
					System.out.println("登入成功");
				}else {
					System.out.println("登入失败");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				//关闭顺序,先声明后关闭,后声明先关闭。
				res.close();
				pstate.close();
				conn.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}


		String nameAfter = "mpp";
		double balanceAfter = 100;
		Connection conn = null;//声明数据库连接对象
		PreparedStatement pstate = null;//声明数据库预处理对象
		ResultSet res = null;//声明数据库查询操作对象
		try {
			conn = DBUtil.getConnection();//1取得数据库连接
			String sql = "select name,balance from account where name = ? and balance = ?";//2编写sql
			pstate = conn.prepareStatement(sql);//3获得数据库预处理对象,并执行sql
			pstate.setString(1, nameAfter);//设置第一个?号的值
			pstate.setDouble(2, balanceAfter);//设置第二个?号的值
			res = pstate.executeQuery();//4接收查询结果集
			while(res.next()) {//5遍历结果集,取得并且判断是否有下一个结果
				if(res.getDouble("balance") > 60) {
					System.out.println("余额充足"+res.getString("name")+"==="+res.getDouble(2));
				}else {
					System.out.println("余额不足");
				}
			}
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {//6关闭,先用后关,后用先关
				res.close();
				pstate.close();
				conn.close();

以上的程序就不存在sql注入 的问题了,而且我们可以使用预处理完成增删改操作。以后的开发中,绝对不会使用Statement,而是使用PreparedStatement预编译的形式。

package com.wanbangee.j;

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

public class JDBCDemo03 {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstate = null;
		try {
			conn = DBUtil.getConnection();
			String sql = "insert into admin (username,password) values (?,?)";
			//String sql = "update admin set passeord = ? where username = ?";
			//String sql = "delete from admin where username = ? and password = ?";
			pstate = conn.prepareStatement(sql);//获取预处理对象,并且执行sql
			pstate.setString(1, "wzx");//设置第一个?号的值
			pstate.setString(2, "wzx123");设置第二个?号的值
			pstate.execute();//最终的执行
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				pstate.close();
				conn.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}

总结:

account表,name(String)和balance(double)字段
使用Statement完成增删改操作:
Connection conn = null;//声明数据库连接对象
Statement state = null;//声明数据库操作对象
try{
conn = DBUtil.getConnection();//1取得数据库连接
String sql ="";//2编写sql
state = conn.createStatement();//3获得数据库操作对象
state.execute(sql);//4执行sql
}catch(Exception e){
e.printStackTrace();
}finally{
try{
state.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}
使用Statement完成查询操作,要用ResultSet来接受查询的结果集
Connection conn = null;//声明数据库连接对象
Statement state = null;//声明数据库操作对象
ResultSet res = null;//声明数据库查询对象
try{
conn = DBUtil.getConnection();//1取得数据库连接
String sql ="";//2编写sql
state = conn.createStatement();//3获得数据库操作对象
res = state.executeQuery(sql);//4执行sql,并接收查询结果集
while(res.next()){
if(res.getDouble(2)>100){
Syetem.out.println(res.getString("name"));
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
res.close();
state.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}
使用PreparedStatement完成增删改操作:
Connection conn = null;//声明数据库连接对象
PreparedStatement pstate = null;//声明数据库预处理对象
try{
conn = DBUtil.getConnection();//1取得数据库连接
String sql ="";//2编写sql
pstate = conn.prepareStatement(sql);//3获得数据库预处理对象,并执行sql
pstate.setString(1,"");//设置第一个?号的值
pstate.setDouble(2,5.00);//设置第二个?号的值
pstate.execute();//4最终的执行
}catch(Exception e){
e.printStackTrace();
}finally{
try{
pstate.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}
使用PreparedStatement完成查询操作,要用ResultSet来接受查询的结果集
Connection conn = null;//声明数据库连接对象
PreparedStatement pstate = null;//声明数据库预处理对象
ResultSet res = null;//声明数据库查询对象
try{
conn = DBUtil.getConnection();//1取得数据库连接
String sql ="";//2编写sql
pstate = conn.prepareStatement(sql);//3获得数据库预处理对象,并执行sql
pstate.setString(1,"");//设置第一个?号的值
pstate.setDouble(2,5.00);//设置第二个?号的值
res = pstate.executeQuery();//4执行sql,并接收查询结果集
while(res.next()){
Syetem.out.println(res.getString(1));
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
res.close();
pstate.close();
conn.close();
}catch(Exception e){
e.printStackTrace();
}

批处理操作(了解)

一次性执行多条sql语句就是批处理。(开发中实际使用的少)

package com.wanbangee.j;

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

public class JDBCDemo03 {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstate = null;
		try {
			conn = DBUtil.getConnection();
			String sql = "insert into admin (username,password) values (?,?)";
			pstate = conn.prepareStatement(sql);
			for (int i = 0; i < 10; i++) {
				pstate.setString(1, "wzx");
				pstate.setString(2, "iiiii");
				pstate.addBatch();//加入批处理
			}
			pstate.executeBatch();//执行批处理
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				pstate.close();
				conn.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}

事务管理(重点,核心)

事务核心点就是事务不能自动提交,而是手动提交,如果程序运行没有异常则进行提交,出现异常则进行回滚。
执行一批sql的时候,要么全部成功要么全部失败。事务的步骤:

任何一条DML(增删改)语句的执行都可视为事务的开启。

使用PreparedStatement这个接口进行增删改查操作,如果是查询的需要使用ResultSet接收查询的结果集。如果是进行增删改操作,还需要进行事务的管理。

  1. 取消事务的自动提交
  2. 执行sql没有出现异常则提交事务
  3. 出现异常则回滚事务
package com.wanbangee.jdbc;

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

import com.wanbangee.util.DBUtil;

public class JDBCDemo09 {

	public static void main(String[] args) {
	
		Connection conn = null;
		PreparedStatement pstate = null;
		try {
			conn = DBUtil.getConnection();
			//取消事务的自动提交
			conn.setAutoCommit(false);
			String sql = "insert into boys (boyname,usercp) values(?,?)";
			pstate = conn.prepareStatement(sql);
			for(int i = 5;i<15;i++) {
				pstate.setString(1, "王大锤");
				pstate.setInt(2, i);
				pstate.addBatch();//加入批处理
			}
			pstate.executeBatch();//执行批处理
			conn.commit();//没有异常则提交事务
		} catch (Exception e) {
			try {
				conn.rollback();//出现异常回滚事务
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		} finally {
			try {
				pstate.close();
				conn.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}
注意这里要将数据库中usercp类型改为char(1)

没有事务的效果:

- 创建account表,有name和balance字段,balance存在检查约束不能小于0(mysql不能添加 check检查约束)

- 往表中加入数据,INSERT INTO account VALUES(‘jjm’,100),(‘mpp’,100);

- jjm转账给mpp1000

package com.wanbangee.jdbc;

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

public class Demo05 {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstate = null;
		
	try {
		conn = DBUtil.getConnection();
		String sql = "update account set balance = balance + ? where name = ?";
		pstate = conn.prepareStatement(sql);
		//jjm转账给mpp1000
		//mpp加上一千
		pstate.setDouble(1, 1000);
		pstate.setString(2, "mpp");
		pstate.addBatch();//加入到批处理
		//jjm减一千
		pstate.setDouble(1, -1000);
		pstate.setString(2, "jjm");
		pstate.addBatch();//加入到批处理
		pstate.executeBatch();//执行批处理
	} catch (Exception e) {
		e.printStackTrace();
		}finally {
			try {
				pstate.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}

以上的程序存在问题,如果jjm的余额小于1000的情况下,转账给mpp,这时jjm余额减去1000的话,违反了检查约束条件,程序出现异常,但是mpp的余额居然加了1000,那么这1000块钱是从哪里来的呢?出现问题的原因就是第一次sql执行成功,第二次执行失败,但是转账又是一个不可分割的整体,要么全部执行成功,要么全部执行失败。这种程序必须放在一个事务中。JDBC中事务的操作步骤:

- 取消事务的自动提交

- 没有异常,提交事务

- 出现异常,回滚事务

package com.wanbangee.jdbc;

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

public class Demo05 {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstate = null;
		
	try {
		conn = DBUtil.getConnection();
		
		conn.setAutoCommit(false);//取消事务的自动提交
		
		String sql = "update account set balance = balance + ? where name = ?";
		pstate = conn.prepareStatement(sql);
		//jjm转账给mpp1000
		//mpp加上一千
		pstate.setDouble(1, 1000);
		pstate.setString(2, "mpp");
		pstate.addBatch();//加入到批处理
		

		//jjm减一千
		pstate.setDouble(1, -1000);
		pstate.setString(2, "jjm");
		pstate.addBatch();//加入到批处理
		pstate.executeBatch();//执行批处理
		
		String balanceAfter = "select balance from account where name = 'jjm'";
		int a = Integer.parseInt(balanceAfter); //将字符串转变为整型
		if(a < 0) {
			throw new RuntimeException("余额不足");
		}
		
		/*if(1==1) {
			throw new Exception("");
		}*/
		
		conn.commit();//没有异常则提交事务
		
	} catch (Exception e) {
		try {
			conn.rollback();//出现异常回滚事务
		} catch (Exception e2) {
		}
		e.printStackTrace();
		}finally {
			try {
				pstate.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}//这还是错误的操作

正确操作:

package com.wanbangee.jdbc;

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

public class Demo5 {
	public static void main(String[] args) {
		Connection conn = null;
		PreparedStatement pstate = null;
		ResultSet res = null;
		
	try {
		conn = DBUtil.getConnection();
		
		conn.setAutoCommit(false);//取消事务的自动提交
		
		String sql = "update account set balance = balance + ? where name = ?";
		pstate = conn.prepareStatement(sql);
		//jjm转账给mpp1000
		//mpp加上一千
		pstate.setDouble(1, 1000);
		pstate.setString(2, "mpp");
		pstate.addBatch();//加入到批处理
		

		//jjm减一千
		pstate.setDouble(1, -1000);
		pstate.setString(2, "jjm");
		pstate.addBatch();//加入到批处理
		pstate.executeBatch();//执行批处理
		
		String balanceAfter = "select balance from account where name = ?";
		pstate = conn.prepareStatement(balanceAfter);//获得预处理对象,并执行sql
		pstate.setString(1, "jjm");//给?设置值
		res = pstate.executeQuery();//返回查询结果集
		while(res.next()) { //遍历结果集
			if(res.getDouble("balance") < 0) {
				System.out.println(res.getDouble(1)+"余额不足");
				throw new RuntimeException("余额不足");
			}else {
				System.out.println(res.getDouble("balance"));
			}
		}
		conn.commit();//没有异常则提交事务
		
	} catch (Exception e) {
		try {
			conn.rollback();//出现异常回滚事务
		} catch (Exception e2) {
		}
		e.printStackTrace();
		}finally {
			try {
				pstate.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值