一篇搞定JDBC【Mysql基础】

1.JDBC

Java Database Connectivity(java语言连接数据库)

2.JDBC的本质

JDBC是SUN公司制定的一套接口(interface)

import java.sql.*  (有很多接口)

接口都有调用者和实现者
面向接口调用、面向接口实现类,这都属于面向接口编程

为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力。

 多态机制就是经典的:面向抽象编程(不要面向具体编程)。

模拟JDBC

列表
上代码

JDBC接口

package 模拟JDBC本质;
/*
   SUN公司负责制定这套JDBC接口
*/
public interface JDBC {
	//连接数据库的方法
	void getConnection();
}

Mysql实现类

package 模拟JDBC本质;
/*
   MySQL的数据库厂家负责编写JDBC接口的实现类
 */
public class Mysql implements JDBC{
	
	public void getConnection() {
		//具体这里的代码涉及到mysql底层数据库的实现原理
		System.out.println("连接MySQL数据库成功!");
		
	}
}
//实现类被称为驱动。(MySQL驱动)

//xxx.jar   当中有很多.class,都是对JDBC接口进行的实现

程序员角色

package 模拟JDBC本质;

import java.util.ResourceBundle;

/*
	Java程序员角色
	不需要关心具体是那个品牌的数据库,只需要面向JDBC接口写代码
	面向接口编程,面向抽象编程,不要面向具体编程
 */
public class JavaProgrammer {

	public static void main(String[] args) throws Exception
	{
		//JDBC jdbc=new MySQL()
		//JDBC jdbc=new Oracle()
		
		//创建对象可以通过反射机制
		ResourceBundle bundle=ResourceBundle.getBundle("模拟JDBC本质.jdbc");
		String className="模拟JDBC本质."+bundle.getString("className");
		Class c=Class.forName(className);
		JDBC jdbc=(JDBC)c.newInstance();	
		//以下代码是面向接口调用方法,不需要修改
		jdbc.getConnection();

	}

}

以上代码中的配置文件 jdbc.properties

className=SqlServer

Oracle

package 模拟JDBC本质;
/*
	Oracle的数据库厂家负责编写JDBC接口的实现类
*/
public class Oracle implements JDBC {

	public void getConnection() {
		//具体这里的代码涉及到Oracle底层数据库的实现原理
		System.out.println("连接Oracle数据库成功!");
		
	}
	
}
//实现类被称为驱动。(Oracle驱动)

//xxx.jar   当中有很多.class,都是对JDBC接口进行的实现

sqlServer

package 模拟JDBC本质;
/*
	SqlServer的数据库厂家负责编写JDBC接口的实现类
*/
public class SqlServer implements JDBC {

	public void getConnection() {
		//具体这里的代码涉及到SqlServer底层数据库的实现原理
		System.out.println("连接SqlServer数据库成功!");
		
	}
}
//实现类被称为驱动。(SqlServer驱动)

//xxx.jar   当中有很多.class,都是对JDBC接口进行的实现

3.导入jar包

mysql-connector-java-5.1.7-bin.jar
mysql数据库驱动包,为了以下JDBC编程第一步需要注册。

4.JDBC编程六步

第一步:注册驱动(告诉JAVA连接哪个数据库)

//注册驱动的弟一种写法
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//注册驱动的第二种方式(常用)
//因为该方法的参数是一个字符串,字符串可以写到xxx.properties文件中
//以下方法不需要接受返回值,因为只需要类加载 加载其中的静态方法块
Class.forName("com.mysql.jdbc.Driver");

第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开,属于进程间的通讯,重量级的,使用后关闭)

//Connection conn=null;
conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","105105");

第三步:获取数据库操作对象(专门执行sql语句的对象)

//3.获取数据库操作对象
//Statement stmt=null;
stmt=conn.createStatement();

第四步:执行SQL语句(DQL DML…)

String sql="select * from emp order by ename "+keyWords+"";//keyWords  自定义字符串
rs=stmt.executeQuery(sql);

** 第五步:处理查询结果集(没有可以不处理)**

while(rs.next()) {
	System.out.println(rs.getString("ename"));
}

第六步:释放资源。

if(rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}
			if(stmt!=null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}

用户登录验证

package Test;

import java.util.*;
import java.sql.*;

/**-
   实现功能:
      1.需求:模拟用户登录功能的实现
      2.业务描述:
                程序运行的时候。提供一个输入的入口,可以让用户输入用户名和密码
                用户输入用户名和密码之后。提交信息,java程序搜集到用户信息
         java程序连接数据库验证用户名和密码是否合法
             合法:显示登陆成功
             不合法:显示登录失败
      3.数据的准备
                在实际开发中,表的设计会使用专业的建模工具,PowerDesigner                          
      4.当前程序存在的问题:
         	用户名:fdsa
			密码:fdsa' or '1'='1
			登陆成功
		这种现象称为SQL注入(安全隐患)(黑客技术)	
	  5.导致SQL注入的根本原因
	     用户输入的信息中含有sql语句的关键字,并且	
*/
public class JDBCTest06 {

	public static void main(String[] args) {
		//1.初始化一个界面
		Map<String,String> userLoginInfo=initUI();
		//2.验证用户名和密码
		boolean LoginSuccess=Login(userLoginInfo);
        //3.最后输出结果
		System.out.println(LoginSuccess ?"登陆成功" :"登陆失败");
	}
	/**
	 * 用户登录
	 * @param userLoginInfo 用户登陆信息
	 * @return false表示失败,true表示成功
	 */
    private static boolean Login(Map<String, String> userLoginInfo) {
    	//JDBC代码
    	Connection conn =null;
    	Statement stmt=null;
    	ResultSet rs=null;
    	Boolean LoginSucess=null;
    	try {
    		//1.注册驱动
    		Class.forName("com.mysql.jdbc.Driver");
    		//2.获取连接
    		conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","105105");   		
    		//3.获取数据库操作对象
    		stmt=conn.createStatement();
    		//4.执行sql
    		String sql="select * from t_user where LoginName='"+userLoginInfo.get("LoginName")+"' and LoginPwd='"+userLoginInfo.get("LoginPwd")+"' ";
    		rs=stmt.executeQuery(sql);
    		//5.处理结果集
    		if(rs.next()) {
    			LoginSucess=true;
    		}
    		else
    			LoginSucess =false;
		} catch (SQLException  | ClassNotFoundException e1) {
			e1.printStackTrace();
		}finally {
			//6.关闭资源
			if(rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(stmt!=null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}			
		}
    	return LoginSucess;		
	}
    
	/**
     * 初始化用户界面
     * @return 用户输入的用户名和密码登录信息
     */
	private static Map<String, String> initUI() {
		Scanner sr=new Scanner(System.in);
		
		System.out.print("用户名:");
		String LoginName=sr.next();
		
		System.out.print("密码:");
		String LoginPwd=sr.next();
		
		Map<String,String> userLoginInfo =new HashMap<String,String>();
		userLoginInfo.put("LoginName",LoginName);
		userLoginInfo.put("LoginPwd",LoginPwd);
		
		return userLoginInfo;
		
	}

}

存在SQL注入的问题

5.解决SQL注入的问题

只要用户提供的信息不参与SQL语句的编译过程,问题就解决了

  • 此时使用java.sql.PrepareStatment
  • PrepareStatement接口继承了Statement
  • PrepareStatement属于预编译的数据库操作对象
  • PrepareStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值

用户登录改进后

package Test;

import java.util.*;
import java.sql.*;

/**
 * 1.解决SQL注入问题
 *    只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
 *    此时使用java.sql.PrepareStatment
 *    PrepareStatement接口继承了Statement
 *    PrepareStatement属于预编译的数据库操作对象
 *    PrepareStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传值
 */
public class JDBCTest07 {

	public static void main(String[] args) {
		//1.初始化一个界面
		Map<String,String> userLoginInfo=initUI();
		//2.验证用户名和密码
		boolean LoginSuccess=Login(userLoginInfo);
        //3.最后输出结果
		System.out.println(LoginSuccess ? "登陆成功" :"登陆失败");
	}
	/**
	 * 用户登录
	 * @param userLoginInfo 用户登陆信息
	 * @return false表示失败,true表示成功
	 */
    private static boolean Login(Map<String, String> userLoginInfo) {
    	//JDBC代码
    	Connection conn =null;
    	PreparedStatement ps=null;//这里使用PreParedStatement(预编译的数据库操作对象)
    	ResultSet rs=null;
    	Boolean LoginSucess=null;
    	
    	String LoginName=userLoginInfo.get("LoginName");
    	String LoginPwd=userLoginInfo.get("LoginPwd");
    	try {
    		//1.注册驱动
    		Class.forName("com.mysql.jdbc.Driver");
    		//2.获取连接
    		conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","105105");   		
    		//3.获取预编译数据库操作对象
    		//SQL语句的框子,其中一个?表示一个占位符,用来接受一个"值"
    		String sql="select * from t_user where LoginName= ? and LoginPwd= ?";
    		ps=conn.prepareStatement(sql);//发送框子给DBMS,进行sql语句的预编译
    		//给占位符?传值 (下标从1开始)
    		ps.setString(1, LoginName);
    		ps.setString(2, LoginPwd);
    		//4.执行sql
    		rs=ps.executeQuery();
    		//5.处理结果集
    		if(rs.next()) {
    			LoginSucess=true;
    		}
    		else
    			LoginSucess =false;
		} catch (SQLException  | ClassNotFoundException e1) {
			e1.printStackTrace();
		}finally {
			//6.关闭资源
			if(rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}			
		}
    	return LoginSucess;		
	}
    
	/**
     * 初始化用户界面
     * @return 用户输入的用户名和密码登录信息
     */
	private static Map<String, String> initUI() {
		Scanner sr=new Scanner(System.in);
		
		System.out.print("用户名:");
		String LoginName=sr.next();
		
		System.out.print("密码:");
		String LoginPwd=sr.next();
		
		Map<String,String> userLoginInfo =new HashMap<String,String>();
		userLoginInfo.put("LoginName",LoginName);
		userLoginInfo.put("LoginPwd",LoginPwd);
		
		return userLoginInfo;
		
	}
}

6.对比Statement和PreparedStatement

  • Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题
  • Statement是编译一次执行一次,PreparedStatement是编译一次,可执行n次。PreparedStatement效率比较高
  • PreparedStatement会在编译阶段做类型的安全检查。
  • Statement可以传递SQL语句PreparedStatement只传值

综上所述:PreparedStatement使用较多,只有极少数的情况下需要Statement(当业务需要进行sql语句拼接的时候)。

7.演示只能使用Statement对象不能使用PreparedStatement的业务需求

用户输入sql语句

package Test;


import java.sql.*;
import java.util.*;

/**
 * 演示只能使用Statement对象不能使用PreparedStatement的业务需求
 */
public class JDBCTest08 {

	public static void main(String[] args) {
		//用户在控制台输入desc就是降序,输入asc就是升序。
		Scanner sr=new Scanner(System.in);
		System.out.println("desc-降序  asc-升序");
		System.out.print("请输入:");
		String keyWords=sr.next();
		
		//执行SQL
		Connection conn=null;
		Statement stmt=null;
		ResultSet rs=null;		
		try {
			//注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			//获取连接
			conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/company","root","105105");
			//获取数据库预编译操作对象
			stmt=conn.createStatement();
			//执行sql语句
			String sql="select * from emp order by ename "+keyWords+"";
			rs=stmt.executeQuery(sql);
			while(rs.next()) {
				System.out.println(rs.getString("ename"));
			}
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
		}finally {
			if(rs!=null) {
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}
			if(stmt!=null) {
				try {
					stmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
				
			}
			
		}

	}
	
	
	/**
	 * 使用PreparedStatement
	 */
//	public static void main(String[] args) {
//		//用户在控制台输入desc就是降序,输入asc就是升序。
//		Scanner sr=new Scanner(System.in);
//		System.out.println("desc-降序  asc-升序");
//		System.out.print("请输入:");
//		String keyWords=sr.next();
//		
//		//执行SQL
//		Connection conn=null;
//		PreparedStatement ps=null;
//		ResultSet rs=null;		
//		try {
//			//注册驱动
//			Class.forName("com.mysql.jdbc.Driver");
//			//获取连接
//			conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/company","root","105105");
//			//获取数据库预编译操作对象
//			String sql="select * from emp order by ename ?";
//			ps=conn.prepareStatement(sql);
//			ps.setString(1, keyWords);
//			//执行sql语句
//			rs=ps.executeQuery();
//			while(rs.next()) {
//				System.out.println(rs.getString("ename"));
//			}
//		} catch (ClassNotFoundException | SQLException e) {
//			e.printStackTrace();
//		}finally {
//			if(rs!=null) {
//				try {
//					rs.close();
//				} catch (SQLException e) {
//					e.printStackTrace();
//				}
//				
//			}
//			if(ps!=null) {
//				try {
//					ps.close();
//				} catch (SQLException e) {
//					e.printStackTrace();
//				}
//				
//			}
//			if(conn!=null) {
//				try {
//					conn.close();
//				} catch (SQLException e) {
//					e.printStackTrace();
//				}
//				
//			}
//			
//		}
//		
//	}//                           报错显示SQL语法有误

}

8.JDBC事务控制

JDBC事务机制

  •    JDBC中的事务是自动提交的
    
  •    只要执行任意一条DML语句,则自动提交一次,这是JDBC默认的事务行为
    
  •    但是在实际业务中,通常是N条DML语句共同联合才能完成的。
    
  •    必须保证这些DML在同一个事物中同时成功或者同时失败。
    

三段重要代码

   conn.setAutoCommit(false);//关闭自动提交
   conn.commit;//提交事务

   catch(EXception e){//程序异常
      if(conn!=null){
         conn.rollback;//手动回滚
      }
   }

应用于数据库用户之间的转账

package Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
 * JDBC事物的控制
 */
public class JDBCTest11 {

	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement ps=null;
		
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","105105");
			
			conn.setAutoCommit(false);//开启事务
			
			String sql="update t_act set balance= ? where actno= ?";
            ps=conn.prepareStatement(sql);
            ps.setDouble(1,10000);
            ps.setInt(2, 111);
            int count=ps.executeUpdate();
            
            ps.setDouble(1,10000);
            ps.setInt(2, 222);
            count+=ps.executeUpdate();
            System.out.println(count==2 ? "转账成功":"转账失败");
            conn.commit();//提交事务
			
		} catch (Exception e) {
			//如果出现异常,
			if(conn!=null) {
				try {
					conn.rollback();//回滚事务
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}finally {
			if(ps!=null) {
				try {
					ps.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
			if(conn!=null) {
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
			}
		}
		

	}
}

9.悲观锁和乐观锁机制

悲观锁:事务必须排队执行,数据锁住了,不允许并发(行级锁:select后面添加for update)
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

举栗子说明

第一个事务使用悲观锁,锁定相关数据

package Test;
/**
 * 这个程序开启一个事务,这个事务专门进行查询,并且使用行级锁/悲观锁,锁住相关的数据
 */
import java.sql.*;

import utils.DBUtil;
public class JDBCTest13 {

	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		
		try {
			conn=DBUtil.getConnection();
			//开启事物
			conn.setAutoCommit(false);
			
			String sql="select ename,job,sal from emp where job =? for update";
			ps=conn.prepareStatement(sql);
			ps.setString(1, "manager");			
			rs=ps.executeQuery();
			while(rs.next()) {
				System.out.println(rs.getString("ename")+","+rs.getString("job")
				+","+rs.getDouble("sal"));
			}
			

			//提交事务
			conn.commit();
		} catch (Exception e) {
			if(conn!=null) {
				try {
					//回滚事务,事务结束
					conn.rollback();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}finally {
			DBUtil.close(conn, ps, rs);
		}

	}

}

第二个事务负责修改被锁定的记录

package Test;
/**
 *这个程序负责修改被锁定的记录
 */
import java.sql.*;

import utils.DBUtil;
public class JDBCTest14 {

	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement ps=null;
		try {
			conn=DBUtil.getConnection();
			conn.setAutoCommit(false);
			
			String sql="update emp set sal=sal*1.1 where job=?  ";
			ps=conn.prepareStatement(sql);
			ps.setString(1, "manager");
			int count=ps.executeUpdate();
			System.out.println(count);
			
			conn.commit();
		} catch (Exception e) {
			if(conn!=null) {
				try {
					conn.rollback();
				} catch (SQLException e1) {
					// TODO 自动生成的 catch 块
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}

	}

}

总结:第一个事务没有结束,第二个事务需要排队

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值