day04_JDBC笔记

JDBC
* mysql这三天的课程mysql语法 sql语言只能是mysql认识的
* 从基础班到反射 Java语言只能是Java语言认识的
//我们现在的目的是想用java语言来操作数据库
一、 概念:Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
* JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。
各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

二、快速入门:
	* 步骤:
		1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
			//为什么要导入jar包?
				因为jdk中没有mysql的驱动jar包,但是我现在需要使用它,把需要的jar就添加进来
			
			1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下  //导入的一定是jar包,其他的类型不行
			2.右键-->Add As Library
		2. 注册驱动
			//为什么要注册驱动?  Class.forName("com.mysql.jdbc.Driver");
				创建类的字节码文件对象					
		
		3. 获取数据库连接对象 Connection
			//相当于一座桥
			
		4. 定义sql
		5. 获取执行sql语句的对象 Statement
			//相当于一个小车
		
		6. 执行sql,接受返回结果
		7. 处理结果
		8. 释放资源
			//拆桥毁车

	* 代码实现:
		//1. 导入驱动jar包
		//2.注册驱动
		Class.forName("com.mysql.jdbc.Driver");
		//3.获取数据库连接对象
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
		//4.定义sql语句
		String sql = "update account set balance = 500 where id = 1";
		//5.获取执行sql的对象 Statement
		Statement stmt = conn.createStatement();
		//6.执行sql
		int count = stmt.executeUpdate(sql);
		//7.处理结果
		System.out.println(count);
		//8.释放资源
		stmt.close();
		conn.close();

三、 详解各个对象:
	1. DriverManager:驱动管理对象
		* 功能:
			1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
				static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。 
				写代码使用:  Class.forName("com.mysql.jdbc.Driver");
				通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
				 static {
						try {
							java.sql.DriverManager.registerDriver(new Driver());
						} catch (SQLException E) {
							throw new RuntimeException("Can't register driver!");
						}
					}

				注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。  //不建议省略 Class.forName("com.mysql.jdbc.Driver");  省略后会影响执行效率,不能向下兼容

		2. 获取数据库连接:
				* 方法:static Connection getConnection(String url, String user, String password) 
					代码实现:
						        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/java33", "root", "root");

					
				* 参数:
					* url:指定连接的路径
						* 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
							//注意:数据库名称前面是一条斜杠,不能写成两条斜杠
						* 例子:jdbc:mysql  ://  localhost:3306/db3
								https       ://  www.baidu.com     (153.37.235.4:80)
						
						* 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
							//使用省略写法的时候,一定要保证你的mysql端口号是3306
						
					* user:用户名
					* password:密码 


	2.Connection:数据库连接对象
		1. 功能:
			1. 获取执行sql 的对象
				* Statement createStatement()   //这个方法就是用来获取小车的
					代码实现:
						Statement statement = connection.createStatement();
					
				* PreparedStatement prepareStatement(String sql)  
			2. 管理事务:
				* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
				* 提交事务:commit() 
				* 回滚事务:rollback() 

	3.Statement:执行sql的对象
		1. 执行sql
			1. boolean execute(String sql) :可以执行任意的sql //了解 
			2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句  //掌握
				* 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
			3. ResultSet executeQuery(String sql)  :执行DQL(select)语句

		2.练习
			(1)新增数据
				Statement stmt = null;
				Connection conn = null;
				try {
					//1. 注册驱动
					Class.forName("com.mysql.jdbc.Driver");
					//2. 定义sql
					String sql = "insert into account values(null,'王五',3000)";
					//3.获取Connection对象
					conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
					//4.获取执行sql的对象 Statement
					stmt = conn.createStatement();
					//5.执行sql
					int count = stmt.executeUpdate(sql);//影响的行数
					//6.处理结果
					System.out.println(count);
					if(count > 0){
						System.out.println("添加成功!");
					}else{
						System.out.println("添加失败!");
					}
		
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (SQLException e) {
					e.printStackTrace();
				}finally {
					//stmt.close();
					//7. 释放资源
					//避免空指针异常
					if(stmt != null){
						try {
							stmt.close();
						} catch (SQLException e) {
							e.printStackTrace();
						}
					}
		
					if(conn != null){
						try {
							conn.close();
						} catch (SQLException e) {
							e.printStackTrace();
						}
					}
				}

			//以后开发中只要涉及到  "释放资源"  的代码,一定要放在finally中释放
			
			//(1)try{
					可能出现异常的代码
				}catch(Exception e){
					对异常的处理
				}finally{
					释放资源
				}
				
				//(2)什么时候需要判空?
					//引用类型调用方法的时候
		
			//小建议:在使用Java代码操作数据库数据之前,一定要先将sql在小海豚上执行一遍,保证sql语句的正确性

(2)修改数据
			除了sql语句和添加数据不同,其他的都一模一样

		(3)删除语句
			//在写JDBC的时候,一定要保证sql语句的正确性
	
	4.ResultSet:结果集对象,封装查询结果
		* boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
			//做了两件事:  (1)将游标向下移动一位   (2)判断是否有数据   
		
		* getXxx(参数):获取数据
			* Xxx:代表数据类型   如: int getInt() ,	String getString()
			* 参数:
				1. int:代表列的编号,从1开始   如: getString(1)   //如果字段过多的话,容易数错
				2. String:代表列名称。 如: getDouble("balance")   //推荐使用  
		
			//    mysql                  Java
				 varchar                 String
				   int                    int
				 double                  double
			date/datetime				  date
			
		
		* 注意:
			* 使用步骤:
				1. 游标向下移动一行
				2. 判断是否有数据
				3. 获取数据

			//循环判断游标是否是最后一行末尾。
            while(rs.next()){
                //获取数据
                //6.2 获取数据
                int id = rs.getInt(1);
                String name = rs.getString("name");
                double balance = rs.getDouble(3);

                System.out.println(id + "---" + name + "---" + balance);
            }

		//对比结果集和迭代器的两种遍历方式
			结果集 :依赖了两个方法
				(1)next()
					*将游标向下移动一位
					*判断是否还有数据
				(2)getXxx
					//*获取的某一行的某一列数据(简单的说就是一行中的一个数据)
			
			迭代器:依赖了两个方法
				(1)hashNext()
					*判断是否有下一行数据
				(2)next()
					*取当前的数据
					*将游标向下移动一位
		
	* 练习:    //非常重要   晚上一定要敲
		* 定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回。
			1. 定义Emp类
			2. 定义方法 public List findAll(){}
			3. 实现方法 select * from emp;
			
			//从案例看数据库和Java的对应关系
				数据库              Java 
				
				 表                  类
			  字段(列名)         成员变量
			  每一条记录            对象
				 
			//在Java中新建一张和数据库表字段对应的类,这样的类所在的包的名字是有要求的
				常用的包的名字有三个  
					*pojo
					*domain
					*entity

代码实现:
package demo01;

import domain.Fruit;

import java.sql.*;
import java.util.ArrayList;
//ResultSet遍历结果集存入ArrayList集合并遍历
public class Demo03 {
public static void main(String[] args) {
ResultSet rs = null;
Statement stat = null;
Connection con = null;
try {
Class.forName(“com.mysql.jdbc.Driver”);
con = DriverManager.getConnection(“jdbc:mysql:///db2”, “root”, “root”);
stat = con.createStatement();
String sql = “select * from a”;
rs = stat.executeQuery(sql);
ArrayList array = new ArrayList<>();
while (rs.next()) {
int id = rs.getInt(“A_ID”);
String name = rs.getString(“A_NAME”);
Fruit f = new Fruit(id, name);
array.add(f);
}
for (Fruit f : array) {
System.out.println(f.getA_ID()+"="+f.getA_NAME());
}

    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}

}

四、JDBC工具类   //晚上一定要敲
	1.工具类的书写要求
		(1)包名的要求
			必须以util或者utils结尾   //  com.java33.utils

		(2)类名的要求
			类名也必须以util或者utils结尾  //JDBCUtils.java
				
		(3)构造方法私有
		(4)类中的所有方法都要使用static修饰

	2.配置文件的要求(结合properties使用)
		(1)配置文件中的内容也要是键值对形式,通常以=分隔
		(2)配置文件的结尾不能有多余的空格
		(3)末尾不能有分号

	3.相对路径问题
		(1)回忆一下IO流中的相对路径
			相对于我们当前的项目(project)的,而不是相对于module,和module是平级关系
		
		(2)类加载器中的相对路径
				// InputStream resourceAsStream = Demo3.class.getClassLoader().getResourceAsStream("jdbc.properties");

			从表面看:相对于src目录
			从本质上看:相对于out目录里面当前模块下

代码实现:
package Util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtil {
private static String url;
private static String user;
private static String password;
private static String driver;

static {
    try {
        Properties p = new Properties();
        InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
        p.load(is);
        url = p.getProperty("url");
        user = p.getProperty("user");
        password = p.getProperty("password");
        driver = p.getProperty("driver");
        Class.forName(driver);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

public static Connection getConnection()  {
    try {
        return DriverManager.getConnection(url, user, password);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }

}

public static void close(Statement stat, Connection conn) {
    if (stat != null) {
        try {
            stat.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

public static void close(ResultSet rs, Statement stat, Connection conn) {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (stat != null) {
        try {
            stat.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

}

	4.登录案例
		 String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";
		原则:单引号里面套双引号    双引号里面要套单引号

	5. PreparedStatement:执行sql的对象
		1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
			1. 输入用户随便,输入密码:a' or 'a' = 'a
			2. sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a' 

		2. 解决sql注入问题:使用PreparedStatement对象来解决
		3. 预编译的SQL:参数使用?作为占位符
		4. 步骤:
			1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
			2. 注册驱动
			3. 获取数据库连接对象 Connection
			4. 定义sql
				* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
			5. 获取执行sql语句的对象 PreparedStatement  Connection.prepareStatement(String sql) 
			6. 给?赋值:
				* 方法: setXxx(参数1,参数2)
					* 参数1:?的位置编号 从1 开始
					* 参数2:?的值
			7. 执行sql,接受返回结果,不需要传递sql语句
			8. 处理结果
			9. 释放资源

		5. 注意:后期都会使用PreparedStatement来完成增删改查的所有操作
			1. 可以防止SQL注入
			2. 效率更高
			
		//问题
			(1)sql注入是怎么产生的?
				我们使用了sql字符串的 "拼接"
				
			(2)怎么解决sql注入问题?
				使用PreparedStatement对象来解决
		
			(3)PreparedStatement对象(预编译)是怎么解决sql注入问题的?
				预编译的意思就是,先把sql语句结构定死,参数使用?(占位符)的方式来填充,给?赋值的数据只能作为和数据库中数据对比的参数

代码实现:
package demo01;

					import Util.JDBCUtil;
					
					import java.sql.Connection;
					import java.sql.PreparedStatement;
					import java.sql.ResultSet;
					import java.sql.SQLException;
					import java.util.Scanner;
					
					public class Demo06 {
					    public static void main(String[] args) {
					        Scanner sc = new Scanner(System.in);
					        System.out.println("请输入用户名:");
					        String name = sc.nextLine();
					        System.out.println("请输入密码:");
					        String password = sc.nextLine();
					        Connection conn = null;
					        PreparedStatement ps = null;
					        ResultSet rs = null;
					        try {
					            conn = JDBCUtil.getConnection();
					            String sql = "select * from stu where name = ? and password = ?";
					            ps = conn.prepareStatement(sql);
					            ps.setString(1, name);
					            ps.setString(2, password);
					            rs = ps.executeQuery();
					            if (rs.next()) {
					                System.out.println("登录成功");
					            } else {
					                System.out.println("登录失败");
					            }
					        } catch (SQLException e) {
					            e.printStackTrace();
					        } finally {
					            JDBCUtil.close(rs,ps,conn);
					        }
					    }
					}

JDBC控制事务:
1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
2. 操作:
1. 开启事务
2. 提交事务
3. 回滚事务
3. 使用Connection对象来管理事务
* 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务
* 提交事务:commit()
* 当所有sql都执行完提交事务
* 回滚事务:rollback()
* 在catch中回滚事务

4.mysql中的事务和Java代码是怎么对应起来的

	mysql              				 		 Java 

start transaction ----------------- conn.setAutoCommit(false);
commit ---------------------- conn.commit();
rollback -------------------- conn.rollback();

5.注意事项
	(1)事务是由Connection这个类来管理的
		
	(2)Java中事务的代码结构
		try{
			可能出现异常的代码(执行多个dml操作的语句)
			最后有一个conn.commit();
			
		}catch(RuntimeException e){
			conn.rollback();
		}finally{
			释放资源
		}
	
	代码实现:
					package demo01;
			
			import Util.JDBCUtil;
			
			import java.sql.Connection;
			import java.sql.PreparedStatement;
			import java.sql.SQLException;
			
			public class Demo07 {
			    public static void main(String[] args) {
			        Connection conn = null;
			        PreparedStatement ps1 = null;
			        PreparedStatement ps2 = null;
			
			        try {
			            conn = JDBCUtil.getConnection();
			            conn.setAutoCommit(false);
			            String sql1 = "update stu set password = password - ? where id =?";
			            String sql2 = "update stu set password = password + ? where id =?";
			            ps1 = conn.prepareStatement(sql1);
			            ps2 = conn.prepareStatement(sql2);
			            ps1.setInt(1, 500);
			            ps1.setInt(2, 1);
			            ps2.setInt(1, 500);
			            ps2.setInt(2, 2);
			            ps1.executeUpdate();
			            int i = 2 / 0;
			            ps2.executeUpdate();
			            conn.commit();
			        } catch (Exception e) {
			            if(conn!=null){
			                try {
			                    conn.rollback();
			                } catch (SQLException ex) {
			                    ex.printStackTrace();
			                }
			            }
			            e.printStackTrace();
			        } finally {
			            JDBCUtil.close(ps1, conn);
			            JDBCUtil.close(ps2, null);
			        }
			    }
			}

你可能感兴趣的:(MySQL,mysql,jdbc)