文章目录
1.前言
(1)JDBC概念:Java DataBase Connectivity(Java语言连接数据库)
(2)本质:是SUN公司制定的一套接口
2. JDBC编程六步骤
(1)注册驱动(告诉Java程序,即将要连接的是哪个品牌的数据库)
(2)获取连接(表示JVM的进程和数据库进程之间的通道打开了,这是属于进程之间的通信,重量级的,使用完之后一定要关闭通道)
(3)获取数据库操作对象(专门执行sql语句的对象)
(4)执行SQL语句(DQL,DML)
(5)处理查询结果集(只有当第四步执行的是select语句的时候才有这第五步处理查询结果集)
(6)释放资源(使用完资源以后一定要关闭资源。Java和数据库属于进程间的通信,使用完一定要关闭)
2.1示例
import java.sql.*;
public class JDBCTest {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try{
//1.注册驱动
Driver driver = new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);
//2.获取连接
/**
* url:统一资源定位符
* 比如一个网址就是url
* URL由协议、IP、Port、资源名字 组成
*
* http://182.61.200.7:80/index.html
* http://:通信协议
* 182.61.200.7:80:是IP地址
* 80:是端口号
* index.html:是服务器上的某个资源名
*
* jdbc:mysql://127.0.0.1:3306/bjpowernode
* jdbc:mysql://:是协议
* 127.0.0.1是本机IP地址
* 3306是MySQL服务端口号
* bjpowernode是需要访问的资源
*
* 所谓通信协议就是组织好的数据传输格式
*
* */
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root";
String password = "1017";
conn = DriverManager.getConnection(url,user,password);
//3.获取数据库操作对象,Statement专门执行sql语句。
stmt = conn.createStatement();
//4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
/**
* 专门执行DML语句(update,delete,insert)
* 返回值是影响数据库中的数据条数(比如插入三条就返回3)
* */
int count = stmt.executeUpdate(sql);
System.out.println(count == 1?"保存成功":"保存失败");
//5.处理查询结果集
}catch (SQLException e){
e.printStackTrace();
}finally {
//6.释放资源
/**
* 为了保证资源一定释放,在finally中释放资源
* 释放资源要保证从小到大依次释放。比如statement是由connection对象产生的,我们要先关闭statement再关闭connection对象
* 释放资源要写在try...catch块里
* */
try {
if (stmt!=null)
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
try {
if (conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
3.进阶写法
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public class JDBCTest02 {
/**
* 注册驱动最常用的一种方法
*
* 在实际开发中,不建议把连接数据库的信息写死到程序里面去
* */
public static void main(String[] args) {
//使用资源绑定器绑定属性配置文件
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");
Connection conn = null;
Statement stmt = null;
try {
//1.注册驱动
/**
* 这种方式常用是因为参数是字符串,字符串可以写入xxx.properties文件中
* */
Class.forName(driver);//使用反射机制将驱动加载到内存当中。
//2.获取连接
conn = DriverManager.getConnection(url,user,password);
//3.获取数据库操作对象,Statement专门执行sql语句。
stmt = conn.createStatement();
//4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1?"保存成功":"保存失败");
//5.处理查询结果集
}catch (SQLException e){
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}finally {
//6.释放资源
try {
if (stmt!=null)
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
try {
if (conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
}
}
4.处理查询结果集
import java.sql.*;
public class JDBCTest03 {
public static void main(String[] args) {
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","1017");
//3.获取数据库操作对象,Statement专门执行sql语句。
stmt = conn.createStatement();
//4.执行sql语句
String sql = "select empno,ename,sal from emp";
//int count = stmt.executeUpdate(sql);<---专门处理DML语句的方法
rs = stmt.executeQuery(sql);
//5.处理查询结果集
boolean flag1 = rs.next();
/**
* getString()方法的特点是不管数据库中的数据类型是什么,都是以String的形式取出.
* 并且其中要求的参数是列的下标(getString(1))或者是列名(getString("ename"),如果列名被重写必须要使用被重写的列名)
*
* 除了可以以String类型取出之外,还可以以特定的类型取出
* 比如:int empno = rs.getInt("empno")
*
* 不过要注意的是,jdbc中的下标从1开始不是从0开始
* */
while (rs.next()){
String empno = rs.getString(1);
String ename = rs.getString(2);
String sal = rs.getNString(3);
System.out.println(empno+","+ename+","+sal);
}
}catch (SQLException e){
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}finally {
//6.释放资源
try {
if (rs!=null)
rs.close();
}catch (SQLException 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();
}
}
}
}
5.关于SQL注入
5.1代码展示
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 如何解决SQL注入的问题:
*
* 使用java.sql.PreparedStatement
* 该类继承了statement,其属于预编译的数据库操作对象。
* 其原理是:预先对SQL语句的框架进行编译,然后再给sql语句传值。
*
* */
public class JDBCTest05 {
public static void main(String[] args) {
Map<String, String> userLoginInfo = init();
boolean loginSuccess = login(userLoginInfo);
System.out.println(loginSuccess?"登录成功":"登录失败");
}
private static boolean login(Map<String,String> userLoginInfo) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String userName = userLoginInfo.get("userName");
String passWord = userLoginInfo.get("passWord");
boolean loginSuccess = false;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",
"root","1017");
String sql = "select * from t_user where loginName=? and loginPwd=?";
ps = conn.prepareStatement(sql);
ps.setString(1,userName);
ps.setString(2,passWord);
rs = ps.executeQuery();
if (rs.next()){
loginSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs!=null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (ps!=null){
try {
ps.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
return loginSuccess;
}
private static Map<String, String> init() {
Scanner in = new Scanner(System.in);
System.out.println("用户名:");
String userName = in.nextLine();
System.out.println("密码:");
String passWord = in.nextLine();
Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("userName",userName);
userLoginInfo.put("passWord",passWord);
return userLoginInfo;
}
}
5.2关于Statement和PreparedStatement的区别
Statement存在注入问题;而PreparedStatement存在注入问题。
Statement是编译一次执行一次;而PreparedStatement是编译一次执行N次。
PreparedStatement会在编译阶段做类型的安全检查。
大多数情况下使用PreparedStatement,只有在业务要求SQL注入的时候才会使用Statement。
6.JDBC事务自动提交机制
6.1代码演示
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* JDBC事务机制:
* 1.JDBC中的事务是自动提交,什么是自动提交?
* 只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
* 但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须
* 保证他们这些DML语句在同一个事务当中同时成功或者同时失败。
* */
public class JDBCTest06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",
"root","1017");
String sql = "update dept set dname = ? where deptno=?";
ps = conn.prepareStatement(sql);
//操作1
ps.setString(1,"X部门");
ps.setInt(2,30);
int count = ps.executeUpdate();
System.out.println(count);
//操作2
ps.setString(1,"Y部门");
ps.setInt(2,30);
int count2 = ps.executeUpdate();
System.out.println(count2);
} catch (Exception e) {
e.printStackTrace();
}finally{
if (ps!=null){
try {
ps.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
我们可以对以上代码进行Debug,不难发现,当操作1代码执行完毕以后我们的数据库就已经发生了改变。
6.2手动修改事务
//将Connection对象禁用自动提交机制。
conn.setAutoCommit(false);
//在操作完成以后我们使用commit()方法手动提交。
conn.commit();
//为了保证数据安全,最后还要在catch块中手动调用事务回滚
catch (Exception e) {
conn.rollback();
e.printStackTrace();
}
7.JDBC工具类的封装
7.1封装代码
public class DBUtil {
private DBUtil(){}
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",
"root","1017");
}
public static void close(Connection conn, Statement ps, ResultSet rs){
if (rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
7.2使用JDBC实现模糊查询
public class DBUtil {
private DBUtil(){}
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",
"root","1017");
}
public static void close(Connection conn, Statement ps, ResultSet rs){
if (rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
8.悲观锁和乐观锁的概念
8.1悲观锁(行级锁)
select ename,job,sal from emp where job="MANAGER" for update;
在sql语句末尾加上for update表示悲观锁,在事务结束之前,其他线程无法对悲观锁锁上的数据进行操作,所有事务都必须排队执行,不允许并发。
8.2乐观锁
支持并发,事务也不需要排队,只不过需要一个版本号
比如说一条记录具有版本号1.1。
有两个事务分别是:A、B
其中事务A先进行修改,完成以后版本号变为1.2,
事务B是后进行修改的,修改以后准备提交发现版本号变为1.2,和他最初读的版本号不一样,所以它选择回滚。