我是靠谱客的博主 失眠早晨,最近开发中收集的这篇文章主要介绍JDBC连接数据库,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

开发前准备

使用IDEA开发

新建一个空工程Project , 在当前工程下新建一个模块Module , 打开模块设置(Open Moduel seting), 为当前模块添加jar包(Libraries->“+”->“java”)

  • 也可以直接在在项目下创建一个文件夹比如 libs , 将 mysql.jar 拷贝到该目录下,点击 add to project …加入到项目中

mysql8 和 mysql5 的包路径不同

  • mysql5:com.mysql.jdbc.Driver
  • mysql8:com.mysql.cj.jdbc.Driver

使用文本编辑器开发

JDBC开发前的准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中

#"."表示先从当前路径下加载类,如果没有再从指定路径加载
classpath=.;D:course6-JDBCresourcesMySql Connector Java 5.1.23mysql-connector-java-5.1.23-bin.jar

数据库的url分析(本质 socket 连接 )

jdbc:mysql://localhost:3306/hsp_db02

  • jdbc:mysql://: 规定好的表示协议,通过 jdbc 的方式连接 mysql
  • localhost: ip 地址 , localhost和127.0.0.1都是本机IP地址
  • 3306: 表示mysql数据库端口号
  • hsp_db02: 连接到 mysql dbms 中的具体的数据库实例名

oracle的URL:jdbc:oracle:thin:@localhost:1521:orcl

JDBC本质

概述

Java DataBase Connectivity(Java语言连接数据库) , 是SUN公司制定的一套接口(interface) , 在 java.sql.*包下

  • 面向接口编程(多态): 面向接口调用、面向接口写实现类,降低程序的耦合度,提高程序的扩展力

因为各种数据库的底层实现原理都不一样 , 所以SUN制定了一套JDBC接口

在这里插入图片描述

模拟JDBC本质

接口及其实现类

//SUN公司负责制定这套JDBC接口
public interface JDBC{
	//连接数据库的方法
	void getConnection();
}

//MySQL的数据库厂家负责编写JDBC接口的实现类(mysql驱动)
public class MySQL implements JDBC{

	public void getConnection(){
		// 具体这里的代码怎么写,对于我们Java程序员来说没关系
		// 这段代码涉及到mysql底层数据库的实现原理。
		System.out.println("连接MYSQL数据库成功!");
	}
}


//Oracle的数据库厂家负责编写JDBC接口的实现类(oracle驱动)
public class Oracle implements JDBC{

	public void getConnection(){
		System.out.println("连接Oracle数据库成功!");
	}
}

Java程序员不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码

import java.util.*;
public class JavaProgrammer
{
	public static void main(String[] args) throws Exception{
		// JDBC jdbc = new MySQL();
		// JDBC jdbc = new Oracle();

		// 创建对象可以通过反射机制
		ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
		String className = bundle.getString("className");
		Class c = Class.forName(className);
		JDBC jdbc = (JDBC)c.newInstance();

		// 以下代码都是面向接口调用方法,不需要修改
		jdbc.getConnection();
	}
}

连接数据库的方式

JDBC编程六步

  • 第一步:注册驱动 , 加载Driver类时会完成驱动的创建和注册(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
  • 获取连接对象(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道)
  • 获取数据库操作对象(专门执行sql语句的对象)
  • 利用数据库操作对象执行SQL语句(DQL DML…)
  • 处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集)
  • 释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭)

创建数据库实现的driver对象

创建各大数据库厂商实现的java.sql.Driver接口的对象 , 利用driver对象的connect方法创建连接对象

方法名作用
Connection connect(url, properties)获取连接对象, properties中封装了用户名和密码

静态创建driver对象 , 包名是com.mysql.jdbc

@Test
public void connect01() throws SQLException {
    //创建driver对象,包名是com.mysql.jdbc
    Driver driver = new com.mysql.jdbc.Driver(); //多态,父类型引用指向子类型对象
    // Driver driver = new oracle.jdbc.driver.OracleDriver(); // oracle的驱动
    String url = "jdbc:mysql://localhost:3306/hsp_db02";
    
    //将用户名和密码放入到 Properties 对象
    Properties properties = new Properties();
   
    //说明 user 和 password 是规定好的键,后面的值根据实际情况写
    properties.setProperty("user", "root");// 用户名
    properties.setProperty("password", "123456"); //密码
    Connection connect = driver.connect(url, properties);
    
    // com.mysql.jdbc.JDBC4Connection@41cf53f9
    //Connection connect = new com.mysql.jdbc.JDBC4Connection();//多态
	System.out.println("数据库连接对象 = " + connect);
}

**利用反射机制动态创建driver对象 ,包名是com.mysql.jdbc **

@Test
public void connect02() throws ClassNotFoundException, IllegalAccessException, InstantiationException, SQLException {
    //使用反射加载 Driver 类 , 动态加载,更加的灵活,减少依赖性
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    //多态,因为com.mysql.jdbc.Driver实现了java.sql.Driver,所以可以强制
    Driver driver = (Driver)aClass.newInstance();
    String url = "jdbc:mysql://localhost:3306/hsp_db02";
    
    //将用户名和密码放入到 Properties 对象
    Properties properties = new Properties();
    
    //说明 user 和 password 是规定好的键,后面的值根据实际情况写
    properties.setProperty("user", "root");// 用户
    properties.setProperty("password", "hsp"); //密码
    Connection connect = driver.connect(url, properties);
    System.out.println(connect);
}

使用驱动管理类管理driver对象

将创建的 driver 对象注册到 DriverManager 中进行管理 , 对 driver 对象的方法进行扩展

方法名功能
static Connection getConnection(url,uesr,pwd)直接传入参数信息, 获取连接对象(底层干活的还是 driver 对象)
static void registerDriver(driver)注册驱动交给DriverManager管理
@Test
public void connect03() throws IllegalAccessException, InstantiationException, ClassNotFoundException, SQLException {
    //使用反射加载 Driver
    Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
    Driver driver = (Driver) aClass.newInstance();
    
    //创建 url 和 user 和 password
    String url = "jdbc:mysql://localhost:3306/hsp_db02";
    String user = "root";
    String password = "hsp";
    
    //注册 Driver 驱动
    DriverManager.registerDriver(driver);
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println("第三种方式=" + connection);
}

加载com.mysql.jdbc.Driver

Class.forName 加载 Driver 类时 , 会执行一次静态代码块中的代码自动完成驱动的创建与注册

  • 也可以无需显示的调用 Class.forName(“com.mysql.jdbc.Driver”) , mysql中默认就会加载 , 但是写上更加明确

com.mysql.jdbc.Driver源码

//当加载完driver类时 , 驱动注册的工作就完成了
static {
    try {
        //完成了 driver 对象的创建与注册
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}
@Test
public void connect04() throws ClassNotFoundException, SQLException {
    //使用反射机制加载 Driver 类 , 在类加载期间底层自己完成了 driver 对象的创建与注册
    //以下方法不需要接收返回值,因为我们只想用它的类加载动作
    Class.forName("com.mysql.jdbc.Driver");
   
    //连接数据库
    String url = "jdbc:mysql://localhost:3306/hsp_db02";
    String user = "root";
    String password = "hsp";
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);
}

创建属性资源文件

将数据库的连接信息写入到 properties 配置文件 , 连接数据库更加灵活

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.151.9:3306/bjpowernode
user=root
password=981127
@Test
public void connect05() throws IOException, ClassNotFoundException, SQLException {
    //通过 Properties 对象获取配置文件的信息
    Properties properties = new Properties();
    properties.load(new FileInputStream("src\mysql.properties"));
    
    // 使用资源绑定器绑定属性配置文件,
    ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
    String driver = bundle.getString("driver");
    String url = bundle.getString("url");
    String user = bundle.getString("user");
    String password = bundle.getString("password");
    
    //获取相关的值
    String user = properties.getProperty("user");
    String password = properties.getProperty("password");
    String driver = properties.getProperty("driver");
    String url = properties.getProperty("url");
    //连接数据库
    Class.forName(driver);
    Connection connection = DriverManager.getConnection(url, user, password);
    System.out.println(connection);
}

代码基本骨架和释放资源

在finally语句块中关闭资源保证资源一定释放

  • 并且要遵循从小到大依次关闭 , 从里到外即先开启的后关闭
  • 分别对其try…catch , 如果一起try,第一个出现问题就直接进入catch语句块了, 导致后面的资源没有关闭
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;

public class JDBCTest01{
	public static void main(String[] args){
		Connection conn = null;
        //PreparedStatement ps = null;
		Statement stmt = null;
        ResultSet rs = null;
		try{
			//1、注册驱动
			//2、获取连接
			//3、获取数据库操作对象(Statement专门执行sql语句的)
			//4、执行sql
			//5、处理查询结果集
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			//6、释放资源
            if(rs != null){
				try{
					rs.close();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
			try{
				if(stmt != null){
					stmt.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
			try{
				if(conn != null){
					conn.close();
				}
			}catch(SQLException e){
				e.printStackTrace();
			}
		}
	}
}

封装 JDBCUtils 工具类

JDBC工具类,简化JDBC编程 , 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用

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 {
        //执行这行代码可能会出现异常,将其抛出,在JDBC代码中捕捉
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
    }

    /**
     * 关闭资源,由小到大 ,如果需要关闭哪个资源就传入对应的对象,不需要关闭就传入null
     * @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();
            }
        }
    }

}

使用配置文件的方式

在实际开发中,我们可以将编译异常转成运行异常 , 调用者可以选择捕获该异常,也可以选择默认处理该异常,比较方便

//定义相关的属性(4 个), 因为只需要一份可以使用static修饰
private static String user; //用户名
private static String password; //密码
private static String url; //url
private static String driver; //驱动名

//工具类中的构造方法都是私有的,防止new对象
private DBUtil() {
}

//在 static 代码块去初始化
static {
    try {
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\mysql.properties"));
        //读取相关的属性值
        user = properties.getProperty("user");
        password = properties.getProperty("password");
        url = properties.getProperty("url");
        driver = properties.getProperty("driver");
        Class.forName(driver);
    } catch (IOException e) {
        //将编译异常转成运行异常
        throw new RuntimeException(e);
    }
}

//连接数据库, 返回 Connection 对象
public static Connection getConnection() {
    try {
        return DriverManager.getConnection(url, user, password);
    } catch (SQLException e) {
        //将编译异常转成运行异常
        throw new RuntimeException(e);
    }
}

使用工具类后的基本骨架

public class JDBCTest12 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 获取连接
            conn = DBUtil.getConnection();
            // 获取预编译的数据库操作对象
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
            // 释放资源
            DBUtil.close(conn, ps, rs);
        }

    }
}

数据库操作对象和结果集

Connection接口中的方法

方法名功能
Statement createStatement()创建Statement对象
PreparedStatement prepareStatement(sql)创建预处理对象

Statement

使用连接对象的createStatement()方法获取数据库操作对象 , JDBC中的sql语句不需要提供分号结尾

方法名功能
int executeUpdate(insert/delete/update)执行dml语句(增删改) , 返回受影响的行数
ResultSet executeQuery(select)执行dql语句(查询) , 返回 ResultSet 结果集对象
Connection conn = null;
Statement stmt = null;
//1、注册驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//3、获取数据库操作对象
stmt = conn.createStatement();
//4、执行SQL语句	
// String sql = "delete from dept where deptno = 40";
String sql = "update dept set dname = '销售部', loc = '天津' where deptno = 20";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "修改成功" : "修改失败");

SQL注入的优缺点

SQL注入问题: 在对sql语句拼接时,将用户提供的“非法信息”编译到要执行的sql语句当中 , 导致了原sql语句的含义被扭曲了

  • 如用户名:zhangsan , 密码:123456’ or ‘1’='1(两个用or连接的条件去掉两边的引号)
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
//执行sql语句拼接时,将用户提供的“非法信息sql语句关键字”编译到要执行的sql语句当中 , 导致了原sql语句的含义被扭曲了
//select * from t_user where loginName = 'zhangsan' and loginPwd = '123456' or '1'='1';
//字符串拼接变量或表达式的方法加双引号中间加两个"+"号, 两个加号中间加表达式
String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";

//完成了sql语句的拼接,发送sql语句给DBMS,DBMS进行sql编译
rs = stmt.executeQuery(sql);

SQL注入的用途: 凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement

  • 用户在控制台输入desc就是降序,输入asc就是升序
// 用户在控制台输入desc就是降序,输入asc就是升序
Scanner s = new Scanner(System.in);
System.out.println("输入desc或asc,desc表示降序,asc表示升序");
System.out.print("请输入:");
String keyWords = s.nextLine();

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;

// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
// 获取数据库操作对象
stmt = conn.createStatement();
// 执行SQL
// 使用prepareStatement传值后的结果 ,select ename from emp order by ename 'desc/asc'(sql语法错误)
// ps = conn.prepareStatement(sql);
// ps.setString(1, keyWords);

//使用Statement进行sql语句拼接的不会出现问题
String sql = "select ename from emp order by ename " + keyWords;
rs = stmt.executeQuery(sql);
// 遍历结果集
while(rs.next()){
    System.out.println(rs.getString("ename"));
}

ResultSet[查询结果集]

ResultSet表示数据库结果集的数据表 , 通常通过执行查询数据库的语句生成 , ResultSet对象保持一个光标指向当前的数据行(最初光标位于第一行之前)

在这里插入图片描述

ResulrSet结果集常用方法

方法名功能
boolean next()执行 next 方法会让光标向下一行移动,如果没有下一行,返回 false(最初光标位于第一行之前)
boolean previous()向上移动一行 , 如果没有上一行则返回false
String getString(结果集中列的索引/结果集中列的字段名)不管数据库中的数据类型是什么,都以String的形式取出 , 下标从1开始
Date getDate(结果集中列的索引/结果集中列的字段名)以Date的形式取出字段的值
int getInt(结果集中列的索引/结果集中列的字段名)以int的形式取出字段的值
Xxx getXxx( 结果集中列的索引/结果集中列的字段名 )以指定的数据类型取出结果集中的数据 , 前提是该数据类型可以正常转换
Object getObject( 列的索引/列的字段名 )以对象的形式取出结果集中的数据
String sql = "select empno as a,ename,sal from emp";
// 专门执行DQL语句的方法
rs = stmt.executeQuery(sql);//处理查询结果集
while(rs.next()){
	// 以结果集中列的下标获取 , JDBC中所有下标从1开始,不是从0开始
	String empno = rs.getString(1);
	String ename = rs.getString(2);
	String sal = rs.getString(3);
	System.out.println(empno + "," + ename + "," + sal);


	// 以结果集中列的名字获取语义更明确 , 列名称不是表中的列名称,是查询结果集的列名称
	// 除了可以以String类型取出之外,还可以以特定的类型取出,方便使用
	int empno = rs.getInt("a");
	String ename = rs.getString("ename");
	double sal = rs.getDouble("sal");
	System.out.println(empno + "," + ename + "," + (sal + 200));
}

PreparedStatement

PreparedStatement接口继承了java.sql.Statement , 通过connection对象的prepareStatement(sql)方法获取 , 是预编译的数据库操作对象 , 可解决SQL注入问题

  • PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”
  • 用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译过程 , 只是被当作普通的字符不起作用

PreparedStatement对象使用细节

  • 预编译的SQL语句的参数用 ? 表示 , 一个?表示一个占位符(不能使用单引号括起来),调用ps对象的setXxx()方法设置占位符的"值"
  • 一个PreparedStatement对象每次只能预编译一条SQL语句
  • PreparedStatement会在编译阶段做类型的安全检查

数据库中SQL的执行原理: 执行第一次时一定会对SQL语句进行编译, 如果第二次执行时sql语句没有任何变化,则直接执行不再编译

  • Statement是编译一次执行一次,每次执行sql语句都可能会发生变化
  • PreparedStatement每次传值是在sql语句编译之后,所以同一个sql模板编译一次可执行N次 , 效率较高一些

PreparedStatement接口中的方法

  • 预处理对象的方法参数中不能再写sql语句 ,否则就会重新编译sql语句
方法名功能
int executeUpdate()执行dml语句(增删改) , 返回受影响的行数
ResultSet executeQuery()执行dql语句(查询) , 返回 ResultSet 结果集对象
execute()执行任意的sql , 返回布尔值
void setXxx(占位符索引 , 占位符的值)给占位符设置对应类型的值 , 占位符下标从1开始
void setString(占位符索引 , 占位符的值)给占位符设置的值在sql语句中被当作字符处理(自动加引号)
void setInt(占位符索引 , 占位符的值)给占位符设置的值在sql语句中被当作int类型的数据处理(不会加引号)
// 获取预编译的数据库操作对象
// 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);
// 执行sql,ps已经预编译过了sql语句,如果再传就会重新编译sql语句
rs = ps.executeQuery();
// 处理结果集
if(rs.next()){
    // 登录成功
    loginSuccess = true;
}
//执行增删改查,一个ps对象只能预编译一条sql
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 = ?";
ps2 = conn.prepareStatement(sql);
ps2.setString(1, "研发一部");
ps2.setString(2, "北京");
ps2.setInt(3, 60);*/

String sql = "delete from dept where deptno = ?";
ps3 = conn.prepareStatement(sql);
ps3.setInt(1, 60);

// 执行SQL
int count = ps.executeUpdate();
System.out.println(count);

JDBC实现模糊查询

查找第二个字母包含A的员工

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
// 使用工具类获取连接
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"));
}

模拟用户登录功能(防止注入)

编程技巧: 调用一个方法时,我们先假定有这么一个方法,确定方法的接收参数以及返回值 , 最后Alt + Enter自动生成,然后再写逻辑代码

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 ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @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 (stmt != null) {
                try {
                    stmt.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;
    }
}

JDBC单机事务

JDBC事务机制:JDBC默认的事务行为是自动提交的,只要执行任意一条DML语句,则自动提交一次

  • 在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败
  • 调用connection对象的setAutoCommit(false) , 表示关闭数据库的自动提交功能改为手动提交, 即开启数据库事务的功能
  • JDBC完整事务的流程: conn.setAutoCommit(false)开启事务 -> 执行一组sql -> conn.commit()提交(结束)事务->conn.rollback()遇到异常回滚事务

模拟转账业务

sql脚本

drop table if exists t_act;
create table t_act(
     actno int,
     --注意:7表示有效数字的个数,2表示小数位的个数
     balance double(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;
package com.bjpowernode.jdbc;

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

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连接数据库所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部