JDBC(Java操作数据库)

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();
	}

}

执行结果如下:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值