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:静态代码块触发条件
- 创建类的实例
- 使用类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式来强制创建某个类或接口对应的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);
}
}