Java操作数据库
1.加载数据库驱动,Class.forName(“数据库驱动的字符串”)-----调用的方法上声明了异常:throws ClassNotFoundException
2.连接数据库,产生连接对象connection,DriverManager.getConnection(url, username, password)-----调用的方法上声明了异常:throws SQLException
3.通过连接对象,使用准备执行sql语句的方法,得到句柄对象。PreparedStatement pstmt = connection.prepareStatement(sql语句)
4.最后要通过Java垃圾回收器,自动回收对象,connection.close()-----自动回收由Java虚拟机决定,顺序/时间不一定,是不确定的,不是靠代码决定的。
public class DB {
private String driver;
private String url;
private String user;
private String pwd;
// 构造函数的作用,初始化类中的变量
public DB(String driver, String url, String user, String pwd) {
this.driver = driver;
this.url = url;
this.user = user;
this.pwd = pwd;
}
public void connDataBase() {
Connection connection = null;
try {
// 1.加载数据库驱动
Class.forName(this.driver);
// 2.获取数据库连接对象
connection = DriverManager.getConnection(url, user, pwd);
System.out.println("连接数据库成功" + connection);
return;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
System.out.println("SQLException");
e.printStackTrace();
} catch (Exception e) {
System.out.println("Exception");
e.printStackTrace();
}
finally {
//java垃圾回收器,自动回收对象
//Java的自动回收由Java虚拟机决定,顺序/时间不一定,是不确定的,不是靠代码决定的
if(connection != null)
{
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// System.out.println("业务流程执行结束");//结束不代表业务成功或失败
System.out.println("连接数据库失败");
}
public static void main(String[] args) {
DB db = new DB("com.mysql.cj.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/jk202508", "root", "123456");
db.connDataBase();
}
}
5.Java对数据库表的操作
(1)增加,删除,修改
主要是调用executeUpdate方法。
下面通过向数据库中的表添加数据的两种实现方式来体现JDBC:
1.抽象类
- 抽象父类AbstractDB里实现了连接数据库的实例方法connDB。
- 子类实现重写父类的OperatorSQL方法具体SQL操作(UserAddDao/StuAddDao)。
- 参数使用Object类型进行动态绑定,因为执行的对象不一样。
package com.demo12;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
//抽象父类
public abstract class AbstractDB {
protected Connection conn;
public void connDB() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
//抽象方法 :1.执行的sql不一样,2.执行的对象不一样
public abstract void OperatorSQL(Object obj);
public void closeConn() {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.demo12;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class StuAddDao extends AbstractDB{
@Override
public void OperatorSQL(Object obj) {
this.connDB();
String sql= "insert into t_stus(sname,saddress) values(?,?)";
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
if(obj instanceof Stus)
{
Stus s = (Stus) obj;
pstmt.setString(1, s.getSname());
pstmt.setString(2, s.getSaddress());
}
pstmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
this.closeConn();
}
}
}
package com.demo12;
public class Stus {
private String sname;
private String saddress;
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSaddress() {
return saddress;
}
public void setSaddress(String saddress) {
this.saddress = saddress;
}
}
package com.demo12;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UserAddDao extends AbstractDB {
@Override
public void OperatorSQL(Object obj) {
this.connDB(); //调用父类的连接方法
String sql = "insert into t_user(uname,upwd) values(?,?)"; //在SQL语句中的问号 ? 是参数占位符
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
if (obj instanceof User) {
User u = (User) obj;
pstmt.setString(1, u.getUname()); //给参数占位符设置值,下标从1开始
pstmt.setString(2, u.getUpwd());
}
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeConn();
}
}
}
package com.demo12;
public class User {
private String uname;
private String upwd;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpwd() {
return upwd;
}
public void setUpwd(String upwd) {
this.upwd = upwd;
}
}
package com.demo12;
public class Test {
public static void main(String[] args) {
User u = new User();
u.setUname("张三");
u.setUpwd("11223344111");
AbstractDB db = new UserAddDao();
db.OperatorSQL(u);
Stus s = new Stus();
s.setSname("李四");
s.setSaddress("南京");
AbstractDB db1 = new StuAddDao();
db1.OperatorSQL(s);
}
}
执行结果如下:
2.抽象类+接口
- 连接数据库的代码写在了抽象父类AbstractDB的构造函数里,因为创建子类一定会依赖于父类的构造函数。抽象父类是为了执行的对象不一样。
- 定义Dao接口,里面定义了抽象方法,子类重写方法执行不同的SQL语句。
- 子类DBDao同时继承抽象父类和实现接口方法。
- 参数使用Object类型进行动态绑定,因为执行的对象不一样。
package com.demo13;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public abstract class AbstractDB {
protected Connection conn;
public AbstractDB()
{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void closeConn()
{
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.demo13;
public interface Dao {
public void addDatas(Object obj);
public void updateDatas(Object obj);
}
package com.demo13;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DBDao extends AbstractDB implements Dao {
@Override
public void addDatas(Object obj) {
String sql = "insert into t_stus(sname,saddress) values(?,?)";
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
if (obj instanceof Stus) {
//向下转型
Stus s = (Stus) obj;
pstmt.setString(1, s.getSname());
pstmt.setString(2, s.getSaddress());
}
pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
this.closeConn();
}
}
@Override
public void updateDatas(Object obj) {
}
}
package com.demo13;
public class Stus {
private String sname;
private String saddress;
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSaddress() {
return saddress;
}
public void setSaddress(String saddress) {
this.saddress = saddress;
}
}
package com.demo13;
public class Test {
public static void main(String[] args) {
Stus s = new Stus();
s.setSname("鹿鹿");
s.setSaddress("北京");
DBDao db = new DBDao();
db.addDatas(s);
}
}
执行结果如下:
基于以上两种实现方式,思考以下问题:
1.为什么要设计抽象类或接口?
(1)抽象类主要是处理不变的公共代码,实现代码复用,如将连接数据库的代码提升到父类。
(2)接口主要是定义可变的数据操作,如子类重写接口中的方法,实现不同的SQL语句。
2.为什么抽象方法中的参数是Object类型,而不是子类的具体类型? 为什么会做向下转型?
(1)因为在抽象方法/接口方法定义时无法预知所有可能的子类类型,先把参数设置成Object类型,即顶级父类,后续通过动态绑定可以灵活地调用子类的方法。
(2)向下转型主要是做个保护,确保运行时对象的正确性和防止传入错误类型的对象。
3.为什么把连接数据库,关闭数据库连接的代码抽取到父类(普通或抽象父类中)?
(1)避免每个子类重复编写相同的连接代码,实现代码复用。
(2)实现关注点分离,父类主要处理基础逻辑(如连接管理);子类专注业务逻辑(如SQL操作)。
(3)实现一致性控制,确保所有子类使用相同的连接方式和统一异常处理策略。
(2)查询
主要是调用executQuery方法。
执行完会得到一个表的结果集:ResultSet rs = pstmt.executeQuery();这个结果集就是表的二维结构,类似于下面这张表:
结果集里包含:
(1)表的列的信息:ResultSetMetaData rsmd = rs.getMetaData();
(2)表中的数据
public class App {
Connection conn;
public void connDB() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/jk202508", "root", "123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 得到表的列的信息
public void queryColumnInfo(String tableName) {
this.connDB();
try {
PreparedStatement pstmt = conn.prepareStatement("select * from " + tableName);
// 结果集 表的列的信息+表中的数据
ResultSet rs = pstmt.executeQuery();
// ResultSetMetaData包含了表的列的信息
ResultSetMetaData rsmd = rs.getMetaData();
int columns = rsmd.getColumnCount();
System.out.println("总共有几列:" + columns);
for (int i = 0; i < columns; i++) {
// 表中的列的下标不同于数组,下标是从1开始的
String cname = rsmd.getColumnName(i + 1);
System.out.println("列名为:" + cname);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
// 得到表中的数据
public void queryColumnData() {
this.connDB();
try {
PreparedStatement pstmt = conn.prepareStatement("select * from t_stus");
// 结果集 表的列的信息 + 表中的数据
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
int id = rs.getInt(1);
String sname = rs.getString(2);
String saddress = rs.getString(3);
System.out.println(id + "," + sname + "," + saddress);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
App t = new App();
// t.connDB();
t.queryColumnInfo("t_stus");
// t.connDB();
t.queryColumnData();
}
}
执行结果如下: