JDBC
概念:Java Database Connectivity 数据库连接,Java语言操作数据库
-
JDBC本质:官方定义的一套接口(规则),用于操作所有关系型数据库。各个数据库厂商去实现这个接口,提供数据库驱动jar驱动包。
==使用这套接口编程,真正执行的代码是驱动jar包中的实现类==
-
导包步骤:
- 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
- 复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
- 右键-->Add As Library // 添加成库
实现代码
//**初学者代码,有许多不足,需要修改!!!**
public class JdbcDemo1_1 {
public static void main(String[] args) throws Exception {
// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库路径
String url = "jdbc:mysql:///db3";
// 获取数据库连接对象
Connection conn = DriverManager.getConnection(url, "root", "root");
// 获取执行sql的对象Statement
Statement stat = conn.createStatement();
// 写sql语句
String sql = "update account set balance = 505 where id = 1";
// 通过statement对象调用excuteUpdate执行sql语句
int count = stat.executeUpdate(sql);
// 输出影响条数
System.out.println(count);
// 释放资源
stat.close();
conn.close();
}
}
详解对象
-
DriverManager : 驱动管理对象
- 注册驱动
static void registerDriver(Driver driver) //注册与给定的驱动程序 DriverManager 。
com.mysql.jdbc.Driver类中存在这样的静态代码块源码:
static { try { //注册驱动 DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
由此可见,真正注册驱动的是com.mysql.jdbc.Driver类。
==注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。==
- 获取数据库连接
Connection conn = DriverManager.getConnection(url, user, password);
url:指定连接的路径
语法: jdbc:mysql://ip地址(域名):端口号/数据库名称
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
==注意: 在实际开发中并不推荐采用registerDriver方法注册驱动==
原因:
- 查看Drive的源码可以看到,如果采用此方法,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。
- 程序依赖MySql的API,脱离MySql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
-
Connection:数据库连接对象
- 获取执行SQL的对象Statement
- createStatement() : 创建向数据库发送sql的statement对象
- preparedStatement( sql ) : 创建向数据库发送预编译 sql 的PreparedStatement 对象。
- prepareCall(sql):创建执行存储过程的callableStatement对象。
- setAutoCommit(boolean autoCommit):设置事务是否自动提交。(false为手动,即开启事务)
//Statement createStatement() Statement stat = connection.createStatement(); //PreparedStatement prepareStatement(String sql) PreparedStatement p = connection.prepareStatement(sql);
- 管理事务
//开启事务,调用该方法设置参数为false,即开启事务 setAutoCommit(boolean autoCommit); //提交事务 commit(); //回滚事务 rollback();
-
Statement : 执行SQL的对象
- execute(String sql):执行任意的sql
boolean execute(String sql);
- executeUpdate(String sql):执行DML(insert、update、delete),DDL(create,alter、drop ) 语句
int executeUpdate(String sql); //返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 //返回值>0的则执行成功,反之,则失败。
- executeQuery(String sql) :执行DQL(select )语句
ResultSet executeQuery(String sql);
-
ResultSet : 结果集对象,封装查询结果
- next()方法:
while(rs.next()){ int id = rs.getInt(1); String name = rs.getString("name"); double balance = rs.getDouble(3); System.out.println(id + "---" + name + "---" + balance); } // 判断游标是否还有下一行,有的话返回值为true // 当调用 next 方法返回 false 时,光标位于最后一行的后面
- Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast() :移动到resultSet的最后面。
- 释放资源
- JDBC程序运行完之后,切记要释放资源,在运行过程中,创建的那些与数据库交互的对象,比如ResultSet,Statement(包括他的子类PreparedStatement)和Connection对象,特别是Connection对象是非常稀有的资源,用完必须马上释放掉,否则极易导致系统宕(dang<第四声>)机。
练习案例:
- 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
import itcast.domain.Emp;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/*
需求:* 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
1. 定义Emp类
2. 定义方法 public List findAll(){}
3. 实现方法 select * from Emp;
*/
public class JdbcDemo4 {
public static void main(String[] args) {
List all = new JdbcDemo4().findAll();
for (Emp emp : all) {
System.out.println(emp.getId()+"|\t"+emp.getEname()+"|\t"+emp.getJob_id()+"|\t"+emp.getMgr()+"|\t"+emp.getJoindate()+"|\t"+emp.getSalary()+"|\t"+emp.getBonus()+"|\t"+emp.getDept_id());
}
}
public List findAll(){
//define and init connection
Connection connection = null;
//define and init statement
Statement statement = null;
//define and init resultSet
ResultSet resultSet = null;
//definr and init List
List list = null;
try {
//register Drive
Class.forName("com.mysql.jdbc.Driver");
//define path
String url = "jdbc:mysql:///db3";
//obtain connection from DriverManager--use the method which is getConnection(url,user,password)
connection = DriverManager.getConnection(url, "root", "admin");
//obtain execute sql object statement
statement = connection.createStatement();
//write sql
String sql = "select * from emp";
//invoking executeQuery to execute sql statement
resultSet = statement.executeQuery(sql);
Emp emp = null;
list = new ArrayList<>();
//judge whether there is the next line cursor
while (resultSet.next()){
//obtain values
int id = resultSet.getInt(1);
String ename = resultSet.getString(2);
int job_id = resultSet.getInt(3);
int mgr = resultSet.getInt(4);
Date joindate = resultSet.getDate(5);
double salary = resultSet.getDouble(6);
double bonus = resultSet.getDouble(7);
int dept_id = resultSet.getInt(8);
//setting emp values
emp = new Emp();
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
list.add(emp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}
-
PreparedStatement : 执行sql的对象
它是Statement的子类
-
PreparedStatement的优点:
避免SQL注入问题
-
Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
而PreparedStatement可以对SQL进行预编译,从而提高数据库的执行效率。
PreparedStatement允许使用占位符替换sql语句中的参数,简化sql语句编写。
-
SQL注入问题:
在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。可以直接绕过账号密码的验证,会造成安全性问题。
-
SQL注入问题解决办法:
使用PreparedStatment对象来解决
抽取JDBC工具类:JDBCUtils
- 分析:
- 抽取方法释放资源
- 注册驱动也抽取
- 抽取一个方法获取连接对象
- 不想传递参数(麻烦),还得保证工具类的通用性。
JDBCUtils类
package itcast.jdbc;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/*
工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//注册驱动一次就好,所以使用静态代码块
static {
//1.创建Properties集合类
Properties properties = new Properties();
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
//executeUpdate释放资源
public static void close(Statement statement, Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// executeQuary释放资源
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
配置文件
url = jdbc:mysql:///db3
user = root
password = admin
driver=com.mysql.jdbc.Driver
案例:使用JDBCUtils工具类完成以下需求
- 通过键盘录入用户名和密码
- 判断用户是否登录成功
JdbcDemo6类
package itcast.jdbc;
import java.sql.*;
import java.util.Scanner;
public class JdbcDemo6 {
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 flag = new JdbcDemo6().login(username, password);
if (flag) {
System.out.println("登陆成功");
} else {
System.out.println("用户名或密码错误!");
}
}
// 方法体:判断登陆
public boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection();
String sql = "select * from user where username = ? and password = ? ";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
resultSet = preparedStatement.executeQuery();
return resultSet.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(resultSet, preparedStatement, connection);
}
return false;
}
}
配置文件
url = jdbc:mysql:///db4
user = root
password = admin
driver=com.mysql.jdbc.Driver
JDBC控制事务:
代码:
public class JDBCDemo10 {
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
pstmt1.executeUpdate();
// 手动制造异常
int i = 3/0;
pstmt2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
}
}