1.JDBC的概念及步骤
java数据库连接技术:通过java语言来操作数据库(CRUD)
①发展过程
ODBC:开放数据库互联技术,微软公司用C语言写的,JAVA语言出现
JDBC-ODBC:桥接技术,既能操作java又能连接C语言,但是效率低,java已经成熟
JDBC:纯java编写
②连接约定
约定:接口,现在接口是由各个数据库厂商给java提供,jar包
连接:IP port端口号(3306) username password db
package edu.hm.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCStep {
public static void main(String[] args) {
step();
}
/*
* JDBC步骤
* 1、导入相关数据库的jar包(驱动程序)
* 2、注册驱动
* 3、创建连接
* 4、编写业务SQL
* 5、创建Statement传送SQL对象
* 6、执行SQL并返回相应的处理结果
* 7、对结果进行处理
*
* 注意:jdbc涉及到的类要么统一使用java.sql包下的对象
* 或者com.mysql下的,避免强制类型转换
*/
// 设置URL时,要注意代码的编写,否则会出现异常java.sql.SQLException: No suitable driver found for jdbc://mysql://localhost:3306/c1809
private static final String URL = "jdbc:mysql://localhost:3306/c1809"; // 连接本地数据库的端口号
private static final String USERNAME = "root"; // 以root身份登录
private static final String PASSWORD = "123456"; // 登录密码
public static void step() {
Connection conn = null;
Statement stmt = null;
String sql = "";
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver"); // 注册驱动:DriverManager.registerDriver(new Driver());,如果要连接数据库一定要有该行代码
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD); // 创建连接
stmt = conn.createStatement(); // 创建Statement传送SQL对象(执行SQL语句,返回执行结果)
// 编写业务SQL,执行SQL并返回相应结果
sql = "select sid,name,age from students";
// 修改 返回 int
rs = stmt.executeQuery(sql);
// 1、判断当前行的后面是否还有数据 2、true 则把rs的cursor指向下一行
while(rs.next()) {
int sid = rs.getInt("sid");
String name = rs.getString(2);
int age = rs.getInt("age");
System.out.println(sid+"\t"+name+"\t"+age);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 资源关闭
try {
if(null != rs) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if(null != stmt) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if(null != conn) {
conn.close();
}
} catch(SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
运行结果:
1 irving 22
2 kobe 32
3 paul 28
4 durant 33
5 curry 29
6 james 39
7 hardon 29
8 libiyu 21
9 xiaoyu 20
11 byrant 21
需要注意的是: 导入的包需要一致。也就是说在创建连接的时候有两个包,一个是java提供的,另外一个是各大数据库厂商提供的,所以在这里导包的时候需要统一导入,不能一个是java的,而另一个是其他数据库厂商的,否则在传送SQL对象时就需要强制类型转换。
2.JDBC实现CRUD操作
package edu.hm.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import edu.hm.vo.Students;
public class JDBCStep1_2 {
private static final String URL = "jdbc:mysql://localhost:3306/c1809";
private static final String USER = "root";
private static final String PASSWORD = "123456";
public static void main(String[] args) {
int i = updateStu();
System.out.println(i == 1 ? "success" : "failure");
}
public static int updateStu() {
Connection conn = null;
Statement stmt = null;
String sql = "";
int i = 0;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USER, PASSWORD);
stmt = conn.createStatement();
sql = "update student set sex = '女' where sid = 19";
i = stmt.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(null != stmt) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
if(null != conn) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return i;
}
}
运行结果:
success
运行结束后,对比数据库修改前后的内容:

另外insert、delete原理类似
3.ResultSet元数据
元数据:描述数据的数据
而ReslutSet元数据就是对数据库以及对应表的字段进行操作的
package edu.hm.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import edu.hm.vo.Student;
public class JDBCMetadata {
private static final String URL = "jdbc:mysql://localhost:3306/c1809";
private static final String USER = "root";
private static final String PASSWORD = "123456";
public static void main(String[] args) {
List<Student> list = getAllStu();
}
public static List<Student> getAllStu() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Student> list = null;
String sql = "";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USER, PASSWORD);
stmt = conn.createStatement();
sql = "select sid,name familyname,age from students";
rs = stmt.executeQuery(sql);
// 获取rs对象的元数据对象
ResultSetMetaData rsmd = rs.getMetaData();
String cn = rsmd.getCatalogName(1); // 数据库名
String ccn = rsmd.getColumnClassName(2); // 对应字段类型在java中的包装类名
int cc = rsmd.getColumnCount(); // 返回列数
int cds = rsmd.getColumnDisplaySize(2); // 返回指定列的类型在数据库中定义的长度
String cl = rsmd.getColumnLabel(2); // 返回指定列修改后的字段名
String con = rsmd.getColumnName(2); // 返回指定列原始表中的字段名
int ct = rsmd.getColumnType(1); // 返回sql类型,具体整数对应类型请查看java.sql.Types
String ctn = rsmd.getColumnTypeName(1); // 返回指定列的数据类型
int gp = rsmd.getPrecision(2);
String tn = rsmd.getTableName(1); // 获取表名
System.out.println(cn+"\n"+ccn+"\n"+cc+"\n"+cds+"\n"
+cl+"\n"+con+"\n"+ct+"\n"+ctn+"\n"+gp+"\n"+tn);
} catch (Exception e) {
e.printStackTrace();
// 关闭资源
}finally {
try {
if(null != rs) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(null != stmt) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(null != conn) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
return list;
}
}
运行结果:
c1809
java.lang.String
3
20
familyname
name
4
INTEGER
20
students
4.SQL注入
客户可以通过参数传递的方式来改写原始的业务SQL,Statement对象组合SQL语句时只会把参数放入到SQL字符串中组合一个新的SQL字符串,原始参数中的SQL代码就会被看做是业务SQL中的一部分,执行时把原本为参数的值作为了SQL语句的一部分。
package edu.hm.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import edu.hm.utils.DBUtil;
public class JDBCPstmt {
// 主方法
public static void main(String[] args) {
int id = updateStu("kerr",8);
System.out.println(id > 0 ? "success" : "failure");
}
// 该函数的两个形参就是SQL注入
public static int updateStu(String name,int sid) {
Connection conn = DBUtil.getConn();
PreparedStatement pstmt = null;
ResultSet rs = null;
int id = 0;
try {
// SQL注入
// String sql = "select * from students where name = '"+name+"' and password = '"+password+"'";
// 防SQL注入
String sql = "update students set name = ? where "
+ "sid = ?";
/*
* 对每一条sql会做一个执行计划(预编译)并且存入到pstmt对象中
* 下次传入同样sql时,就直接从内存中取出已经编译好的执行计划直接赋值执行即可
*/
pstmt = conn.prepareStatement(sql);
// 给参数赋值:把传入的参数直接当做值处理,而不会重新组合SQL,这样就能防止sql注入
pstmt.setString(1, name);
pstmt.setInt(2, sid);
id = pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
// 资源关闭
DBUtil.close(conn, null, pstmt, rs);
}
return id;
}
}
5.PrepareStatement对象
①防SQL注入,提升了安全性
pstmt对象防SQL注入:
// 把特殊字符转义成普通字符
select sid from students where name='张三' and password='23456\' or \'1=1'
②预编译
预编译:v4.1前不支持,v5.x支持,但是默认是关闭的
开启预编译:
在URL地址后加上: c1809?useServerPrepStmts=true&cachePrepStmts=true"
每一条SQL在执行前都会做相应的执行计划
③可实现动态SQL:通过改变参数的值可以多次执行相同的SQL而无需重新编译
④能够提升SQL的执行效率
6.MySQL的日志
查看日志信息:–>show variables like ‘log%’;
配置:
my.ini文件中最后配置日志路径重启服务即可
log=D:\mysqlslowlog\mysql_log.log
log=D:\mysqlslowlog\mysql_error.log
log:基本信息日志
log_error:错误日志
7.JDBC中的MySQL事务处理
package edu.hm.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Savepoint;
import java.sql.Statement;
import edu.hm.utils.DBUtil;
public class JDBCTransaction {
public static void main(String[] args) {
testDTL();
}
public static void testDTL() {
Connection conn = DBUtil.getConn();
PreparedStatement pstmt = null;
Statement stmt = null;
ResultSet rs = null;
Savepoint sp = null;
try {
stmt = conn.createStatement();
// 关闭SQL自动提交事务模式
conn.setAutoCommit(false);
String sql1 = "update account set balance = balance - 100 where aid = 2";
// 设置还原点
// sp = conn.setSavepoint("p1");
String sql2 = "update account set balance = balance + 100 where aid = 3";
int a = stmt.executeUpdate(sql1);
// int i = 10/0;
int b = stmt.executeUpdate(sql2);
System.out.println((a>0 && b>0) ? "success" : "failure");
// conn.rollback(sp);
conn.commit();
String sql3 = "update account set balance = balance + 10000 where aid = 1";
int c = stmt.executeUpdate(sql3);
System.out.println(c>0?"successful":"failure");
conn.commit();
} catch (Exception e) {
e.printStackTrace();
}finally {
DBUtil.close(conn, null, pstmt, rs);
}
}
}
需要注意的是:
①如果在一个事务中执行了ddl,则当前事务会立即提交;如果一个事务在未提交状态下直接退出,事务不会提交
②当一个多条sql语句组成一个事务提交后,再开启另外一个事务的情况下,需要再一次调用conn.commit方法,否则该事务不会提交。
8.JDBC的简单封装
封装:给你什么,你进行相应的处理后你给我什么
JDBC操作中
①Connection对象只需要创建一次,CRUD操作可以基于同一个conn对象进行相应的操作,就可以把创建Connection对象进行封装,对外提供一个方法返回Connection对象
函数/方法: public static Connection getConn(){}
②JDBC创建的相应对象可以让一个方法进行同一关闭
函数/方法:public static void close(){}
package edu.hm.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtil {
private static final String URL = "jdbc:mysql://localhost:3306/c1809";
private static final String USER = "root";
private static final String PASSWORD = "123456";
private static Connection conn = null;
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Gets the JDBC-MySQL connection CONNECTION object
* @return Connection
*/
public static final Connection getConn() {
try {
// 当conn对象不等于null且对象未关闭时,共用同一个对象
if(null != conn && !conn.isClosed()) { // 这里 null != conn 与 !conn.isClosed()不能调换位置,虽然在语法结构上是合理的;但是上面
return conn;
}else {
conn = DriverManager.getConnection(URL, USER, PASSWORD);
}
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* Close the resource of JDBC
* @param conn
* @param stmt
* @param pstmt
* @param rs
*/
public static final void close(Connection conn,Statement stmt
,PreparedStatement pstmt,ResultSet rs) {
try {
if(null != rs) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(null != pstmt) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(null != stmt) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if(null != conn) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
9.单例设计模式
多次调用同一个方法对外产生同一个对象
package edu.hm.pattern;
public class Student {
private String name;
private int age;
private static Student stu = null;
// 私有的构造器,使其对外不能创建对象
private Student () {
}
/*
* 单例模式
* 1.私有的构造器
* 2.对外提供一个静态返回当前对象实例的方法
* 3.私有的静态全局对象属性
*/
public static Student getInstance() {
if(null != stu) {
return stu;
}
stu = new Student();
return stu;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试类:
package edu.hm.pattern;
public class Test {
public static void main(String[] args) {
Student stu = Student.getInstance();
Student stu1 = Student.getInstance();
Student stu2 = Student.getInstance();
Student stu3 = Student.getInstance();
System.out.println(stu);
System.out.println(stu1);
System.out.println(stu2);
System.out.println(stu3);
}
}
运行结果:
edu.hm.pattern.Student@15db9742
edu.hm.pattern.Student@15db9742
edu.hm.pattern.Student@15db9742
edu.hm.pattern.Student@15db9742
由运行结果可以得出的结论:单例模式下创建的对象都是同一个。
还有一个需要介绍的是:工厂模式
10.JDBC的批处理
批处理:一次性处理多条SQL
package edu.hm.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
import edu.hm.utils.DBUtil;
public class JDBCBatch {
/*
* 一次性插入三条记录
*/
@Test
public void test1() {
Connection conn = DBUtil.getConn();
PreparedStatement pstmt = null;
try {
String sql = "insert into address(num,bunum) values(?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "10");
pstmt.setString(2, "1002");
// 将上面的sql作为一个批处理
pstmt.addBatch();
// 执行这两个SQL语句,代码执行后,数据库插入了后面这个数据。这可以理解为是后面把前面这一条覆盖掉了
pstmt.setString(1, "11");
pstmt.setString(2, "1003");
// 将上面的sql作为一个批处理
pstmt.addBatch();
String sql1 = "insert into address(num,bunum) values('13','1004')";
// 新插入一条sql语句
pstmt.addBatch(sql1);
int[] i = pstmt.executeBatch();
for(int ii : i) {
System.out.println(ii);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(conn, null, pstmt, null);
}
}
}
运行结果:
1
1
1
11.JDBC的properties配置
①理解properties文件
class目录:存放编译文件的根目录
package edu.hm.config;
import java.io.IOException;
import java.util.Properties;
public class Config {
/*
* 把DB.properties文件加载到jvm中
* 从文件获取对应的key,从而得到key对应的值
*/
// Properties对象能够加载和管理属性文件
private static Properties PROP = new Properties();
static {
try {
// 加载src根路径下的文件
// PROP.load(Config.class.getClassLoader()
// .getResourceAsStream("DB.properties"));
// 包路径下:/src/edu/hm/config/db1.properties
PROP.load(Config.class.getClassLoader()
.getResourceAsStream("edu/hm/config/db.properties"));
// 第二种获取ClassLoader类加载器的方法
// Thread.currentThread().getContextClassLoader().getResourceAsStream("edu/hm/config/db.properties");
// 当前路径下的配置文件
// PROP.load(Config.class.getResourceAsStream("db.preporties"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static final Integer FLAG = Integer.parseInt(PROP.getProperty("FLAG"));
public static final String MYSQL_DRIVERCLASS_PATH = PROP.getProperty("MYSQL_DRIVERCLASS_PATH");
public static final String MYSQL_CONN_URL = PROP.getProperty("MYSQL_CONN_URL");
public static final String MYSQL_USERNAME = PROP.getProperty("MYSQL_USERNAME");
public static final String MYSQL_PASSWORD = PROP.getProperty("MYSQL_PASSWORD");
}
配置文件:
KEY = 123
NAME = 张三
# 0:关闭 1:开启
FLAG = 1
# mysql
MYSQL_DRIVERCLASS_PATH=com.mysql.jdbc.Driver
MYSQL_CONN_URL=jdbc:mysql://localhost:3306/c1809
MYSQL_USERNAME=root
MYSQL_PASSWORD=123456
# oracle
ORACLE_DRIVERCLASS_PATH=oracle.jdbc.driver.OracleDriver
ORACLE_CONN_URL=jdbc:oracle:thin:@localhost:1521:orcl
ORACLE_USERNAME=itstudy
ORACLE_PASSWORD=Oracle01
本文深入解析Java数据库连接(JDBC)技术,涵盖概念、基本操作步骤、CRUD实现、元数据处理、批处理、事务管理及单例设计模式等核心内容。同时,探讨了PreparedStatement对象如何提升安全性与执行效率,以及JDBC的高级封装技巧。
3820

被折叠的 条评论
为什么被折叠?



