三十七、Java基础之JDBC
一、Java代码使用JDBC之Statement
1.六个步骤:
/** * JDBC驱动六大步骤: * 1、注册数据库驱动,目的是为了让jvm可以识别mysql数据库驱动类 * 2、获取和数据库的连接对象 * 3、通过连接获取语句对象 * 4、通过语句对象执行具体的DQL语句对象,返回结果集对象 * 5、遍历结果集,取出每条记录 * 6、关闭数据库相 关资源 */
2.用到的方法:
//1、注册数据库驱动: Driver driver = new com.mysql.jdbc.Driver(); //通过驱动管理器注册驱动 DriverManager.registerDriver(driver);
//2、获取和数据库的连接 //a、准备数据库相关资源 String username="root"; String password = "123456"; String url="jdbc:mysql://localhost:3306/test-csj"; //b、获取和数据库的连接 conn = DriverManager.getConnection(url, username, password);
//3、通过连接获取语句对象 //Statement是jdbc定一个的接口,表示sql语句 //conn.createStatement():返回的是一个具体类对象,这个具体类实现了Statement 接口 state = conn.createStatement();
//4、通过语句对象执行具体的DQL语句对象,返回结果集对象 //a、准备好sql语句 //b、sql有不同的种类,Statement表示不同的sql语句,所以Statement上面有不同的方法执行不同的sql语句 //我们通过调用Statement上面的executeQuery(sql)执行DQL语句,数据库返回结果集对象, String sql = "SELECT * FROM user_info WHERE role_id=2;"; ResultSet res = state.executeQuery(sql);
//5、遍历结果集,取出每条记录 if (state.execute(sql)){ res = state.getResultSet(); //5、遍历结果集,取出每条记录 while(res.next()){ //res.next():a、判断结果集中是否有下一条数据,b、如果有下一行,迭代器移动到下一行 //通过字段在查询列表中位置取得字段信息,位置从1开始 String name1 = res.getString(2); //通过该字段名称取得字段信息,这是首选方式,;字段名称不区分大小写 String name2 = res.getString("name"); int id1 = res.getInt(1); int id2 = res.getInt("id"); Date create_time = res.getDate("create_time"); String account = res.getString("account"); String employee_number = res.getString("employee_number"); String email = res.getString("email"); System.out.println(id2+" ,"+name2+" ,"+account+" ,"+email+" ,"+employee_number+" ,"+create_time+" ."); } }
//6、关闭数据库相关资源 //在关闭的时候,需要反向关闭 //异常处理: // a、可以合着处理,也就是一个try处理多个异常,后面也可接多个catch // b、可以分着处理,也就是分别用try....catch....处理每个异常 // c、何时分着?何时合着?当多个操作之间有联系的时候,应该合着处理;没有联系的时候,分着处理 if (res!=null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (state !=null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
3.实例
public class Test01 { public static void main(String[] args){ test01(); test02(); } private static void test02() { //1、注册数据库驱动,目的是为了让jvm可以识别mysql数据库驱动类 //com.mysql.jdbc.Driver()是SUN公司对Driver接口的实现类型 Connection conn=null; Statement state=null; ResultSet res=null; try { Driver driver = new com.mysql.jdbc.Driver(); //通过驱动管理器注册驱动 DriverManager.registerDriver(driver); //2、获取和数据库的连接 //a、准备数据库相关资源 String username="root"; String password = "123456"; //url:统一资源定位器,确定数据库服务器的信息 //jdbc:是一套网络协议,jdbc:mysql:是jdbc协议下的mysql协议 String url="jdbc:mysql://localhost:3306/test-csj"; //b、获取和数据库的连接 //Connection是jdbc定义的接口,表示和数据库的连接 //DriverManager.getConnection返回的是一个具体类对象,这个具体类实现了Connection接口 //这个具体类在驱动的jar文件中, conn = DriverManager.getConnection(url, username, password); //3、通过连接获取语句对象 //Statement是jdbc定一个的接口,表示sql语句 //conn.createStatement():返回的是一个具体类对象,这个具体类实现了Statement 接口 state = conn.createStatement(); //4、通过语句对象执行具体的DQL语句对象,返回结果集对象 //a、准别好sql语句 //b、sql有不同的种类,Statement表示不同的sql语句,所以Statement上面有不同的方法执行不同的sql语句 //我们通过调用Statement上面的executeQuery(sql)执行DQL语句,数据库返回结果集对象, String sql = "SELECT * FROM user_info WHERE role_id=2;"; String sql2= "INSERT INTO user_info VALUES(666,'chuchu','chuchu');"; //调用Statement中的getResultSet()方法获取, if (state.execute(sql)){ res = state.getResultSet(); //5、遍历结果集,取出每条记录 while(res.next()){ //res.next():a、判断结果集中是否有下一条数据,b、如果有下一行,迭代器移动到下一行 //通过字段在查询列表中位置取得字段信息,位置从1开始 String name1 = res.getString(2); //通过该字段名称取得字段信息,这是首选方式,;字段名称不区分大小写 String name2 = res.getString("name"); int id1 = res.getInt(1); int id2 = res.getInt("id"); Date create_time = res.getDate("create_time"); String account = res.getString("account"); String employee_number = res.getString("employee_number"); String email = res.getString("email"); System.out.println(id2+" ,"+name2+" ,"+account+" ,"+email+" ,"+employee_number+" ,"+create_time+" ."); } } if (!state.execute(sql2)){ int i = state.executeUpdate(sql2); System.out.println("i="+i); } } catch (Exception e) { e.printStackTrace(); }finally { //6、关闭数据库相关资源 //在关闭的时候,需要反向关闭 //异常处理: // a、可以合着处理,也就是一个try处理多个异常,后面也可接多个catch // b、可以分着处理,也就是分别用try....catch....处理每个异常 // c、何时分着?何时合着?当多个操作之间有联系的时候,应该合着处理;没有联系的时候,分着处理 if (res!=null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (state !=null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } private static void test01() { //1、注册数据库驱动,目的是为了让jvm可以识别mysql数据库驱动类 //com.mysql.jdbc.Driver()是SUN公司对Driver接口的实现类型 Connection conn=null; ResultSet res=null; Statement state=null; try { Driver driver = new com.mysql.jdbc.Driver(); //通过驱动管理器注册驱动 DriverManager.registerDriver(driver); //2、获取和数据库的连接 //a、准备数据库相关资源 String username="root"; String password = "123456"; //url:统一资源定位器,确定数据库服务器的信息 //jdbc:是一套网络协议,jdbc:mysql:是jdbc协议下的mysql协议 String url="jdbc:mysql://localhost:3306/test-csj"; //b、获取和数据库的连接 //Connection是jdbc定义的接口,表示和数据库的连接 //DriverManager.getConnection返回的是一个具体类对象,这个具体类实现了Connection接口 //这个具体类在驱动的jar文件中, conn = DriverManager.getConnection(url, username, password); //3、通过连接获取语句对象 //Statement是jdbc定一个的接口,表示sql语句 //conn.createStatement():返回的是一个具体类对象,这个具体类实现了Statement 接口 state = conn.createStatement(); //4、通过语句对象执行具体的DQL语句对象,返回结果集对象 //a、准别好sql语句 //b、sql有不同的种类,Statement表示不同的sql语句,所以Statement上面有不同的方法执行不同的sql语句 //我们通过调用Statement上面的executeQuery(sql)执行DQL语句,数据库返回结果集对象, String sql = "SELECT * FROM user_info WHERE role_id=2;"; res = state.executeQuery(sql); System.out.println(res); //5、遍历结果集,取出每条记录 while(res.next()){ //res.next():a、判断结果集中是否有下一条数据,b、如果有下一行,迭代器移动到下一行 //通过字段在查询列表中位置取得字段信息,位置从1开始 String name1 = res.getString(2); //通过该字段名称取得字段信息,这是首选方式,;字段名称不区分大小写 String name2 = res.getString("name"); int id1 = res.getInt(1); int id2 = res.getInt("id"); Date create_time = res.getDate("create_time"); String account = res.getString("account"); String employee_number = res.getString("employee_number"); String email = res.getString("email"); System.out.println(id2+" ,"+name2+" ,"+account+" ,"+email+" ,"+employee_number+" ,"+create_time+" ."); } } catch (Exception e) { e.printStackTrace(); }finally { //6、关闭数据库相关资源 //在关闭的时候,需要反向关闭 //异常处理: // a、可以合着处理,也就是一个try处理多个异常,后面也可接多个catch // b、可以分着处理,也就是分别用try....catch....处理每个异常 // c、何时分着?何时合着?当多个操作之间有联系的时候,应该合着处理;没有联系的时候,分着处理 if (res!=null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (state !=null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
二、由于代码中重复的代码很多,故进行封装
1.新建uti l/jdbcUtil封装类(获取数据库连接对象,和关闭数据库)
a、db.properties(为了读取文件,我们需要获取InputStream,注意下面这种方式是专门用来读取src下的文件的)
user1 =root password1=123456 url1=jdbc:mysql://localhost:3306/csjin className1=com.mysql.jdbc.Driver
b、获取db.properties中的数据并通过流InputStream读取到代码中:
//为了读取文件,我们需要获取InputStream,注意下面这种方式是专门用来读取src下的文件的 //获取jdbcUtil类对应的Class对象 Class<jdbcUtil> clasz = jdbcUtil.class; //通过Class对象获取ClassLoader类加载器对象 ClassLoader loader = clasz.getClassLoader(); //调用ClassLoader上面的方法获取inputStream InputStream in = loader.getResourceAsStream("db.properties"); Properties pro = new Properties(); //把pro对象和流连接起来 pro.load(in); user = pro.getProperty("user1"); password = pro.getProperty("password1"); url = pro.getProperty("url1"); className = pro.getProperty("className1");
c、封装获取数据库连接
/** * 注册驱动获取链接 * @return */ public static Connection getConnection() throws SQLException, ClassNotFoundException { Class.forName(className);//反射方法获取驱动 Connection conn = DriverManager.getConnection(url, user, password); return conn; }
其他方法获取数据库驱动:
//方式一:我们创建new com.mysql.jdbc.Driver类对象的时候,JVM把这个类加载到内存 //这样JVM就可以是被MySQL的数据库驱动了 Driver driver = new com.mysql.jdbc.Driver(); DriverManager.registerDriver(driver); //方式二:我们创建new com.mysql.jdbc.Driver类对象的时候,JVM把这个类加载到内存 //这样JVM就可以是被MySQL的数据库驱动了 Driver driver = new com.mysql.jdbc.Driver();可以不用执行DriverManager.registerDriver(driver); //方式三:使用反射方法,我们获取com.mysql.jdbc.Driver这个类对应的class类的时候, //JVM需要把com.mysql.jdbc.Driver自动加载到内存,这个JVM就可以识别数据库驱动了
2.代码实例:
import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class jdbcUtil { private static String user; private static String password; private static String url; private static String className; static { /* //为了读取文件,我们需要获取InputStream,注意下面这种方式是专门用来读取src下的文件的 //获取jdbcUtil类对应的Class对象 Class<jdbcUtil> clasz = jdbcUtil.class; //通过Class对象获取ClassLoader类加载器对象 ClassLoader loader = clasz.getClassLoader(); //调用ClassLoader上面的方法获取inputStream InputStream in = loader.getResourceAsStream("db.properties");*/ /**并上面三行代码*/ InputStream in = jdbcUtil.class.getClassLoader().getResourceAsStream("db.properties"); //为了方便的读取"db.properties"这张特殊格式的文件,创建一个java.util.properties类对象 Properties pro = new Properties(); try { //把pro对象和流连接起来 pro.load(in); user = pro.getProperty("user1"); password = pro.getProperty("password1"); url = pro.getProperty("url1"); className = pro.getProperty("className1"); }catch (IOException e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 注册驱动获取链接 * @return */ public static Connection getConnection() throws SQLException, ClassNotFoundException { Class.forName(className); Connection conn = DriverManager.getConnection(url, user, password); return conn; } /** * 关闭数据库相关对象的方法 */ public static void closeDb(Connection conn, Statement state, ResultSet res){ if (res!=null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } if (state !=null) { try { state.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn!=null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
3.调用封装的方法jdbcUtil,执行数据库sql
import com.csjin.edu.TestJDBC.Test02.util.jdbcUtil; import java.sql.*; public class Test02 { public static void main(String[] args) throws SQLException, ClassNotFoundException { /** * 方式一:我们创建new com.mysql.jdbc.Driver类对象的时候,JVM把这个类加载到内存 * 这样JVM就可以是被MySQL的数据库驱动了 * Driver driver = new com.mysql.jdbc.Driver(); * DriverManager.registerDriver(driver); */ test1(); /** * 方式一:我们创建new com.mysql.jdbc.Driver类对象的时候,JVM把这个类加载到内存 * 这样JVM就可以是被MySQL的数据库驱动了 * Driver driver = new com.mysql.jdbc.Driver();可以不用执行DriverManager.registerDriver(driver); */ test2(); /** * 使用反射方法,我们获取com.mysql.jdbc.Driver这个类对应的class类的时候, * JVM需要把com.mysql.jdbc.Driver自动加载到内存,这个JVM就可以识别数据库驱动了 */ test3(); test4(); } private static void test4() { Connection conn=null; Statement state=null; ResultSet res=null; try { conn = jdbcUtil.getConnection(); state = conn.createStatement(); String sql = "SELECT * FROM user_info WHERE role_id=2;"; String sql2= "INSERT INTO user_info VALUES(222,'李','chu01','4b31a1d73cea9e6c432b59adc9eed725',1,2,111111,NULL,NULL,NULL,'aaaa@qq.com',NULL,NULL,0,'2020-03-20 00:00:00','2017-11-30 10:21:01','2017-11-30 10:21:01',1);"; //如果第一个结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在任何结果,则返回false if (!state.execute(sql2)){ // 来获取更新计数器 int count = state.getUpdateCount(); System.out.println("count ="+count); } } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { jdbcUtil.closeDb(conn,state,res); } catch (Exception e) { e.printStackTrace(); } } } private static void test3() throws ClassNotFoundException, SQLException { String className = "com.mysql.jdbc.Driver"; Class.forName(className); String username="root"; String password = "123456"; //url:统一资源定位器,确定数据库服务器的信息 //jdbc:是一套网络协议,jdbc:mysql:是jdbc协议下的mysql协议 String url="jdbc:mysql://localhost:3306/test-csj"; Connection conn = DriverManager.getConnection(url, username, password); if (conn!=null){ System.out.println("驱动注册成功3!"); } } private static void test2() throws SQLException { Driver driver = new com.mysql.jdbc.Driver(); //通过驱动管理器注册驱动 String username="root"; String password = "123456"; //url:统一资源定位器,确定数据库服务器的信息 //jdbc:是一套网络协议,jdbc:mysql:是jdbc协议下的mysql协议 String url="jdbc:mysql://localhost:3306/test-csj"; Connection conn = DriverManager.getConnection(url, username, password); if (conn!=null){ System.out.println("驱动注册成功2!"); } } private static void test1() throws SQLException { Driver driver = new com.mysql.jdbc.Driver(); //通过驱动管理器注册驱动 DriverManager.registerDriver(driver); String username="root"; String password = "123456"; //url:统一资源定位器,确定数据库服务器的信息 //jdbc:是一套网络协议,jdbc:mysql:是jdbc协议下的mysql协议 String url="jdbc:mysql://localhost:3306/test-csj"; Connection conn = DriverManager.getConnection(url, username, password); if (conn!=null){ System.out.println("驱动注册成功1!"); } } }
三、如何防止SQl注入攻击
/** * sql注入攻击: * SELECT * FROM users WHERE username='sss' AND `password`='ss' or 'x' = 'x'; * * 如何避免sql注入攻击: * 使用PreparedStatement,PreparedStatement不需要sql拼接 * * 比较PreparedStatement与Statement区别? * 1、Statement执行sql语句的时候,编译一次,执行一次,执行效率低 * PreparedStatemen执行sql语句的时候,编译一次,可以反复执行,效率高 * 2、Statement执行sql语句需要拼接,有sql注入风险 * PreparedStatemen执行sql语句不需要拼接,使用占位符即可,可 避免sql注入 * */
1.从键盘获取用户名和密码,并将此方法封装成一个map类型的
public class Test03 { public static Map<String,String> readkeyBoard(){ Map<String,String> map = new HashMap<>(); Scanner scan = new Scanner(System.in); System.out.println("请输入username,并回车:"); String user = scan.nextLine();//scan.nextLine();以\n为标记读取一 map.put("user",user); System.out.println("请输入password,并回车:"); String password = scan.nextLine(); map.put("password",password); if (scan != null){ scan.close(); } return map; }
2.利用登陆实例说明SQL注入,无防止
密码只要输入:ss' or 'x' = 'x 即可登录成功,因为Statement,我们只能使用的sql拼接
/** * 检查登录是否成功 * @return */ public static boolean login(){ Map<String,String> map = readkeyBoard(); Connection conn =null; Statement state =null; ResultSet res = null; boolean flag = false; try { conn = jdbcUtil.getConnection(); state = conn.createStatement(); String sql = "SELECT * FROM users WHERE username='"+map.get("user")+"' AND `password`='"+map.get("password")+"';"; System.out.println(sql); res = state.executeQuery(sql); System.out.println(res); /** * 查询结果中有数据 */ if (res.next()){ flag=true; } } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,state,res); } return flag; } public static void main(String[] args){ if (login()){ System.out.println("登录成功!"); }else{ System.out.println("无效的用户名密码"); } }
3.防止使用SQL注入,使用PreparedStatement,不实用拼接SQL,使用?占位符
/** * 检查登录是否成功,防止SQL注入 * @return */ public static boolean loginSQL(){ Map<String,String> map = readkeyBoard(); Connection conn =null; PreparedStatement pstate =null; ResultSet res = null; boolean flag = false; try { conn = jdbcUtil.getConnection(); //由于pstate对象需要把SQL预先编译到pstate对象中,所以我们需要提前准别一个SQL语句 //注意:SQL语句中所有需要设置的内容,不论是什么类型,统统用一个占位符?表示,?表示需要设置的内容 String sql = "SELECT * FROM users WHERE username =? AND password =? "; //把SQL语句编译到pstate对象中,并且返回pstate对象 pstate = conn.prepareStatement(sql); //设置占位符具体值,注意从1开始 pstate.setString(1,map.get("user")); pstate.setString(2,map.get("password")); //输出SQL语句 System.out.println(sql); //由于SQL语句已经预先被预编译到pstate对象中,所以pstate.executeQuery()时候已经不需要SQL语句了 res = pstate.executeQuery(); System.out.println(res); /** * 查询结果中有数据 */ if (res.next()){ flag=true; } //输出SQL语句 System.out.println(sql); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,res); } return flag; } public static void main(String[] args){ if (loginSQL()){ System.out.println("登录成功!"); }else{ System.out.println("无效的用户名密码"); } } }
四、其他案例
import com.csjin.edu.TestJDBC.Test02.util.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class Test04 { public static void main(String[] args){ //执行数据库DML test1(); //执行数据DQL test2(); } private static void test2() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try{ conn = jdbcUtil.getConnection(); String sql ="SELECT username,id,password FROM USERS where username=?"; pstate = conn.prepareStatement(sql); pstate.setString(1,"admin"); res = pstate.executeQuery(); while (res.next()){ String username = res.getString("username"); int id = res.getInt("id"); String password = res.getString("password"); System.out.println("username ="+username+",id="+id+",password="+password); } } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,res); } } private static void test1() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try { conn = jdbcUtil.getConnection(); String sql = "INSERT INTO USERS VALUES(?,?,?)"; pstate = conn.prepareStatement(sql); pstate.setInt(1,2); pstate.setString(2,"chu01"); pstate.setString(3,"123456"); int i = pstate.executeUpdate(); System.out.println("i="+i); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,null); } } }
五、使用PreparedStatement执行模糊查询
package com.csjin.edu.TestJDBC.Test02; import com.csjin.edu.TestJDBC.Test02.util.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 使用PreparedStatement执行模糊查询 * */ public class Test05 { public static void main(String[] args){ //使用PreparedStatement执行模糊查询 test1(); } private static void test1() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try{ conn = jdbcUtil.getConnection(); String sql ="SELECT username,id,password FROM USERS where username like ?"; pstate = conn.prepareStatement(sql); pstate.setString(1,"%dmi%"); res = pstate.executeQuery(); while (res.next()){ String username = res.getString("username"); int id = res.getInt("id"); String password = res.getString("password"); System.out.println("username ="+username+",id="+id+",password="+password); } } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,res); } } }
六、数据库事务与JDBC事务
/** * 数据库事物 * Mysql中默认的事物管理方式是自动提交事务,mysql默认每个DML语句都会引起一个单独的事务,这个DML语句执行结束的时候,事务会自动提交 * * 在数据库需要自动关闭事务: * 1、执行语句START TRANSACTION ,自动关闭事务,注意:这是本次关闭,在事务结束后,会恢复到默认自动提交 * 2、在事务开始后,只要没有结束事务,无论执行多少个DML语句,这些DML语句都是隶属于同一个事务,事务中对数据库修改保存在内存中 * 3、事务结束的两种方式: * 1、提交(COMMIT),结束事务,事务中对数据库的修改被永久的保存到数据库中 * 2、回滚(ROLLBACK)结束事务,事务中的对数据库的修改被放弃 * * * *在JDBC需要自动关闭事务: * 1、执行语句conn.setAutoCommit(false); ,自动关闭事务,注意:这是本次关闭,在事务结束后,会恢复到默认自动提交 * 2、在事务开始后,只要没有结束事务,无论执行多少个DML语句,这些DML语句都是隶属于同一个事务,事务中对数据库修改保存在内存中 * 3、事务结束的两种方式: * 1、提交(conn.commit()),结束事务,事务中对数据库的修改被永久的保存到数据库中 * 2、回滚(conn.rollback)结束事务,事务中的对数据库的修改被放弃 * */
import com.csjin.edu.TestJDBC.Test02.util.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 数据库事物 * Mysql中默认的事物管理方式是自动提交事务,mysql默认每个DML语句都会引起一个单独的事务,这个DML语句执行结束的时候,事务会自动提交 * * 在数据库需要自动关闭事务: * 1、执行语句START TRANSACTION ,自动关闭事务,注意:这是本次关闭,在事务结束后,会恢复到默认自动提交 * 2、在事务开始后,只要没有结束事务,无论执行多少个DML语句,这些DML语句都是隶属于同一个事务,事务中对数据库修改保存在内存中 * 3、事务结束的两种方式: * 1、提交(COMMIT),结束事务,事务中对数据库的修改被永久的保存到数据库中 * 2、回滚(ROLLBACK)结束事务,事务中的对数据库的修改被放弃 * * * *在JDBC需要自动关闭事务: * 1、执行语句conn.setAutoCommit(false); ,自动关闭事务,注意:这是本次关闭,在事务结束后,会恢复到默认自动提交 * 2、在事务开始后,只要没有结束事务,无论执行多少个DML语句,这些DML语句都是隶属于同一个事务,事务中对数据库修改保存在内存中 * 3、事务结束的两种方式: * 1、提交(conn.commit()),结束事务,事务中对数据库的修改被永久的保存到数据库中 * 2、回滚(conn.rollback)结束事务,事务中的对数据库的修改被放弃 * */ public class Test06 { public static void main(String[] args){ //非事务 test1(); //事务成功 test2(); //事务过程中失败,需手动执行回滚操作 test3(); } private static void test3() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try { conn = jdbcUtil.getConnection(); //开启事务,关闭自动提交 conn.setAutoCommit(false); String sql = "INSERT INTO users VALUES(?,?,?)"; pstate = conn.prepareStatement(sql); // 设置并执行第一个DML语句 pstate.setInt(1,2); pstate.setString(2,"chu02"); pstate.setString(3,"123456"); int i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第二个DML语句 pstate.setInt(1,3); pstate.setString(2,"chu03"); pstate.setString(3,"123456"); i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第三个DML语句 pstate.setInt(1,1); pstate.setString(2,"chu04"); pstate.setString(3,"12345611111111111111111111111111111111111111111111111111111111111111111111111"); i = pstate.executeUpdate(); System.out.println("i="+i); //提交事务 conn.commit(); } catch (SQLException e) { System.err.println("事务SQLException异常信息如下:"); e.printStackTrace(); //手动回滚 try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } catch (ClassNotFoundException e) { System.out.println("事务ClassNotFoundException异常信息如下:"); e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { jdbcUtil.closeDb(conn,pstate,null); } } private static void test2() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try { conn = jdbcUtil.getConnection(); //开启事务,关闭自动提交 conn.setAutoCommit(false); String sql = "INSERT INTO users VALUES(?,?,?)"; pstate = conn.prepareStatement(sql); // 设置并执行第一个DML语句 pstate.setInt(1,2); pstate.setString(2,"chu02"); pstate.setString(3,"123456"); int i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第二个DML语句 pstate.setInt(1,3); pstate.setString(2,"chu03"); pstate.setString(3,"123456"); i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第三个DML语句 pstate.setInt(1,4); pstate.setString(2,"chu04"); pstate.setString(3,"123456"); i = pstate.executeUpdate(); System.out.println("i="+i); //提交事务 conn.commit(); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,null); } } private static void test1() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try { conn = jdbcUtil.getConnection(); String sql = "INSERT INTO users VALUES(?,?,?)"; pstate = conn.prepareStatement(sql); // 设置并执行第一个DML语句,并且自动提交 pstate.setInt(1,2); pstate.setString(2,"chu02"); pstate.setString(3,"123456"); int i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第二个DML语句,并且自动提交 pstate.setInt(1,3); pstate.setString(2,"chu03"); pstate.setString(3,"123456"); i = pstate.executeUpdate(); System.out.println("i="+i); // 设置并执行第三个DML语句,并且自动提交 pstate.setInt(1,4); pstate.setString(2,"chu04"); pstate.setString(3,"123456"); i = pstate.executeUpdate(); System.out.println("i="+i); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { jdbcUtil.closeDb(conn,pstate,null); } } }
七、在数据库与JDBC中使用行级锁
/** * 在数据库中使用行级锁: * 1、执行数据库语句:start transaction ,关闭数据库自动提交 * 2、执行语句:select * from users where id in(1,2) for update; 引起事务,启动行级锁,并且锁住id=1,2行 * 注意:行级锁语句必须是主键作为查询条件,才能启动行级锁,否则是表级锁(数据库序列化的时候启动表) * 3、作用:启动行级锁只有当前事务才可以修改i=1,2这条记录,在当前事务中修改 * 4、commit,结束事务,释放行级锁 * */ /** * 在jdbc中使用行级锁: * 1、执行语句:conn.setAutoCommit(false),关闭数据库自动提交 * 2、执行语句:select * from users where id in(1,2) for update; 引起事务,启动行级锁,并且锁住id=1,2行 * 注意:行级锁语句必须是主键作为查询条件,才能启动行级锁,否则是表级锁(数据库序列化的时候启动表) * 3、作用:启动行级锁只有当前事务才可以修改i=1,2这条记录,在当前事务中修改 * 4、结束事务,释放行级锁 * */
示例代码:
import com.csjin.edu.TestJDBC.Test02.util.jdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * 在数据库中使用行级锁: * 1、执行数据库语句:start transaction ,关闭数据库自动提交 * 2、执行语句:select * from users where id in(1,2) for update; 引起事务,启动行级锁,并且锁住id=1,2行 * 注意:行级锁语句必须是主键作为查询条件,才能启动行级锁,否则是表级锁(数据库序列化的时候启动表) * 3、作用:启动行级锁只有当前事务才可以修改i=1,2这条记录,在当前事务中修改 * 4、commit,结束事务,释放行级锁 * */ /** * 在jdbc中使用行级锁: * 1、执行语句:conn.setAutoCommit(false),关闭数据库自动提交 * 2、执行语句:select * from users where id in(1,2) for update; 引起事务,启动行级锁,并且锁住id=1,2行 * 注意:行级锁语句必须是主键作为查询条件,才能启动行级锁,否则是表级锁(数据库序列化的时候启动表) * 3、作用:启动行级锁只有当前事务才可以修改i=1,2这条记录,在当前事务中修改 * 4、结束事务,释放行级锁 * */ public class Test07 { public static void main(String[] args){ test1(); } private static void test1() { Connection conn = null; PreparedStatement pstate = null; ResultSet res = null; try { conn = jdbcUtil.getConnection(); //开启事务,关闭自动提交 conn.setAutoCommit(false); String sql = "select * from users where id =? for update"; pstate = conn.prepareStatement(sql); pstate.setInt(1,2); pstate.executeQuery(); sql = "update users set password=1234567 where id=?"; pstate = conn.prepareStatement(sql); pstate.setInt(1,2); int i = pstate.executeUpdate(); System.out.println("i="+i); //提交事务 conn.commit(); } catch (SQLException e) { System.err.println("事务SQLException异常信息如下:"); e.printStackTrace(); //手动回滚 try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } catch (ClassNotFoundException e) { System.out.println("事务ClassNotFoundException异常信息如下:"); e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } finally { jdbcUtil.closeDb(conn,pstate,null); } } }
当有些人一出生就有的东西,我们要为之奋斗几十年才拥有。但有一样东西,你一辈子都不会有,那就是我们曾经一无所有。