今日内容【重点掌握】
- JDBC基本概念
- 快速入门
- 对JDBC中各个接口和类详解
1. JDBC:
- 概念:Java数据库连接(Java DataBase Connectivity)
- Java语言操作数据库
- JDBC本质:
- 其实是官方(sun公司)定义的一套操作++所有关系型数据库的规则++,即接口。
- 各个数据库厂商去实现这套接口,提供数据库驱动jar包。
- 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
- 快速入门:
- 步骤:
- 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
- 复制到项目的libs目录下
- 右键libs文件–>Add As Library(这样了才真正的加入到了项目中)
- 注册驱动(编写代码)
Class.forName("com.mysql.jdbc.Driver");
- Class.forName(“”);的作用是将代码加载进内存
- 会有异常:正常写的时候要处理
- 获取数据库的连接对象(Connection)
//数据库db3的url,账号,密码 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password");
- 定义sql
String sql = "update account set balance = 500 where id = 1";
- 获取执行sql语句的对象(Statement)
Statement stmt = conn.createStatement();
- 执行sql,接收返回结果
int count = stmt.executeUpdate(sql);
- 处理结果
System.out.println(count);
- 释放资源
stmt.close(); conn.close();
- 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
- 步骤:
- ++详解各个对象++:
1. ++DriverManager:驱动管理对象++
- 是一个类
- 功能:
- 注册驱动:告诉程序该使用哪一个数据库驱动jar包
- 方法:static void registerDriver(Driver driver):注册与给定的驱动程序(DriverManager)
- 写代码时使用的是:Class.forName(“com.mysql.jdbc.Driver”);【重点掌握】//作用是将代码(类文件)加载进内存
- ++这样有一些代码会随着类的加载而自动执行(静态代码块static)++
- 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static{ try{ java.sql.DriverManager.registerDriver(new Driver()); }catch(SQLException E){ throw new RuntimeException("Can't register driver!"); } }
- 所以底层实现就是DriverManager,只不过使用Class.forName(“com.mysql.jdbc.Driver”);简单【总结】
- 注:mysql之后的驱动jar包可以省略注册驱动的步骤【一般写上】
- 在jar包–>META-INF–>services–>java.sql.Driver中
- 注:Class.forName会有异常:正常写的时候要处理
- 获取数据库连接
- 方法:static Connection getConnection(String url, String user, String password);//静态方法,可以直接通过类名调用
- 这个也是会抛出异常,和Class.forName抛出的异常放到一起
- 参数:
- url:指定连接的路径
- 语法【重点】(每个sql语言都不太一样):
- MySQL
//ip找到某计算机,端口号可以找到该计算机上的MySQL jdbc:mysql://ip地址(域名):端口号/数据库名称
- 例子:
jdbc:mysql://localhost:3306/db3
- 细节:如果访问本地则ip和端口号可以省略(了解)
jdbc:mysql:///db3
- 语法【重点】(每个sql语言都不太一样):
- user:用户名
- password:密码
- url:指定连接的路径
- 注册驱动:告诉程序该使用哪一个数据库驱动jar包
2. ++Connection:数据库连接对象++
- 是个接口
- 功能:
- 获取执行sql的对象
- 方法:
- Statement createStatement():【重点掌握】
- 例子:获取==执行sql语句的对象(Statement是返回值类型)
Statement stmt = conn.createStatement();
- PreparedStatement preparedStatement(String sql)
- 管理事务
- 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(自动变为手动),即为开启事务
- 提交事务:commit()
- 回滚事务:rollback()
- 获取执行sql的对象
3. ++Statement:执行sql的对象【重点】++
- 是个接口
- 功能:
- 执行sql
- 方法:
- boolean execute(String sql):可以执行任意的sql–>(了解)
- int executeUpdate(String sql):【重点掌握】–>执行DML(增删改insert,delete,update)语句、DDL(表和库的操作create,alter,drop)语句
- 注:返回值就是执行语句影响的行数
- 执行DDL的时候不返回(默认0)
- 可以通过这个影响的行数判断DML语句是否执行成功,返回值>0则执行成功,反之,则失效
- 注:返回值就是执行语句影响的行数
- ResultSet executeQuery(String sql):【重点掌握】–>执行DQL(查询select)语句
- 返回值是:结果集对象(就是查询sql后显示的呢个结果)
- 执行sql
- 练习:
- account表 添加一条记录(insert)
- 注:释放资源放到finally里面(不管代码对不对都要释放)
- 导入sql包不要看错了
import java.sql.Statement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class Test { public static void main(String[] args) { Statement stmt = null; Connection conn = null; try { //1. 注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2. 定义sql String sql = "INSERT INTO account value(null, '王五', 3000)"; //3. 获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "password"); //4. 获取执行sql的对象:Statement stmt = conn.createStatement(); //5. 执行sql int count = stmt.executeUpdate(sql); //影响的行数 //处理的结果 System.out.println(count); if(count > 0) { System.out.println("添加成功"); }else { System.out.println("添加失败"); } } catch (ClassNotFoundException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //为了避免空指针异常 //7. 释放资源 if(stmt != null) { try { stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- account表 修改记录
- 修改id为3的钱为1500
import java.sql.Statement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class Test2 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1. 驱动注册 Class.forName("com.mysql.jdbc.Driver");//** //2. 编写sql String sql = "UPDATE account SET balance=1500 WHERE id=3"; //3.创建Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password"); //4. 获取执行sql对象 stmt = conn.createStatement(); //5.执行sql int count = stmt.executeUpdate(sql); //6.执行结果 System.out.println(count); if(count > 0) { System.out.println("执行成功"); }else { System.out.println("执行失败"); } } catch (ClassNotFoundException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //7.释放资源 if(stmt != null) { try { stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- account表 删除一条记录
- 删除id=3的记录
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class Test3 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; try { //1.驱动注册 Class.forName("com.mysql.jdbc.Driver"); //2.sql语句 String sql = "DELETE FROM account WHERE id = 3"; //3.获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password"); //*** //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql语句 int count = stmt.executeUpdate(sql); //6.执行结果 System.out.println(count); if(count > 0) { System.out.println("删除成功"); }else { System.out.println("删除失败"); } } catch (ClassNotFoundException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(stmt != null) { try { stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- account表 添加一条记录(insert)
4. ++ResultSet:结果集对象(用来封装查询结果)++
- 是一个接口
- 意义:从ResultSet结果集对象中,把查出来的结果数据拿出来
- 默认在第一行,所以先向下
- 执行方法让游标一行一行向下进行数据获取
- 每次获取的是该行的某一列数据
- 方法:
- boolean next():++游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果没有数据,返回false++
- getXxx(参数):获取数据
- Xxx:代表数据类型
- 比如获取id的时候getInt()
- 获取name的时候getString()
- 参数:(体现了方法的重载)
- Int:代表列的编号(从1开始)
- 获取id的列数据
- getInt(1):表示获取id列数据
- 获取id的列数据
- String:代表列的名称。
- 获取balance列的数据(余额)
- getDouble(“balance”);
- 获取balance列的数据(余额)
- Int:代表列的编号(从1开始)
- Xxx:代表数据类型
- 演示:(将来不是这样使用的,这里只是基本使用)
- 结果集对象也是资源,所以也需要释放
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Test4 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.sql语句 String sql = "SELECT * FROM account"; //3.获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "password"); //4.创建获取执行sql对象 stmt = conn.createStatement(); //5.执行sql(DQL-->用executeQuery) rs = stmt.executeQuery(sql); //6. 执行结果(获取的数据) //6.1先让游标向下移动一行 rs.next(); //6.1获取数据 int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble("balance"); //打印 System.out.println(id + "---" + name + "---" + balance); } catch (ClassNotFoundException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- 出现的问题
- 获取第二行的时候又要复制所有获取数据代码
- ResultSet真正使用方式
- 游标向下移动一行
- 判断是否有数据
- 获取数据
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class Test5 { public static void main(String[] args) { ResultSet rs = null; Statement stmt = null; Connection conn = null; try { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2.sql语句 String sql = "SELECT * FROM account"; //3.获取Connection对象 conn = DriverManager.getConnection("jdbc.mysql://localhost.3306/db3", "root", "password"); //4.获取执行sql对象 stmt = conn.createStatement(); //5.执行sql(DQL-->Query) rs = stmt.executeQuery(sql); //6. 执行结果(获取的数据) //循环判断游标是否是最后一行末尾 while(rs.next()) { //6.1获取数据 int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble("balance"); //打印 System.out.println(id + "---" + name + "---" + balance); } } catch (ClassNotFoundException | SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { if(rs != null) { try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(stmt != null) { try { stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
- 练习:
- 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
- 定义Emp类
- 定义方法 public List findAll(){}
- 实现方法 select * from emp;
//Emp类(JavaBean) public class Emp { private int id; private String balance; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getBalance() { return balance; } public void setBalance(String balance) { this.balance = balance; } @Override public String toString() { return "Emp{" + "id=" + id + ", balance='" + balance + '\'' + '}'; } } //JdbcDemo类 import java.sql.*; import java.util.LinkedList; import java.util.List; public class JdbcDemo { public static void main(String[] args){ List<Emp> list = new JdbcDemo().findAll(); System.out.println(list); } public List<Emp> findAll(){ Connection conn = null; Statement stmt = null; ResultSet rs = null; List<Emp> listA = null; try { //1注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2sql语句 String sql = "SELECT * FROM emp"; //3获取Connection对象 conn = DriverManager.getConnection("jdbc:mysql://localhost.3306/db3", "root", "psaaword"); //4.获取执行sql的对象 stmt = conn.createStatement(); //5.执行sql(查询的DQL,所以query) rs = stmt.executeQuery(sql); Emp emp = null; //因为要返回所以写在外面 listA = new LinkedList<>(); //用来存储信息的集合 //获取信息后存到集合中 while (rs.next()){ //判断下一行是否有值 //先获取到数据库里面的值 int id = rs.getInt("id"); String balance = rs.getString("balance"); //创建emp对象并赋值 emp = new Emp(); //将值添加到emp类中 emp.setId(id); emp.setBalance(balance); //加到集合中 listA.add(emp); } } catch (ClassNotFoundException | SQLException 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(); } } } return listA; } }
- 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
5. ++PreparedStatement:表示预编译sql语句的对象【开发真正用的】(功能更多)++
- Statement接口的子接口
- 注:浏览顺序,先看JDBC工具类在回头来看该内容
- JDBC工具类中综合案例出现的问题:SQL注入问题【重点】
- SQL注入概念【*】:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。(会造成安全问题)
- 例子:
- 输入用户随便(gxw),输入密码(特殊密码):
//输入的特殊密码 a' or 'a' = 'a
- 就会生成一段sql语句
select * form user where username='gxw' and password='a' or 'a' = 'a'
- or之前确实是false但是后面加的为true,or是或运算,所有就恒等于true了(就很危险)
- ++解决SQL注入问题++:使用PreparedStatement对象来解决
- 预编译的sql【重点】:参数使用?作为占位符
- 步骤【真正要掌握的】:
- 导入驱动jar包(mysql-connector-java-5.1.37-bin.jar)
- 注册驱动(编写代码)
- Class.forName(“”);的作用是将代码加载进内存
- 获取数据库的连接对象(Connection)
- 定义sql
- 注意:sql的参数使用?作为占位符【#】
- 如:
select * form user where username=? and password=?;
- 获取执行sql语句的对象(PreparedStatement)
- 调用:Connection.prepareStatement(String sql)方法
- 要传递一个sql进来(不同之处)【注】
- 调用:Connection.prepareStatement(String sql)方法
- 给?去赋值:
- 方法:setXxx(参数1,参数2);
- 参数1:?的位置编号从1开始
- 参数2:?的值
- 方法:setXxx(参数1,参数2);
- 执行sql,接收返回结果(这里就不需要传递sql语句了)
- 处理结果
- 释放资源
- 优化JDBC工具类中的登录方法:
public class JdbcTest1 { public static void main(String[] args) { //输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号"); String username = sc.nextLine(); System.out.println("请输入密码"); String password = sc.nextLine(); boolean login = new JdbcTest1().login(username, password);//非静态方法 if(login){ System.out.println("登录成功"); }else { System.out.println("账号或者密码错误"); } } /* * 登录方法,使用PreparedStatement实现 */ public boolean login(String username, String password){ if(username == null || password == null){ return false; } Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { //1.获取资源 conn = JDBCUtils.getConnection(); //2.sql语句 String sql = "SELECT * FROM username=? AND password=?"; //3.获取执行sql的对象 pstmt = conn.prepareStatement(sql); //4.给?号赋值 pstmt.setString(1, username); pstmt.setString(2, password); //5.执行sql,不需要传递参数 rs = pstmt.executeQuery(); //判断 return rs.next(); //有下一行则返回true } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtils.close(rs, pstmt, conn); } return false; } }
- 注意:后期都会使用PreparedStatement来完成增删改查的所有操作【重点】
- 防止SQL注入
- 效率更高
- SQL注入概念【*】:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。(会造成安全问题)
2. 抽取JDBC工具类 : JDBCUtils【工具类重点,会使用即可】
- 目的:简化书写
- 分析:【抽取的:注册驱动,获取连接对象,释放资源】
- 注册驱动也抽取
- 抽取一个方法获取连接对象【DriverManager.getConnection】
- 需求:++不想传递参数(麻烦),还得保证工具类的通用性。++
- 解决:配置文件
jdbc.properties
url=
user=
password=
- 抽取一个方法释放资源
- 代码实现【*】:
- 配置文件:jdbc.properties
//数据库db3的位置 url=jdbc:mysql:///db3 user=root password=root driver=com.mysql.jdbc.Driver
- 方便调取,所以都是静态
public class JDBCUtils { //先声明4个静态成员变量 private static String url; private static String user; private static String password; private static String driver; /** * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块 */ static{ //读取资源文件,获取值。 try { //1. 创建Properties集合类。 Properties pro = new Properties(); //获取src路径下的文件的方式--->ClassLoader 类加载器 ClassLoader classLoader = JDBCUtils.class.getClassLoader(); URL res = classLoader.getResource("jdbc.properties"); String path = res.getPath(); //System.out.println(path);///D:/IdeaProjects/itcast/out/production/day04_jdbc/jdbc.properties //2. 加载文件 pro.load(new FileReader(path));//读取字符文件 //3. 获取数据,赋值 url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver"); //4. 注册驱动 Class.forName(driver); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 获取连接 * @return 连接对象 */ public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } /** * 释放资源【dml时候,增删改时】 * @param stmt * @param conn */ public static void close(Statement stmt,Connection conn){ if( stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if( conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 释放资源【dql时候,查询时】 * @param rs * @param stmt * @param conn */ public static void close(ResultSet rs,Statement stmt, Connection conn){ 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(); } } } }
- 什么时候使用静态代码块
- 如果需要程序在启动之前就进行一些操作,以及一些经常用到却不占用多少内存的代码或变量直接写在static静态代码块中,当程序启动的时候随之初始化,这样可以节省部分后续操作的时间
- 格式:
static{ }
- ClassLoader 类加载器【先了解】
- 作用
- 获取src路径下的(资源路径)文件的方式
- 可以来加载我们的字节码文件进内存
- (JDBC工具类获取配置文件路径的)使用步骤【重点】
- 获取到ClassLoader,首先获取到对应的字节码文件对象
- 类名.class对象
- 其中一个getClassLoader()方法:获取到ClassLoader
ClassLoader classloader = JDBCUtils.class.getClassLoader();
- 类名.class对象
- ClassLoader一个方法:getResource(String name):传入一个文件名(配置文件),就可以获取Resource资源。
- ++返回的是一个URL对象(表示:统一资源标识符/定位符):可以用来定位一个文件的绝对路径++
URL res = classloader.getResource("jdbc.properties");
- 使用URL对象的一个方法,getPath():可以获取一个字符串路径
String path = res.getPath();
- 最后就可以将获取到的path,写到加载文件中了(上面三步就可以获取对应的资源文件的绝对路径了)
- 获取到ClassLoader,首先获取到对应的字节码文件对象
- 作用
- Properties集合类
- 双列集合,键值都是String,唯一一个可以直接结合IO流使用的集合类,++从流中读取文件,直接写入文件到流中++
- 读取资源文件获取值的步骤
- 创建Properties集合类
Properties pro = new Properties();
- 加载文件:
- Properties集合类中load()方法:从字节输入流中读取键值对。参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。该方法常用于读取配置文件
pro.load(new FileReader(path));
- 获取值
- Properties集合类中getProperty()方法:将搜索此属性列表中指定键的属性值
url = pro.getProperty("url"); user = pro.getProperty("user"); password = pro.getProperty("password"); driver = pro.getProperty("driver");
- 创建Properties集合类
- 演示JDBC工具类
import java.sql.*; import java.util.LinkedList; import java.util.List; public class JdbcDemo { public static void main(String[] args){ List<Emp> list = new JdbcDemo().findAll(); System.out.println(list); } public List<Emp> findAll(){ Connection conn = null; Statement stmt = null; ResultSet rs = null; List<Emp> listA = null; try { //注册驱动和获取Connection对象直接换成: conn = JDBCUtils.getConnection(); //3sql语句 String sql = "SELECT * FROM emp"; //4.获取执行sql的对象 stmt = conn.createStatement(); //5.执行sql(查询的DQL,所以query) rs = stmt.executeQuery(sql); Emp emp = null; //因为要返回所以写在外面 listA = new LinkedList<>(); //用来存储信息的集合 //获取信息后存到集合中 while (rs.next()){ //判断下一行是否有值 //先获取到数据库里面的值 int id = rs.getInt("id"); String balance = rs.getString("balance"); //创建emp对象并赋值 emp = new Emp(); //将值添加到emp类中 emp.setId(id); emp.setBalance(balance); //加到集合中 listA.add(emp); } } catch (SQLException e) { //ClassNotFoundException异常就不需要了 e.printStackTrace(); }finally { //直接使用JDBCUtils工具类 JDBCUtils.close(rs, stmt, conn); } return listA; } }
- 练习:
- 需求(给db4数据库):
- 通过键盘录入用户名和密码
- 判断用户是否登录成功
* select * from user where username = "" and password = "";
- 如果这个sql有查询结果,则成功,反之,则失败
- 步骤:
- 创建数据库表 user
CREATE TABLE USER( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32), PASSWORD VARCHAR(32) ); INSERT INTO USER VALUES(NULL,'zhangsan','123'); INSERT INTO USER VALUES(NULL,'lisi','234');
- 代码实现:
- 配置文件中的路径要进行修改:
//数据库现在在db4的位置 url=jdbc:mysql:///db4 user=root password=root driver=com.mysql.jdbc.Driver
- 前提要有前面写好的JDBCUtils工具类和配置文件
//创建一个类:JdbcTest1 public class JdbcTest1 { public static void main(String[] args) { //输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号"); String username = sc.nextLine(); System.out.println("请输入密码"); String password = sc.nextLine(); boolean login = new JdbcTest1().login(username, password);//非静态方法 if(login){ System.out.println("登录成功"); }else { System.out.println("账号或者密码错误"); } } /* 登录方法 */ public boolean login(String username, String password){ if(username == null || password == null){ return false; } Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1.获取资源 conn = JDBCUtils.getConnection(); //2.sql语句 String sql = "SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'"; //3.获取执行sql的对象 stmt = conn.createStatement(); //4.执行sql rs = stmt.executeQuery(sql); //判断 return rs.next(); //有下一行则返回true } catch (SQLException e) { e.printStackTrace(); }finally { JDBCUtils.close(rs, stmt, conn); } return false; } }
- 注:定义sql语句时,里面传入的username和password都是字符串,所以拼接的时候别忘了单引号
- 存在的问题SQL注入问题,请看上面第五个内容有讲解【注】
- 配置文件中的路径要进行修改:
- 创建数据库表 user
- 需求(给db4数据库):
3. 使用JDBC控制事务:
- 事务:一个包含多个步骤的业务操作。如果这个业务操作++被事务管理,则这多个步骤要么同时成功,要么同时失败++
- 操作JDBC事务处理的位置【重点】:
- 开启事务:++执行sql之前++
- 在获取到Connection对象之后开启–>(因为事务管理功能就属于Connection对象)
- 提交事务:++sql都执行完++
- 在执行完sql语句之后
- 回滚事务:++catch中++
- 在catch当中,且注意判断conn是否为空
- 开启事务:++执行sql之前++
- Connection对象的两个功能:
- 获取执行sql的对象
- 管理事务
- 所以使用Connection对象来管理事务
- 开启事务:void setAutoCommit(boolean autoCommit):调用该方法设置参数为false(false–>手动:开启事务后如果没有提交事务,就会回滚事务),(true–>自动:每次执行完自动提交事务)
- 提交事务:commit()
- 回滚事务:rollback()
- 注:只要有异常就让回滚(放到catch中)
- 回滚的时候,conn有可能为null,所以在回滚前判断是否为空
if(conn != null){ conn.rollback(); }
- 案例:银行转账
- 注:catch一般写的范围比较大
/** * 事务的操作 */ public class JDBCDemo3 { public static void main(String[] args) { Connection conn = null; PreparedStatement pstmt1 = null; PreparedStatement pstmt2 = null; try { //1.获取数据库的链接 conn = JDBCUtils.getConnection(); ///开启事务 conn.setAutoCommit(false); //2.定义sql //2.1张三-500 String sql1 = "UPDATE account SET balance = balance - ? WHERE id = ?"; //2.2李四+500 String sql2 = "UPDATE account SET balance = balance + ? WHERE id = ?"; //3.获取执行sql对象 pstmt1 = conn.prepareStatement(sql1); pstmt2 = conn.prepareStatement(sql2); //4.给?赋值 pstmt1.setDouble(1, 500); pstmt1.setInt(2, 1); pstmt2.setDouble(1, 500); pstmt2.setInt(2, 2); //5.执行sql语句(DML所以,使用executeUpdate) pstmt1.executeUpdate(); //手动制造一点问题,但能否回滚(可以) int i = 3 / 0; pstmt2.executeUpdate(); ///提交事务 conn.commit(); } catch (Exception e) { //一般抓一个大的异常,防止多次添加 ///事务回滚 try { if(conn != null){ conn.rollback(); } } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); }finally { //这里写的不规范,只是为了演示事务 JDBCUtils.close(pstmt1, conn); JDBCUtils.close(pstmt2, null); } } }