目录
4、对比一下Statement和PreparedStatement?
Java DataBase Connectivity
在java语言中编写sql语句,对mysql数据库中的数据进行CRUD操作。
java.sql.*;
java.sql. * ;
这个包下都是JDBC的接口,SUN公司制定的!
JDBC是体现"接口作用"的非常经典的例子。
JDBC降低了耦合度,提高了扩展力。
对于java程序员来说,不需要关心数据库是哪个品牌。只要面向JDBC接口编程就行!
JDBC整个程序的结构当中有三波人????
第一波:SUN公司,负责制定JDBC接口。这些接口已经写好了,在java.sql.;
第二波:java.sql.*下面的所有接口都要有实现类,这些实现类是数据库厂家编写的。
我们连接的是mysql数据库,mysql数据库厂家的实现类在哪里呢?
mysql-connector-java-5.1.23-bin.jar
jar包中很多.class字节码文件,这是mysql数据库厂家写的接口实现!
注意:如果连接的是oracle数据库,你需要从网上下载oracle的jar包。
mysql-connector-java-5.1.23-bin.jar
这个jar包有一个专业的术语,大家记住就行:mysql的驱动。
如果是oracle的jar,被称为oracle的驱动。
第三波:我们java程序员,面向JDBC接口写代码就行!
mysql的驱动jar包,需要配置到classpath当中吗?
mysql-connector-java-5.1.23-bin.jar里是字节码,是class文件。
Java虚拟机的类加载器会去加载class文件,类加载器怎么能够找到这些class文件呢?
classpath没有配置的情况下,默认从当前路径下加载class。
classpath如果配置死了,例如:classpath-D:\abc,则表示固定只从d:\abc目录下找class
classpath=. ;D: \course\04-JDBC\resources \MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar
“ . ”代表什么?
当前路径。
以上的classpath什么意思?
类加载器从当前路径下加载class,如果当前路径下没找到,则去D:\course\04-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar找class文件。
jar包需要解压吗?
不需要解压,java虚拟机的类加载器有这个能力找到class文件。
- //这就是我们面向JDBC接口调用方法连接数据库
- //我们不需要关心底层是mysql还是oraole,还是DB2,还是sybase.
- //我们都不需要关心,因为JDBC接口的出现让程序降低了耦合度!!!
- //我们Java程序员写一套JDBC程序,可以连接你任何一个品牌的数据库。
- public class JavaProgranmer{
- public static void main(string[] args)(
- //面向JDBC接口写代码
- //JDBC jdbc = now Oracle() ;
- JDBC jdbc = new MySQL() ;
- //连接数据库
- //调用方法的时候,面向JDBC接口调用,JDBC接口中有什么方法就调什么方法。
- jdbc.getConnection();
- //...以下几百行代码都是面向jdbo接口调用的,不需要修改!
- //...
- }
- }
- import java.sql.DriverManager;
- import java.sql.Driver;
- import java.sql.SQLException;
-
- public class JDBCTest01{
- public static void main(String[] args){
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
- try{
- //1、注册驱动(通知java程序我们即将要连接的是哪个品牌的数据库)
- //com.mysql.jdbc.Driver是mysql数据库厂家写的,实现了java.sql.Driver接口。
- //如果是oracle数据库的话,类名就不一样了:oracle.jdbc.driver.oracleDriver
- Driver driver = new com.mysql.jdbc.Driver() ;//mysql的
- DriverManager.registerDriver(driver) ;
- //等同于DriverManager.registerDriver(new com.mysql.jdbc.Diver());
-
- // Draver driver = new oracle.jdbo.driver.OracleDriver();//oracle的
-
- //2、获取数据库连接
- String url = "jdbc:mysql://localhost:3306/bjpowernode";
- String user = "root";
- String password = "123456";
- conn = DriverManager.getConnection (url,user,password) ;
- //conn = DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
- //输出连接对象的内存地址
- // com.mysql.jdbo.JDBC4Connection02aaf7cc2
- // com.mysql.jdbc.JDBc4Connection类实现了java.sql.Connection接口。
- //实际上我们后续的开发不需要关心底层具体是哪个对象,因为面向接口编程。
- //System.out.println(conn);
-
- //3、获取数据库操作对象
- stmt = conn.createstatement() ;
- //System.out.println(stmt);
-
- //通过一个连接对象connection是可以创建多个statement对象的
- //statement stmt2 = conn.createstatement();
- //System.out.println(stmt2);
-
- //4、执行SQL语句
- // insert delete update
- /* insert
- string insertsql="insert into dept(deptno,dname,loc)values(50,'销售部','北京')";
- // Statement接口中的executeUpdate方法专门来执行DML语句的。
- //该方法的返回值表示:影响了数据库表中的总记录条数!
- int count = stmt.executeUpdate (insertSql);
- System.out.println(count); // 1
- */
- /* updata
- string updatesql="update dept set dname='人事部',loc='天津' where deptno=50";
- int count = stmt.executeUpdate (updateSql) ;
- System.out.println(count); // 1
- */
- // delete
- String deleteSql = "delete from dept where deptno = 50";
- int count = stmt.executeUpdate (deleteSql);
- System.out.println(count);
-
- // 执行查询语句
- //JFBC当中的sql语句不需要以";"结尾
- String sql = "select empno,ename,sal from emp order by sal deso";
- // 执行查询语句是这个方法:executeQuery
- // Resultset就是查询结果集对象,查询的结果都在这个对象当中。
- rs = stmt.executeQuery (sql);
- //5、处理查询结果集
- //目前没什么好处理的,直接把结果集中的数据遍历输出吧。
-
- //调用ResultSet接口中相应的方法来遍历结果集
- boolean has=rs.next();//光标向前移动一位
- while(rs.next()){
- //条件成立表示光标指向的行有记录
- //取当前行的第1个值
- String empno=rs.getstring(1);//注意:getstring()这个方法是不管底层数据
- //取当前行的第2个值
- String ename = rs.getString(2);
- //取当前行的第3个信~
- String sal = rs.getstring(3);
- System.out.println (empno + "," + ename + "," + sal);
- //也可以用特定类型取出
-
- //根据查询结果的列名可以取吗?
- //以后这种方式是常用的,健壮
- int empno = rs.getInt("empno");
- String ename = rs.getString("ename");
- double sal = rs.getDouble("sal");
- System.out.println (empno + "," + ename + "," + (sal + 100));
-
- }
- }catch(SQLException e){
- e.printStackTrace();
- }finally{
- // 释放资源
- // 6、先关闭ResultSet,先释放statement,再释放connection
- //分别进行try..catch处理
- //放到finally中关闭
- 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();
- }
- }
- }
- }
- }
任何一个URL都包括:协议+IP地址+端口号port+资源名
http://192.168.100.2:8888/abc
协议:
是一个提前规定好的数据传输格式。通信协议有很多:http、https.
在传送数据之前,提前先商量好数据传送的格式。
这样对方接收到数据之后,就会按照这个格式去解析,拿到有价值的数据。
IP地址:
网络当中定位某台计算机的。
PORT端口号:
定位这台计算机上某个服务的。
资源名:
这个服务下的某个资源。
- /*
-
- jdbc:mysql:// 这是java程序和mysql通信的协议。
- localhost 这是本机IP地址,本机IP地址还可以写成:127.0.0.1
- 3306 mysql数据库端口号
- bjpowernode mysql数据库的名称
-
- jdbc:mysq1://192.168.111.123:3306/abc
-
- 如果是oracle数据库的话:
- oracle: jdbc:thin:0localhost:1521:bjpowernode
- oracle:jdbc:thin:0 这是oracle和java的通信协议
- localhost 这是本机IP地址。
- 1521 oracle默认端口
- bjpowernode oracle中数据库的名字
-
- 1ocalhost和127.0.0.1都是本机IP地址。死记硬背。
- */
- package com.bjpowernode.jdbc;
- /*
- 注册驱动的第二种方式:类加载注册
- mysql的厂家写的类:
- class com.mysql.jdbc.Dniver {
- static {
- try {
- java.sql.DriverManager.registerDriver(new Driver());
- } catch (SQLException E) {
- throw new RuntimeException("Can't register driver!");
- }
- }
- }
- */
- public class JDBCTest04 {
- public static void main(String[] args) {
- try {
- //1、注册驱动
- Class.forName("com.mysq1.jdbc.Driver");
- //2、获取连接
- conn = DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
- //3、获取数据库对象
- stmt = conn.createStatement();
- //4、执行SQL
- String sql = "select e.ename,d.dname from emp e join dept d on e.deptno = d.deptno";
- rs = stmt.executeQuery(sql);
- //5、处理查询结果集
- while(rs.next()){
- String ename = rs.getString("ename");
- String dname = rs.getString("dname");
- System.out.println(ename+","+dname);
-
- }
- } catch (ClassNotFoundException 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();
- }
- }
- }
- }
- }
思想:
将连接数据库的可变化的4条信息写到配置文件中。
以后想连接其他数据库的时候,可以直接修改配置文件,不用修改java程序。
四个信息是什么?
driver
url
user
password
src下新建一个Package名叫“resources”,新建一个fille叫"db.properties"
- #######mysql connectivity configuration###############
- driver=com.mysql.jdbc.Driver
- url=jdbc:mysql://localhost:3306/bjpowernode
- user=root
- password=123456
-
- #######oracle connectivity configuration###############
- driver=oracle.jdbc.driver.OracleDriver
- url=jdbc:oracle:thin:@localhost:1521:bjpowernode
- user=scott
- password=tiger
- //资源绑定器
- ResourceBundle bundle = ResourceBundle.getBundle("resources/db");
- //通过属性配置文件拿到信息
- String driver = bundle.getString( key: "driver");
- String url = bundle.getString( key: "url");
- String user = bundle.getString( key:"user");
- String password = bundle.getString( key: "password");
-
ResultSet 类对象 rs 中当中有一个next()方法,返回值为boolean类型,表示光标指向的行有数据,如果只是一行数据可以用 if 进行判断,如果是多行数据,可以用while循环。
- boolean flag1 = rs.next();
- System.out.println(flag1); // true
- //单行
- if(rs.next()){
- String empno = rs.getString(1);
- String ename = rs.getString(2);
- String sal = rs.getString(3);
- System.out.println(empno + "," + ename + "," + sal);
- }
- //多行
- while(rs.next()){
- String empno = rs.getString(1);
- String ename = rs.getString(2);
- String sal = rs.getString(3);
- System.out.println(empno + "," + ename + "," + sal);
- }
ResultSet 类对象 rs 中当中有getString()、geitInt()等方法。
getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。int同理
- int empno = rs.getInt(1);
- String ename = rs.getString(2);
- double sal = rs.getDouble(3);
- System.out.println(empno + "," + ename + "," + (sal + 100));
getString()内的参数情况:
**第一种:**该行记录的第几个数据,注意 JDBC中所有下标从1开始。不是从0开始。
- if(flag1){
- // 光标指向的行有数据
- // 取数据
- // getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。
- String empno = rs.getString(1); // JDBC中所有下标从1开始。不是从0开始。
- String ename = rs.getString(2);
- String sal = rs.getString(3);
- System.out.println(empno + "," + ename + "," + sal);
- }
**第二种:**以列的名字获取,注意:列名称不是表中的列名称,是查询结果集的列名称。
- String empno = rs.getString("a");
- String ename = rs.getString("ename");
- String sal = rs.getString("sal");
- System.out.println(empno + "," + ename + "," + sal);
1、需求:
模拟用户登录功能的实现。
2、业务描述:
程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码,
用户输入用户名和密码之后,提交信息,java程序收集到用户信息。
Java程序连接数据库验证用户名和密码是否合法。
合法:显示登录成功
不合法:显示登录失败
3、数据的准备:
在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
使用PD工具来进行数据库表的设计。(参见user-login.sql脚本)
- package com.bjpowernode.jdbc;
-
- import java.sql.*;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Scanner;
-
- public class JDBCTest06 {
- public static void main(String[] args) {
- // 初始化一个界面
- Map<String,String> userLoginInfo = initUI();
- // 验证用户名和密码
- boolean loginSuccess = login(userLoginInfo);
- // 最后输出结果
- System.out.println(loginSuccess ? "登录成功" : "登录失败");
- }
- private static boolean login(Map<String, String> userLoginInfo) {
- // 打标记的意识
- boolean loginSuccess = false;
- // 单独定义变量
- String loginName = userLoginInfo.get("loginName");
- String loginPwd = userLoginInfo.get("loginPwd");
-
- // JDBC代码
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
-
- try {
- // 1、注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 2、获取连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
- // 3、获取数据库操作对象
- stmt = conn.createStatement();
- // 4、执行sql
- String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'"; //fdsa' or '1'='1
- // 以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译。
- // 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
- rs = stmt.executeQuery(sql);
- // 5、处理结果集
- if(rs.next()){
- // 登录成功
- loginSuccess = true;
- }
- } catch (Exception 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();
- }
- }
- }
- return loginSuccess;
- }
-
- }
用户输入的用户名和密码等登录信息
- private static Map<String, String> initUI() {
- Scanner s = new Scanner(System.in);
-
- System.out.print("用户名:");
- String loginName = s.nextLine();
-
- System.out.print("密码:");
- String loginPwd = s.nextLine();
-
- Map<String,String> userLoginInfo = new HashMap<>();
- userLoginInfo.put("loginName", loginName);
- userLoginInfo.put("loginPwd", loginPwd);
-
- return userLoginInfo;
- }
用户名:fdsa
密码:fdsa’ or ‘1’='1
登录成功
这种现象被称为SQL注入(安全隐患)。(黑客经常使用)
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。
只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
用户名:fdas
密码:fdsa’ or ‘1’='1
登录失败
用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译。不起作用。
综上所述:PreparedStatement使用较多。只有极少数的情况下需要使用Statement(实现字符串拼接)
业务方面要求必须支持SQL注入的时候。
Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
- // 3、获取预编译的数据库操作对象
- // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”
- //注意:占位符不能使用单引号括起来。
- String sql = "select * from t_user where loginName = ? and loginPwd = ?";
- // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
- ps = conn.prepareStatement(sql);
- // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
- ps.setString(1, loginName);
- ps.setString(2, loginPwd);
- // 4、执行sql
- rs = ps.executeQuery();
- package com.bjpowernode.jdbc;
-
- import java.sql.*;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Scanner;
-
- public class JDBCTest07 {
- public static void main(String[] args) {
- // 初始化一个界面
- Map<String,String> userLoginInfo = initUI();
- // 验证用户名和密码
- boolean loginSuccess = login(userLoginInfo);
- // 最后输出结果
- System.out.println(loginSuccess ? "登录成功" : "登录失败");
- }
-
- /**
- * 用户登录
- * @param userLoginInfo 用户登录信息
- * @return false表示失败,true表示成功
- */
- private static boolean login(Map<String, String> userLoginInfo) {
- // 打标记的意识
- boolean loginSuccess = false;
- // 单独定义变量
- String loginName = userLoginInfo.get("loginName");
- String loginPwd = userLoginInfo.get("loginPwd");
-
- // JDBC代码
- Connection conn = null;
- PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
- ResultSet rs = null;
-
- try {
- // 1、注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 2、获取连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
-
- --------------------------------------重点-------------------------------------------------
- // 3、获取预编译的数据库操作对象
- // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”
- //注意:占位符不能使用单引号括起来。
- String sql = "select * from t_user where loginName = ? and loginPwd = ?";
- // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
- ps = conn.prepareStatement(sql);
- // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
- ps.setString(1, loginName);
- ps.setString(2, loginPwd);
- // 4、执行sql
- rs = ps.executeQuery();
-
- --------------------------------------重点-------------------------------------------------
- // 5、处理结果集
- if(rs.next()){
- // 登录成功
- loginSuccess = true;
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 6、释放资源
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (ps != null) {
- try {
- ps.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (conn != null) {
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- return loginSuccess;
- }
-
- /**
- * 初始化用户界面
- * @return 用户输入的用户名和密码等登录信息
- */
- private static Map<String, String> initUI() {
- Scanner s = new Scanner(System.in);
-
- System.out.print("用户名:");
- String loginName = s.nextLine();
-
- System.out.print("密码:");
- String loginPwd = s.nextLine();
-
- Map<String,String> userLoginInfo = new HashMap<>();
- userLoginInfo.put("loginName", loginName);
- userLoginInfo.put("loginPwd", loginPwd);
-
- return userLoginInfo;
- }
- }
-
- // 获取预编译的数据库操作对象
- String sql = "select ename from emp order by ename ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, keyWords);
如果是上述的这种方式就不行了,传进去的是字符串,是值,带单引号!!!
String sql = "select ename from emp order by ename 'esc' ";
这种形式,会报错
String sql = "select ename from emp order by ename " + keyWords;
只能采取上述这种方式,会导致sql注入,但是可以采用不让用户输入的方式实现该功能
- package com.bjpowernode.jdbc;
-
- import java.sql.*;
- import java.util.Scanner;
-
- public class JDBCTest08 {
- public static void main(String[] args) {
-
- // 用户在控制台输入desc就是降序,输入asc就是升序
- Scanner s = new Scanner(System.in);
- System.out.println("输入desc或asc,desc表示降序,asc表示升序");
- System.out.print("请输入:");
- String keyWords = s.nextLine();
-
- // 执行SQL
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
- try {
- // 注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 获取连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
- // 获取数据库操作对象
- stmt = conn.createStatement();
- // 执行SQL
- String sql = "select ename from emp order by ename " + keyWords;
- rs = stmt.executeQuery(sql);
- // 遍历结果集
- while(rs.next()){
- System.out.println(rs.getString("ename"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- 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();
- }
- }
- }
-
- }
- }
-
增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)。简称CRUD
- //增
- String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
- ps = conn.prepareStatement(sql);
- ps.setInt(1, 60);
- ps.setString(2, "销售部");
- ps.setString(3, "上海");
-
- //改
- String sql = "update dept set dname = ?, loc = ? where deptno = ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, "研发一部");
- ps.setString(2, "北京");
- ps.setInt(3, 60);
-
- //删
- String sql = "delete from dept where deptno = ?";
- ps = conn.prepareStatement(sql);
- ps.setInt(1, 60);
- package com.bjpowernode.jdbc;
-
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
-
- public class JDBCTest09 {
- public static void main(String[] args) {
- Connection conn = null;
- PreparedStatement ps = null;
- try {
- // 1、注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 2、获取连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
- // 3、获取预编译的数据库操作对象
-
- //增
- String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
- ps = conn.prepareStatement(sql);
- ps.setInt(1, 60);
- ps.setString(2, "销售部");
- ps.setString(3, "上海");
-
- //改
- String sql = "update dept set dname = ?, loc = ? where deptno = ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, "研发一部");
- ps.setString(2, "北京");
- ps.setInt(3, 60);
-
- //删
- String sql = "delete from dept where deptno = ?";
- ps = conn.prepareStatement(sql);
- ps.setInt(1, 60);
-
- // 4、执行SQL
- int count = ps.executeUpdate();
- System.out.println(count);
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 6、释放资源
- if (ps != null) {
- try {
- ps.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (conn != null) {
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
-
- conn.setAutoCommit(false);
- conn.commit();
- conn.rollback();
- package com.bjpowernode.jdbc;
-
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.SQLException;
-
- /**
- *
- * sql脚本:
- * drop table if exists t_act;
- * create table t_act(
- * actno int,
- * balance double(7,2) // 注意:7表示有效数字的个数,2表示小数位的个数。
- * );
- * insert into t_act(actno,balance) values(111,20000);
- * insert into t_act(actno,balance) values(222,0);
- * commit;
- * select * from t_act;
- *
- */
- public class JDBCTest11 {
- public static void main(String[] args) {
- Connection conn = null;
- PreparedStatement ps = null;
- try {
- // 1、注册驱动
- Class.forName("com.mysql.jdbc.Driver");
- // 2、获取连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
- // 将自动提交机制修改为手动提交
- conn.setAutoCommit(false); // 开启事务
-
- // 3、获取预编译的数据库操作对象
- String sql = "update t_act set balance = ? where actno = ?";
- ps = conn.prepareStatement(sql);
-
- // 给?传值
- ps.setDouble(1, 10000);
- ps.setInt(2, 111);
- int count = ps.executeUpdate();
-
- //String s = null;
- //s.toString();
-
- // 给?传值
- ps.setDouble(1, 10000);
- ps.setInt(2, 222);
- count += ps.executeUpdate();
-
- System.out.println(count == 2 ? "转账成功" : "转账失败");
-
- // 程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
- conn.commit(); // 提交事务
-
- } catch (Exception e) {
- // 回滚事务
- if(conn != null){
- try {
- conn.rollback();
- } catch (SQLException e1) {
- e1.printStackTrace();
- }
- }
- e.printStackTrace();
- } finally {
- // 6、释放资源
- if (ps != null) {
- try {
- ps.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if (conn != null) {
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
-
JDBC工具类,简化JDBC编程。
- package com.bjpowernode.jdbc.utils;
-
- import java.sql.*;
-
- public class DBUtil {
-
- /**
- * 工具类中的构造方法都是私有的。
- * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
- */
- private DBUtil() {
- }
-
- // 静态代码块在类加载时执行,并且只执行一次。
- static {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-
-
- /**
- * 获取数据库连接对象
- *
- * @return 连接对象
- * @throws SQLException
- */
- public static Connection getConnection() throws SQLException {
- return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
- }
-
- /**
- * 关闭资源
- * @param conn 连接对象
- * @param ps 数据库操作对象
- * @param rs 结果集
- */
- public static void close(Connection conn, Statement ps, ResultSet rs){
- if(rs != null){
- try {
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if(ps != null){
- try {
- ps.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- if(conn != null){
- try {
- conn.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
-
- String sql = "select ename from emp where ename like ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, "_A%");
以下为错误写法:
String sql = "select ename from emp where ename like '_?%'";
完整代码
- package com.bjpowernode.jdbc;
-
- import com.bjpowernode.jdbc.utils.DBUtil;
-
- import java.sql.Connection;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
-
- public class JDBCTest12 {
- public static void main(String[] args) {
- Connection conn = null;
- PreparedStatement ps = null;
- ResultSet rs = null;
- try {
- // 获取连接
- conn = DBUtil.getConnection();
- // 获取预编译的数据库操作对象
-
- // 错误的写法
- /*
- String sql = "select ename from emp where ename like '_?%'";
- ps = conn.prepareStatement(sql);
- ps.setString(1, "A");
- */
-
- String sql = "select ename from emp where ename like ?";
- ps = conn.prepareStatement(sql);
- ps.setString(1, "_A%");
- rs = ps.executeQuery();
- while(rs.next()){
- System.out.println(rs.getString("ename"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally{
- // 释放资源
- DBUtil.close(conn, ps, rs);
- }
-
- }
- }
-
悲观锁:又称为行级锁 for update事务必须排队执行。数据锁住了,不允许井发。(行级锁:select后面添加for update)
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。
事务1–>读取到版本号1.1
事务2—>读取到版本号1.1
其中事务1先修改了,修改之后看了版本号是1.1,于是提交修改的数据,将版本号修改为1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2,和它最初读的版本号不一致。回滚。