1、JDBC是什么?
Java DataBase Connectivity(Java语言连接数据库)
2、JDBC的本质是什么?
JDBC是SUN公司制定的一套接口(interface)
java.sql.*; (这个软件包下有很多接口。)
接口都有调用者和实现者。各大数据库管理系统厂家去实现这些接口,我们负责调用这些接口
面向接口调用、面向接口写实现类,这都属于面向接口编程。
为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力。
多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
建议:
Animal a = new Cat();
Animal a = new Dog();
// 喂养的方法
public void feed(Animal a){ // 面向父类型编程。
}
不建议:
Dog d = new Dog();
Cat c = new Cat();
为什么SUN制定一套JDBC接口呢?
因为每一个数据库的底层实现原理都不一样。
Oracle数据库有自己的原理。
MySQL数据库也有自己的原理。
MS SqlServer数据库也有自己的原理。
…
每一个数据库产品都有自己独特的实现原理。
JDBC的本质到底是什么?
一套接口。
驱动,所有的数据库驱动都是以jar包的形式存在,jar包当中有很多class文件,这些class文件就是对JDBC接口的实现(数据库公司的Java程序员写的,下载驱动jar包需要去数据库官网下载)
驱动就是实现接口的类文件的一个别名
如果没有驱动,就是没有实现类,能编译不能运行,因为底层要new对象
3、JDBC编程六步
第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行SQL语句(DQL DML…)
DML:executeUpdate,返回值为int,指的是影响数据库内容的条数
DQL:executeQuery返回的是结果集ResultSet
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
while(rs.next()){}
第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)
在finally中操作,先判断是否等于空再进行操作,释放查询结果集,释放数据库操作对象,释放连接对象
url:统一资源定位符(网络中某个资源的绝对路径)URL包括哪几部分?
协议
IP
PORT
资源名
百度的ip地址ping出来的
http://,通信协议
39.156.66.18 服务器IP地址
80 服务器上软件的端口,上网端口,
(IP是计算机的代号,端口是计算机上某个软件的代号)
以上内容是把本地计算机和百度服务器(计算机)通道打开了
index.html 服务器(计算机)上某个资源名,(要这个页面)
localhost和127.0.0.1都是本机IP地址
IDEA配置数据库驱动
就相当于配环境变量了
新建工程之后,新建模块,然后右键模块,F4,打开模块设置
点Libraries(库),加号,点Java
新建模块还要重新导入
JDBC六步
普通
import java.sql.*;
public class Test01 {
public static void main(String[] args) {
Connection conn=null;
Statement statement=null;
ResultSet resultSet=null;//如果执行的是查询语句就有这个
try {
//第一步,注册驱动
//Driver这个类有静态代码块
//类加载Class.forName("com.mysql.jdbc.Driver");
//因为参数是个字符串,字符串可以写在xxx.properties文件中(属性资源文件)
Class.forName("com.mysql.jdbc.Driver");
//第一步,注册驱动
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//第二步,获取链接
String url="jdbc:mysql://127.0.0.1:3306/xrh";
String user="root";
String password="666";
conn=DriverManager.getConnection(url,user,password);//面向接口编程,Connection是个接口
//第三步,获取数据库操作对象(statement负责执行sql语句)
statement=conn.createStatement();
// //第四步,执行sql
//
// String sql ="insert into dept (deptno,dname,loc) values('50','2bu','北京')";//插入数据
// // String sql1 ="delete from dept where deptno=50";//删除数据
// String sql2="update dept set dname='3bu',deptno=60 where deptno =50 ";//修改数据
//
// int count=statement.executeUpdate(sql);
// //int count1=statement.executeUpdate(sql1);
// int count2=statement.executeUpdate(sql2);
//第五步,处理查询结果集
String sql0="select empno,ename,sal from emp";//列表也可以重命名
resultSet=statement.executeQuery(sql0);//返回结果集,不是集合
//遍历结果集
while (resultSet.next()){
//resultSet.getString()不管数据库中是什么类型的数据,取出来的都是字符串类型的
//还有resultSet.getInt(),resultSet.getDouble()但是得看数据库中的数据类型
String empno=resultSet.getString(1);//第1列
String ename=resultSet.getString(2);
String sal=resultSet.getString(3);
System.out.println(empno+","+ename+","+sal);
String empno1=resultSet.getString("empno");//用列名,数据库中的列名
String ename1=resultSet.getString("ename");
String sal1=resultSet.getString("sal");
System.out.println(empno+","+ename+","+sal);
}
} catch (Exception e) {//本来是SQLException,因为类加载所以扩大了范围
e.printStackTrace();
}finally {
//第六步,释放资源
//为了保证资源一定释放,在finally语块中关闭资源
//后开先关,因为上边大括号的statement,conn两个变量这里没法用,得挪到括号外边
//分开try
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement!=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
使用配置文件
这里没成功 ,因为.java程序和配置文件不在一个包下
import java.sql.*;
import java.util.ResourceBundle;
public class Ceyan {
public static void main(String[] args) {
//使用资源绑定器绑定配置文件
ResourceBundle bundle=ResourceBundle.getBundle("jdbc");//jdbc.properties
String driver=bundle.getString("drive");
String url=bundle.getString("url");
String user=bundle.getString("user");
String password=bundle.getString("password");
Statement statement=null;
Connection connection=null;
try {
Class.forName(driver);
connection=DriverManager.getConnection(url,user,password);
statement=connection.createStatement();
String sql="insert into dept (deptno,dname,loc) values('50','2bu','北京')";
statement.executeUpdate(sql);
} catch (Exception throwables) {
throwables.printStackTrace();
}finally {
if(statement!=null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
用户登陆
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
实现功能:
1,需求:
模拟用户登录功能
2,业务描述:
程序运行时,提供一个输入的入口,输入用户名和密码
java连接数据库验证是否合法,提示登陆失败或者成功
3,数据准备:
PowerDesigner设计表
4,用户名:
sad
密码:
asdd' or '1'='1
登陆成功
输入1=1,也会成功,叫sql注入现象,sql语句完成拼接,编译后运行,编译的时候将用户输入的非法信息编译进去了,导致原sql语句被扭曲
sql注入现象:用户输入的信息中有sql的关键字,并且这些关键字参与sql语句编译,导致被扭曲,导致注入
*/
public class Test02 {
public static void main(String[] args) {
//初试化界面,初试化界面这个方法要有个返回值吧就是用户输入的
Map<String,String> map=initUI();//这个方法返回的值是Map类型的
boolean b=login(map);
System.out.println(b?"登陆成功":"登陆失败");
}
/*
用户登陆是否成功,jdbc
*/
private static boolean login(Map<String, String> map) {
//打标记的意识
boolean loinSuccess=false;
String u=map.get("用户名");
String p=map.get("密码");
Connection conn=null;
Statement statement=null;
ResultSet resultSet=null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xrh","root","666");
statement=conn.createStatement();
//数据变量拼接'xxx'-->把xxx删了,加两个双引号在中间加两个加号,在加号之间写变量就行了
String sql="select * from t_user where loginName='"+u+"' and loginPwd='"+p+"'";
resultSet= statement.executeQuery(sql);
if(resultSet.next()){
//如果是true,登陆成功,因为上面sql查出来就一条,查不出来就没
loinSuccess=true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loinSuccess;
}
private static Map<String, String> initUI() {
Scanner s=new Scanner(System.in);
System.out.println("用户名: ");
String username=s.nextLine();
System.out.println("密码: ");
String password=s.nextLine();
Map<String,String>map=new HashMap<>();
map.put("用户名",username);
map.put("密码",password);
return map;
}
}
上面存在SQL注入现象
sql注入现象:用户输入的信息中有sql的关键字,并且这些关键字参与sql语句编译,导致被扭曲,导致注入
解决SQL问题:
只要用户提供的信息不参与SQL编译的过程
使用java.sql.PreparedStatement继承了java.sql.Statement接口,PreparedStatement原理:将SQL框架预编译,然后传值
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Test02 {
public static void main(String[] args) {
//初试化界面,初试化界面这个方法要有个返回值吧就是用户输入的
Map<String,String> map=initUI();//这个方法返回的值是Map类型的
boolean b=login(map);
System.out.println(b?"登陆成功":"登陆失败");
}
/*
用户登陆是否成功,jdbc
*/
private static boolean login(Map<String, String> map) {
//打标记的意识
boolean loinSuccess=false;
String u=map.get("用户名");
String p=map.get("密码");
Connection conn=null;
PreparedStatement ps =null;
ResultSet resultSet=null;
try {
//第一步,注册驱动
Class.forName("com.mysql.jdbc.Driver");
//第二步,获取连接
conn= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xrh","root","666");
//第三步,获取预编译的数据库操作对象
String sql="select * from t_user where loginName=? and loginPwd=? ";//sql是框架,?表示占位符,传值
ps=conn.prepareStatement(sql);
//给占位符传值(第一个问号下标是1)
ps.setString(1,u);
ps.setString(2,p);
//第四步,执行sql
resultSet= ps.executeQuery();
//第五步,处理结果集
if(resultSet.next()){
//如果是true,登陆成功,因为上面sql查出来就一条,查不出来就没
loinSuccess=true;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loinSuccess;
}
private static Map<String, String> initUI() {
Scanner s=new Scanner(System.in);
System.out.println("用户名: ");
String username=s.nextLine();
System.out.println("密码: ");
String password=s.nextLine();
Map<String,String>map=new HashMap<>();
map.put("用户名",username);
map.put("密码",password);
return map;
}
}
Statement和preStatement
在mysql里面,如果上一条语句跟下一条语句完全一样包括空格,第二次运行的时候就不用编译了。所以Statement执行一次sql编译一次,preStatement是传值之前就编译了,所以只需要编译一次就行。preStatement还会进行传值类型安全检查
只能用statement的时候,就是查询排序升序的时候
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Scanner;
public class StatementTest {
public static void main(String[] args) {
Scanner s =new Scanner(System.in);
System.out.println("请输入desc或者asc: ");
String keywords =s.nextLine();
//jdbc
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/xrh","root","666");
String sql="select ename,empno from emp order by ename ?";
ps=conn.prepareStatement(sql);
ps.setString(1,keywords);
rs=ps.executeQuery();
while (rs.next()){
String ename =rs.getString("ename");
String empno=rs.getString("empno");
System.out.println(ename+" "+empno);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的例子是传值,因为输入的desc被当作字符串传进去了,所以语法错误
经过测试,删除多条数据时,连续写几条删除语句只执行最后一个删除语句,那么怎么同时删除多个数据呢????
如果是PreparedStatement,设置完问号的值,要进行ps.executeUpdate()这个执行sql,有几个这执行几次。Statement也是如此
import java.sql.*;
public class StatementTest {
public static void main(String[] args) {
//jdbc
Connection conn = null;
PreparedStatement ps = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xrh", "root", "666");
// String sql = "insert into dept(deptno,dname,loc )values(?,?,?)";
// ps = conn.prepareStatement(sql);
// ps.setString(1, "112");
// ps.setString(2, "rensh2ibu");
// ps.setString(3, "bei2jng");
//String sql="update dept set deptno=? where deptno=?这样为什么不行
// String sql="update dept set dname=?,loc=? where deptno=?";
// ps=conn.prepareStatement(sql);
// ps.setString(1,"110");
// ps.setString(2,"shanghai");
// ps.setInt(3,110);
// String sql="delete from dept where deptno=?";
// ps=conn.prepareStatement(sql);
// ps.setInt(1,112);
String sql1="delete from dept where deptno=?";
ps=conn.prepareStatement(sql1);
ps.setInt(1,110);
// String sql2="delete from dept where deptno=?";
// ps=conn.prepareStatement(sql2);
// ps.setInt(1,60);
int count = ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
if (ps != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
}
JDBC事务自动提交
JDBC事务默认是自动提交的,执行一次DML语句就自动提交一次。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*
重点三行代码:都是围绕conn的
conn.setAutoCommit(false);
conn.commit();
conn.rollback();
*/
public class Shiwu {
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/xrh","root","666");
conn.setAutoCommit(false);
String sql="update t_act set balance=?where acount=?";
ps =conn.prepareStatement(sql);
ps.setDouble(1,10000);
ps.setInt(2,111);
ps.executeUpdate();
ps.setDouble(1,10000);
ps.setInt(2,222);
ps.executeUpdate();
conn.commit();
} catch (Exception e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
e.printStackTrace();
}finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
JDBC工具类
工具类构造方法一般是私有的,因为工具类的方法都是静态的,通过类名点方法名调用,不需要new对象,私有化之后就构造不了了
import java.sql.*;
public class JDBCS {
private JDBCS(){}
//静态代码块,在类加载时执行,只执行一次
{
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
//返回连接对象
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/xrh","root","666");
}
//关闭
public static void close(Connection con, Statement statement, ResultSet resultSet ){
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}if ( con!= null) {
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
测试工具类,并进行模糊查询
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcTest {
public static void main(String[] args) {
Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//获取连接
con=JDBCS.getConnection();
//String sql="select ename from emp where ename like '_?%'";//没有问号
String sql="select ename from emp where ename like ?";
ps=con.prepareStatement(sql);//编译
ps.setString(1,"_A%");//第二个字母是A的
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//释放资源
JDBCS.close(con,ps,rs);//如果第三个参数没就传null
}
}
}
悲观锁和乐观锁
select 语句在后面加上for update,就被锁上了,在当前事务没有结束的时候这一行数据不能修改,这是行级锁也叫悲观锁
悲观锁:事务必须排队,数据锁住了,不允许并发
乐观锁:支持并发,事务不需要排队,只不过需要一个版本号