JavaWeb--JDBC
JavaWeb--JDBC
概念
- Java Database Connectivity (Java数据库连接)
- Java语言操作数据库
JDBC的本质
- JAVA官方定义的一套操作所有关系型数据库的规则,即接口,各个数据库厂商去实现这套接口提供数据库驱动jar包,我们可以使用这套接口(JDBC)编程,真正执行的代码时驱动jar包中的实现类
入门
-
1.导入驱动jar包 mysql-connector-java-8.0.30.jar
- 把mysql驱动jar包复制到lib目录(没有就新建命名一个)下,然后选中驱动jar包右击,选择 Add as Library...
-
2.注册驱动
-
Class.forName("com.mysql.cj.jdbc.Driver");
-
-
3.获取数据库的连接对象 Collection
-
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "123456");
-
-
4.定义sql
-
String sql = "update account set balance = 500 where id = 1";
-
-
5.获取执行sql语句的对象 Statement
-
Statement stm = conn.createStatement();
-
-
6.执行sql,接收返回结果
-
int count = stm.executeUpdate(sql);
-
-
7.处理结果
-
System.out.println(count);
-
-
8.释放资源
-
stm.close(); conn.close();
-
JDBC--DriverManager类
功能
注册驱动
-
告诉程序该使用哪个数据库驱动jar
-
static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager
-
写代码使用Class.forName("com.mysql.cj.jdbc.Driver");
-
找到jar包下Driver类的源码
-
public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
- 回顾静态代码块:
- 使用static关键字修饰的代码块称为静态代码块,当类被加载的时候,静态代码块就会被执行,由于类只会加载一次,所以静态代码块只会执行一次
-
-
注意:mysql5之后的驱动包可以省略注册驱动的步骤
获取数据库连接
-
static Connection getConnection(String url, String user, String password)
试图建立一个连接到给定的数据库的网址。-
url:指定连接的路径
-
语法(因数据库而异,此处是mysql)
-
jdbc:mysql://ip地址(域名):端口号/数据库名称
-
如果是本机的mysql服务器,并且默认的端口为3306,可以简写:jdbc:mysql:///数据库名称
-
-
user:用户名
-
password:密码
-
DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "123456");
-
JDBC--Connection类
获取执行sql的对象
-
Statement createStatement() -- 创建用于向数据库发送SQL语句的一 Statement对象。 PreparedStatement prepareStatement(String sql) -- 创建参数化的SQL语句发送到数据库的 PreparedStatement对象。
管理事务
-
开启事物:
-
void setAutoCommit(boolean autoCommit) -- 将此连接的自动提交模式设置为给定的状态。
-
-
提交事务
-
void commit() -- 使自上次提交/回滚永久释放数据库的Connection对象目前持有的所有更改。
-
-
回滚事务
-
void rollback() -- 撤消所有更改在当前事务并释放任何数据库锁的 Connection对象目前持有。
-
JDBC--Statement类
执行sql的对象
-
用于执行静态SQL语句并返回其生成的结果的对象。
-
boolean execute(String sql) -- 执行给定的SQL语句,这可能会返回多个结果。 做了解
-
int executeUpdate(String sql) -- 执行给定的SQL语句,这可能是DML: INSERT , UPDATE ,或 DELETE语句,或者不返回任何内容,如SQL DDL语句的SQL语句。
- 此处的int返回值表示影响的行数,可以通过这个影响行数的int判断DML语句是否执行成功,返回值大于0则执行成功,反之失败
-
ResultSet executeQuery(String sql) -- 执行给定的SQL语句,该语句返回单个 ResultSet对象。
- 执行DQL(select)语句,返回一个结果集对象
练习
-
account表 添加一条insert语句
public static void main(String[] args) { Statement ste = null; Connection conn = null; try { //注册驱动 Class.forName("com.mysql.cj.jdbc.Driver"); //定义sql String sql = "insert into account values(null,'wangwu',3000)"; //获取Collection对象 conn = DriverManager.getConnection("jdbc:mysql:///db3","root","123456"); //获取执行sql对象statement ste = conn.createStatement(); //执行sql语句 int count = ste.executeUpdate(sql); if(count>0) { System.out.println("添加数据成功!已影响"+count+"行数据。"); }else{ System.out.println("添加失败"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { //避免空指针一场 if(ste!=null){ try { ste.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
JDBC--RestultSet类
-
ResultSet executeQuery(String sql) -- 执行给定的SQL语句,该语句返回单个 ResultSet对象。
- 执行DQL(select)语句,返回一个结果集对象
-
结果集对象,封装查询结果
-
next()游标向下一行
-
getxxx(参数):获取数据,xxx代表数据类型,如int getInt() getString
- 参数
- int:代表列的编号,从1开始 如:getString(1)
- String:代表列的名称,如:getDouble("balance")
- 参数
-
使用步骤
-
游标向下移动一行
-
判断是否有数据
-
获取数据
-
boolean next() -- 将光标从当前位置向前移动一行。
-
代码实现(使用封装方法完成判定循环)
public static void main(String[] args) {
Statement ste = null;
Connection conn = null;
ResultSet rs = null;//也需要关闭资源
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//定义sql
String sql = "select * from account";
//获取Collection对象
conn = DriverManager.getConnection("jdbc:mysql:///db3","root","123456");
//获取执行sql对象statement
ste = conn.createStatement();
//使用ResultSet的语句执行sql语句
rs = ste.executeQuery(sql);
//处理结果(此处使用封装方法)
printResult(rs);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//避免空指针一场
if(ste!=null){
try {
ste.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void printResult(ResultSet rs) throws SQLException {
System.out.println("数据读取完成,你所查询的数据为:");
while(rs.next()){
//获取数据
int id = rs.getInt(1);
String name = rs.getString(2);
double balance = rs.getDouble(3);
//打印数据
System.out.println("id:"+id+","+"姓名:"+name+","+"金额:"+balance+"元");
}
}
练习--select语句
-
查询emp表的数据将其封装为对象,装载集合,返回,然后打印
- 定义表的类
- 定义方法
- 实现方法
-
代码
public class JdbcDemo04 { public static void main(String[] args) { JdbcDemo04 jb = new JdbcDemo04(); List<Emp> result = jb.findAll(); //遍历 for (Emp emp : result) { System.out.println(emp); } } //定义一个方法,查询emp表的数据将其封装成对象,然后装载集合,返回 /** * 查询所有emp对象 * @return */ public List<Emp> findAll(){ List<Emp> emps = new ArrayList<Emp>(); Statement ste = null; Connection conn = null; ResultSet rs = null; try { Class.forName("com.mysql.cj.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "123456"); ste = conn.createStatement(); String sql = "select * from emp"; rs = ste.executeQuery(sql); while(rs.next()){ int id = rs.getInt("id"); String ename = rs.getString("ename"); int job_id = rs.getInt("job_id"); int mgr = rs.getInt("mgr"); Date joindate = rs.getDate("joindate"); //这里的Date是sql包下的,而定义的成员变量是util包下的date,是他的子类,所以可以装载 double salary = rs.getDouble("salary"); double bouns = rs.getDouble("bonus"); int dept_id = rs.getInt("dept_id"); //创建emp对象 Emp emp = new Emp(id,ename,job_id,mgr,joindate,salary,bouns,dept_id); emps.add(emp); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally { if(ste!=null){ try { ste.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } return emps; } }
JDBC--工具类
- 由于上面的练习,反复代码操作太多且繁琐,需要做一个JDBC的工具类来实现简化操作
- 建立抽取JDBC工具类:JDBCUtils
- 简化书写
- 抽取一个方法注册驱动
- 抽取一个方法获取连接对象
- 需求:不需要传递参数,还得保证工具类的通用性
- 解决:配置文件
- 抽取一个方法释放资源
- 简化书写
工具类代码
//配置文件jdbc.properties内容范例
url=jdbc:mysql:///db3
user=xxxx
password=xxxxxx
driver=com.mysql.cj.jdbc.Driver
//JDBC工具类
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/**
* 文件的读取,只需要读取一次就可以拿到这些值,使用静态代码块
*/
static {
try {
//创建Properties集合类
Properties pro = new Properties();
//获取scr路径下路径文件的方式--类加载器
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//加载文件
pro.load(is);
// pro.load(new FileReader("D:\\java study\\JDBC\\src\\com\\util\\jdbc.properties")); 绝对路径,但不泛用
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
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);
}
/**
* 释放资源
* @param ste
* @param conn
* @param rs
*/
public static void colse(Statement ste, Connection conn, ResultSet rs){
if(ste!=null){
try {
ste.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
操作类代码
public class JdbcDemo04 {
public static void main(String[] args) {
JdbcDemo04 jb = new JdbcDemo04();
List<Emp> result = jb.findAll();
//遍历
for (Emp emp : result) {
System.out.println(emp);
}
}
//定义一个方法,查询emp表的数据将其封装成对象,然后装载集合,返回
/**
* 查询所有emp对象
* @return
*/
//使用JDBC自建工具类简化
public List<Emp> findAll(){
List<Emp> emps = new ArrayList<Emp>();
Statement ste = null;
Connection conn = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();//此处替换
ste = conn.createStatement();
String sql = "select * from emp";
rs = ste.executeQuery(sql);
while(rs.next()){
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
//这里的Date是sql包下的,而定义的成员变量是util包下的date,是他的子类,所以可以装载
double salary = rs.getDouble("salary");
double bouns = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
//创建emp对象
Emp emp = new Emp(id,ename,job_id,mgr,joindate,salary,bouns,dept_id);
emps.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.colse(ste,conn,rs);//此处替换
}
return emps;
}
}
JDBC--登陆案例
-
需求
- 通过键盘录入用户名和密码
- 判断用户是否登录成功
-
步骤
-
创建数据库表
-
create table users( id int primary key auto_increment, username varchar(20), password varchar(20) ); insert into users values(null,'shijuanjun','xu12345'); INSERT INTO users VALUES(NULL,'xuzhiyuan','xu98765');
-
创建一个类完成登录的方法
public class JdbcDemo05 {
public static void main(String[] args) {
String username = null;;
String password = null;;
Scanner sc = new Scanner(System.in);
while(true) {
System.out.println("请输入帐号:");
if (sc.hasNext()) {
username = sc.next();
}
System.out.println("请输入密码:");
if (sc.hasNext()) {
password = sc.next();
}
boolean b = login(username, password);
if (b) {
System.out.println("登陆成功");
break;
} else {
System.out.println("帐号或密码错误,登录失败,重试");
System.out.println("需要重新登录请输入1,退出系统请输入2");
int i = sc.nextInt();
if(i == 1){
continue;
}else if(i == 2){
break;
}
}
}
}
public static boolean login(String username, String password) {
if(username == null || password == null){
return false;
}
Statement ste = null;
Connection conn = null;
ResultSet rs = null;
boolean b = false;
try {
conn = JDBCUtils.getConnection();//调用JDBC工具类获取配置文件连接数据库
ste = conn.createStatement();
// String sql = "select * from users";
String sql = "select * from users where username = '"+username+"' and password = '"+password+"'";//改良
rs = ste.executeQuery(sql);
// while (rs.next()) {
// String username1 = rs.getString("username");
// String password1 = rs.getString("password");
// if (username1.equals(username) && password1.equals(password)) {
// b = true;
// break;
// }
// }
return rs.next();//改良
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.colse(ste, conn, rs);//JDBC工具类简化关闭
}
return b;
}
JDBC--PreparedStatement类
-
SQL注入问题
- 上面的练习随便输入用户,输入密码:a' or 'a' = 'a 会登入成功
- SQL问题是 再拼接sql的时候,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
-
解决SQL注入问题:使用PreparedStatement对象来解决
-
PreparedStatement对象:表示预编译的SQL语句的对象。
-
预编译的SQL:sql参数使用 ? 作为占位符
-
select * from users where username = ? and password = ?; (上面SQL练习的改良)
-
PreparedStatement prepareStatement(String sql) 创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。
-
使用connection对象调用上面方法传入sql语句
-
给?赋值
- 方法:setXXX(参数1,参数2)
- 参数1,问号的位置,从1开始
- 参数2,问号的值
- 方法:setXXX(参数1,参数2)
-
修改后的登录方法代码
public static boolean login(String username, String password) {
if(username == null || password == null){
return false;
}
PreparedStatement prste = null;
Connection conn = null;
ResultSet rs = null;
boolean b = false;
try {
conn = JDBCUtils.getConnection();//调用JDBC工具类获取配置文件连接数据库
String sql = "select * from users where username = ? and password = ?;";//改良
prste = conn.prepareStatement(sql);
prste.setString(1,username);
prste.setString(2,password);
// String sql = "select * from users";
rs = prste.executeQuery();
// while (rs.next()) {
// String username1 = rs.getString("username");
// String password1 = rs.getString("password");
// if (username1.equals(username) && password1.equals(password)) {
// b = true;
// break;
// }
// }
return rs.next();//改良
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.colse(prste, conn, rs);//JDBC工具类简化关闭
}
return b;
}
JDBC--管理事务
概念
- 如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
操作
-
开启事物:
-
void setAutoCommit(boolean autoCommit) -- 将此连接的自动提交模式设置为给定的状态。
- 在执行sql之前开启事务
-
-
提交事务
-
void commit() -- 使自上次提交/回滚永久释放数据库的Connection对象目前持有的所有更改。
- 当所有sql执行完执行提交事务
-
-
回滚事务
-
void rollback() -- 撤消所有更改在当前事务
- 在异常catch中回滚事务
-
public class JdbcDemo06 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement prste1 = null;
PreparedStatement prste2 = null;
try {
//获取连接
conn = JDBCUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
//定义sql语句
//张三 -500
String sql1 = "update account set balance = balance - ? where name = ?";
//李四 +500
String sql2 = "update account set balance = balance + ? where name = ?";
prste1 = conn.prepareStatement(sql1);
prste2 = conn.prepareStatement(sql2);
prste1.setDouble(1, 500);
prste1.setString(2, "zhangsan");
prste2.setDouble(1, 500);
prste2.setString(2, "lisi");
prste1.executeUpdate();
//手动制造异常
int ii = 3/0;
prste2.executeUpdate();
//提交事务
conn.commit();
} catch (Exception e) {//不管遇到什么异常都要回滚所以用最大异常
e.printStackTrace();
//出现错误在异常处回滚
try {
if (conn != null) {
conn.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
JDBCUtils.colse(prste1, conn);
JDBCUtils.colse(prste2, null);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了