java学习中级篇之JDBC

DML增删改 insert delete update
DQL select查询
DDL create alter drop
curd
1:JDBC所处地位(Java database connectivity)
jdbc就是用java语言操作数据库
JDBC是用于在Java语言编程中与数据库连接的API,是java本身自带的api,在java.sql包中,有接口和实现类。

期望使用一套统一的java代码去操作所有的关系数据库。这套代码就是JDBC。
JDBC是操作所有关系型数据库的规则。就是一堆接口。
驱动就是JDBC接口的实现类
在这里插入图片描述
Java提供访问数据库规范称为JDBC,也就是一堆接口,而生产厂商提供规范的实现类称为驱动
2:JDBC的开发步骤
步骤介绍
0导入jar包
1.注册驱动
告知JVM使用的是哪一个数据库的驱动
2.获得连接
使用JDBC中的类,完成对MySQL数据库的连接
3.获得语句执行平台
通过连接对象获取对SQL语句的执行者对象
4.执行sql语句
使用执行者对象,向数据库执行SQL语句
获取到数据库的执行后的结果
5.处理结果
6.释放资源 一堆close()

最先是导入jar包,jar包是一个压缩包,一堆class文件的压缩包

注册驱动:

该方法会造成二次注册,导致浪费

import com.mysql.jdbc.Driver;

public class jdbc_test {
	public static void main(String[] args) throws SQLException {
		// TODO Auto-generated method stub
		//1:注册驱动
		//使用java.sql.DriverManager类静态方法 registerDriver(Driver driver)
		//其中Driver 是接口类,传入的参数则是接口类的实现类,接口类的实现类是通过jar包来实现的
		//相当于 接口引用 指向 接口实现类对象
		//其中new Driver()是通过import com.mysql.jdbc.Driver来实现的
		DriverManager.registerDriver(new Driver());

	}
}

下面我们看一下Driver的源码

当上面代码 new Driver()时,会使得Driver中的静态代码块执行,导致二次注册

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
            //会造成注册两次,浪费了
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

推荐用以下方式进行驱动注册

原因是:Class.forname是使用反射机制使Driver类加载入内存中,Class.forName是对类的主动使用,导致静态代码块执行,然后触发了改类内部的静态代码块,就完成了注册。

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
	}
}

获得连接

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");//注册驱动
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		//url指定链接的路径
		//jdbc:mysql://是固定写法
		//localhost 本机ip地址
		//3306 端口号
		//madatabase 数据库名称
		java.sql.Connection con = DriverManager.getConnection(url, user, password);	
		//con是连接对象
		//DriverManager的静态方法getConnection,返回值类型是sqp.Connection
		//con值是jdbc.connection4类型的,也就是说其实现类是该类型的
	}
}

获得语句执行平台
通过连接对象获取对SQL语句的执行者对象

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
		Statement stat =  con.createStatement();//获取了sql语句的执行者对象
		//创建一个Statement对象,用于将SQL语句发送到数据库
//PreparedStatement prepareStatement(String sql)
//创建一个PreparedStatement对象,用于将 参数化 的SQL语句发送到数据库。 

	}
}

执行sql语句
使用执行者对象,向数据库执行SQL语句,获取到数据库的执行后的结果。
excuteUpdate()
但是只能进行 增 删 改

返回值是影响的行数

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
		Statement stat =  con.createStatement();
		String yuju = "INSERT INTO sort(sname,sprice,sdesc) VALUES('投影仪','1234','突然降价')";
		//只能 insert,delete,update
		int row = stat.executeUpdate(yuju);
		//返回的row是插入或者删除记录的行数
		//其实就是插入不一样,其余的都一样固定格式
		System.out.println(row);
		stat.close();
		con.close();
	}
}

执行sql语句
查询操作
executeQuery

返回一个ResultSet接口的实现类,也就是一个结果集
如何处理这个结果集,如下
判断结果集中有没有元素用next()方法

.next()方法  返回值是boolean类型

遍历结果集
方法:用getXXX方法,引号内放列名
Xxx:代表数据类型
也可以统一用getString()
re.next()其中re游标在第一个元素之前,可以理解为空
执行next()的过程是,先移动,若没有元素,返回false,有就返回true

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
		Statement stat =  con.createStatement();
		String yuju = "SELECT * FROM sort;";
		ResultSet rs = stat.executeQuery(yuju);
		while(rs.next())
		{
			System.out.println(rs.getInt("sid")+rs.getString("sname")+rs.getDouble("sprice"));
		}
		stat.close();
		con.close();
	}
}

注入攻击:
就是让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分

CREATE TABLE users
(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(100),
	PASSWORD VARCHAR(100)
);

INSERT INTO users(username,PASSWORD) VALUES ('a',1),('b',2);

SELECT * FROM users;

SELECT * FROM users WHERE username = 'a' AND PASSWORD = 1 OR TRUE;
//这里已经与上true ,则整个where表达式是true,就可以登陆成功,这就是注入攻击

注入攻击在java代码中的体现
查询结果为全部数据 但是代码中给的用户名和密码均是假的

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
		Statement stat =  con.createStatement();
		String yuju = "SELECT * FROM users WHERE username = 'g' AND PASSWORD = 3 OR TRUE;";
		ResultSet rs = stat.executeQuery(yuju);
		while(rs.next())
		{
			System.out.println(rs.getString("username")+rs.getString("password"));
		}
		stat.close();
		con.close();
	}
}

有人会说是因为已经提前写好了select语句
现将其改为手动输入
注意一个问题:就是引号的使用

但是在输入时输入 1’ or 1=1 or’ 就可以破解

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
		Statement stat =  con.createStatement();
		Scanner sc = new Scanner(System.in);
		String username = sc.nextLine();
		String pass = sc.nextLine();
		String yuju = "SELECT * FROM users WHERE username = '"+username+"' AND PASSWORD = '"+pass+"';";
		//正常的select语句是usrname = '' 是要用单引号扩上的,所以单引号保持不变,留着,然后用双引号分割
		//分割字段如下
		//"SELECT * FROM users WHERE username = '"
		//username
		//"' AND PASSWORD = '"
		//"';"
		ResultSet rs = stat.executeQuery(yuju);
		while(rs.next())
		{
			System.out.println(rs.getString("username")+rs.getString("password"));
		}
		stat.close();
		con.close();
	}
}

防止攻击注入
statement接口实现类执行sql查询语句,返回结果集
statement借口有个子接口是preparedstatement接口
preparedstatement接口实现类由connection接口的prepareStatement方法调用进行返回
并将所有参数改为? 比如 ‘"+username+"’ 改为 ?

public class jdbc_test {
	public static void main(String[] args) throws SQLException, ClassNotFoundException {
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);		
//		Statement stat =  con.createStatement();
		Scanner sc = new Scanner(System.in);
		String username = sc.nextLine();
		String pass = sc.nextLine();
//		String yuju = "SELECT * FROM users WHERE username = '"+username+"' AND PASSWORD = '"+pass+"';";
		String yuju = "SELECT * FROM users WHERE username = ? AND PASSWORD = ?;";
		//将变量名字用?占位符代替
		PreparedStatement pst = con.prepareStatement(yuju);
		//预编译
		pst.setObject(1, username);
		pst.setObject(2, pass);
		ResultSet rs = pst.executeQuery();
		//无需使用pst.executeQuery(yuju),因为已经预编译完了
		while(rs.next())
		{
			System.out.println(rs.getString("username")+rs.getString("password"));
		}
		pst.close();
		con.close();
	}
}

使用防止注入的方法插入数据
实质上防止攻击注入的方法实质是接口预编译

ublic class insertdemo {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);	
		String sql = "INSERT INTO users(username,PASSWORD) VALUES (?,?)";
		PreparedStatement pst = con.prepareStatement(sql);
		pst.setObject(1, "a");
		pst.setObject(2, 333);
		pst.execute();
		pst.close();
		con.close();
	}
}

使用接口预编译实现查询

public class insertdemo {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Class.forName("com.mysql.jdbc.Driver");
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		java.sql.Connection con = DriverManager.getConnection(url, user, password);	
		String sql = "SELECT * FROM users;";
		PreparedStatement pst = con.prepareStatement(sql);
		ResultSet rs = pst.executeQuery();
		while(rs.next())
		{
			System.out.println(rs.getString(1)+rs.getString(2)+rs.getString(3));
		}
		pst.close();
		con.close();
	}
}

JDBC的工具类
目的,链接数据库的那一堆代码不需要重复写,给他封装成一个类就行。
关闭数据库的一对也不用。写成close函数
注意connection导入的包是java.sql.Connection接口包
1:私有构造函数
2:提供静态方法供外界直接调用获得connection接口实现类
3:重复代码放到static静态代码块中
4:静态代码块触发条件

  1. 创建类的实例
  2. 使用类的静态变量,或者为静态变量赋值
  3. 调用类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    重复语句方法静态代码块里
import java.sql.Connection;
public class jdbcutil {
	private jdbcutil() {}
	public static Connection con;
	static
	{
		try {
			Class.forName("com.mysql.jdbc.Driver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String user = "root";
		String password = "111";
		String url = "jdbc:mysql://localhost:3306/mydatabase";
		try {
			con = DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}
	public static Connection getconnection()
	{
		return con;
	}
	public static void close(Connection con,Statement St,ResultSet rs) throws SQLException
	{
		con.close();
		St.close();
		rs.close();
		//结果集也要关闭
	}
}

从数据库sort表中中读出记录,并将其写入对象,并输出对象

next()方法,最开始指针的位置再第一个元素的前面,先判断,下一个元素是否存在,若存在,则移动指针。

sort类在下面

public class importobj {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Connection con  = jdbcutil.getconnection();
		PreparedStatement pst = con.prepareStatement("SELECT * FROM users;");
		ResultSet rs = pst.executeQuery();
		List<sort> a = new ArrayList(); 
		while(rs.next())
		{
			a.add(new sort(rs.getInt(1),rs.getString(2),rs.getString(3)));
			//注意不能一味的用getString因为id是int定义的,赋予String会报错
		}
		for(sort i:a)
		{
			System.out.println(i);
		}
		jdbcutil.close(con, pst);
	}
}

重写toString的意义
在Object类里面定义toString()方法的时候返回的对象的哈希code码
这个hashcode码不能简单明了的表示出对象的属性。所以要重写toString()方法。
什么时候调用toString方法
当打印对象时

public class sort {
	private int id;
	private String username; 
	private String password;
	public sort(int id, String username, String password) {
		this.id = id;
		this.username = username;
		this.password = password;
	}
	public sort() {
	}	
	public class sort {
	private int id;
	private String username; 
	private String password;
	public sort(int id, String username, String password) {
		this.id = id;
		this.username = username;
		this.password = password;
	}
	public sort() {
	}	
	public String toString() {
		return "Sort [sid=" + id + ", sname=" + username + ", sprice=" + password+"]";
	}				
}
}

sort表
在这里插入图片描述
properties配置文件
使用properties配置文件的原因是,由于连接数据库时的驱动,URL,用户名,密码都是一成不变的
使用properties配置文件就可以在不改动源码的情况下,让用户只改动配置文件就可以完成对驱动,URL,用户名,密码的更改。
配置文件要放在src文件目录下,因为交付工程时,只向用户交付bin文件夹,该文件夹下都是class文件,若交付整个工程文件夹,那下次开发也就没你事了。eclipse会自动将配置文件从src文件夹下复制到bin文件夹下,保证了交付的bin文件夹下还有配置文件。

具体做法:
在src文件夹下创建一个file文件,后缀名是.properties
在其中放入键值对,如下形式

driverClass=com.mysql.jdbc.Driver
user=root
password=111
url=jdbc:mysql://localhost:3306/mydatabase

加载properties配置文件,并将加载配置文件的类也做成工具类
做法:IO读取文件,键值存储到集合,从集合中以键值对的方式获取数据库的链接

public class propertiesdemo {
	private propertiesdemo(){}
	private static Connection con;
	private static String driverClass1;
	private static String user1;
	private static String password1;
	private static String url1;
	static 
	{
		try
		{
			loadconfig();
			Class.forName(driverClass1);
			con = DriverManager.getConnection(url1, user1, password1);
		}
		catch(Exception e)
		{
			throw new RuntimeException("数据库连接失败");
		}
		
	}
	private static void loadconfig() throws Exception
	{
		InputStream a = propertiesdemo.class.getClassLoader().getResourceAsStream("database.properties");
		//先获取properties类文件字节码对象propertiesdemo.class
		//再通过getClassLoader()获得类的构造器
		//调用构造器中的方法getResourceAsStream来获得io流
		Properties pro  = new Properties();
		pro.load(a);
		将io流加载到集合中
		driverClass1 = pro.getProperty("driverClass");
		user1 = pro.getProperty("user");
		password1 = pro.getProperty("password");
		url1 = pro.getProperty("url");
	}
	public static Connection getconnection()
	{
		return con;
	}
	public static void close(Connection con,Statement St,ResultSet rs) throws SQLException
	{
		con.close();
		St.close();
		rs.close();
	}
		
}

测试链接是否建立

public class test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Connection con = propertiesdemo.getconnection();
		System.out.println(con);
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值