JDBC

一、JDBC是什莫?

Java database connectivity(Java语言连接数据库)

1.1JDBC的本质是什莫?

是一套接口(在Java.sql.*下)

为什么要面向接口编程?

解耦合,降低程序的耦合度,提高程序扩展力!

1.2JDBC编程六步

第一步:加载驱动 (告诉程序使用的是哪个品牌的数据库)

第二步:建立连接 (打开与数据库的通道)

第三步:获取数据库对象 (使用数据库对象进行对数据的操作)

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

第五步:对结果集进行处理 (只有当进行DQL才需要对结果集进行处理)

第六步:释放资源 (Java与数据库之间是进程的通信,需要关闭进程)

1.3 具体代码

第一步:加载驱动 (告诉程序使用的是哪个品牌的数据库)

java.sql.DriverManager 下有static registerDirver(Driver driver);用来注册驱动,参数是Driver类型的(Driver是Java.sql包下的接口类,需要其实现类)Driver的实现类是com.mysql.jdbc包下的Driver类 故有Driver driver= new com.mysql.jdbc.Driver();//多态 Java.sql.Driver父类引用指向子类com.mysql.jdbc.Driver对象

//第一种加载驱动的方式
Driver driver=new com.mysql.jdbc.Driver();
DriverManager.registerDriver(diver);
//第二种加载驱动的方式
//因为com.mysql.jdbc.Driver里有静态代码块,此代码块里的代码就是第一种加载驱动方式的代码,直接使用反射机制
Class.forName("com.mysql.jdbc.Driver");

第二步:建立连接 (打开与数据库的通道)

使用java.sql.DriverManager包下的getConnection(String url,String user,String password);进行数据库与jvm的连接。

url:统一资源定位符(网络中某个资源的绝对路径)

例如:https://www.baidu.com/就是url。

url包括几个部分:协议,ip,端口,资源名

https:// 协议;182.61.200.7 服务器ip地址 80 端口号 index.html 资源名

jdbc:mysql//协议 127.0.0.1 ip地址 3306 端口号 student 数据库实例名

说明:localhost 和 127.0.0.1都是本机地址

String url="jdbc:mysql//127.0.0.1:3306/student";
//String url=“jdbc:oracle:thin:@localhost:1521:”
String user="root";
String password="123";
Connection conn=DriverManager.getConnection(String url, String user, String password) ;

第三步:获取数据库对象 (使用数据库对象进行对数据的操作)

Statement state=conn.createStatement();

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

String sql="insert into dept values(50,‘cehuabu‘,zhengzhou)";
int count=state.executeUpdate(sql);//专用于Dml 返回值是影响的行数

第五步:对结果集进行处理 (只有当进行DQL才需要对结果集进行处理)

ResultSet rs=state.executeQuery(sql);//sql必须是DQL
//遍历结果集1
/*while(rs.next()){
String sno=rs.getString(1);//getString的返回值一定是String类型,不管sno是什么类型都字符串返回。参数可以是下标
String sname-rs.getString(2);
}*/
//遍历结果集2
while(rs.next()){
int sno=rs.getInt(‘sno‘);//要是想要返回指定类型需要这样写(getDouble,getInt...)参数可以写字段名,但此字段名必须是得到的表的字段名(如:把sno as a)则此时获取值的字段名就为‘a’;
String sanme=rs.getString(‘sname‘);
}

第六步:释放资源 (Java与数据库之间是进程的通信,需要关闭进程)

需要使用在finally里try...catch....

关闭规则从小到大,先关闭state,再关闭conn

state.colse();
conn.close();

 

//用户登录代码

//主方法

public static void main(String[] args){
  //用户登录界面
  Map<String,String > uselogin=LoginUI();
  //用户登录
  boolean result= userJdbc(uselogin);
  System.out.println(result?"登录成功":"登录失败");
}

//用户登录界面

private static Map<String, String> LoginUI() {
  Scanner in =new Scanner(System.in);
  System.out.println("请输入用户名:");
  String username=in.next();
  System.out.println("请输入密码:");
  String password=in.next();
  //封装用户名与密码
  Map<String,String> users=new HashMap<>();
  users.put("username",username);
  users.put("password",password);
  return users;
}

//用户信息连接数据库

private static boolean userJdbc(Map<String,String > uselogin) {
  Connection conn=null;
  Statement state=null;
  ResultSet rs=null;
  boolean b=false;
  try {
      //1、加载驱动
      Class.forName("com.mysql.jdbc.Driver");
      //2、建立连接
      String url="jdbc:mysql://127.0.0.1:3306/uses?useUnicode=true&characterEncoding=gbk";
      String user="root";
      String password="root";
      conn = DriverManager.getConnection(url, user, password);
      //3、获取数据库操作对象
      state = conn.createStatement();
      //4、执行sql语句(DQL,DML)
      String sql = "select * from user where username=‘"+uselogin.get("username")+"‘ and password=‘"+uselogin.get("password")+"‘";
      rs=state.executeQuery(sql);
      //5、处理结果集
      if(rs.next()){
          b=true;
      }
?
  } catch (SQLException throwables) {
      throwables.printStackTrace();
  } catch (ClassNotFoundException e) {
      e.printStackTrace();
  } finally {
      //6、释放资源
      if (rs != null) {
          try {
              rs.close();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
      }
      if (state != null) {
          try {
              state.close();
          } catch (SQLException e) {
              e.printStackTrace();
          }
      }
      if (conn != null) {
          try {
              conn.close();
          } catch (SQLException e) {
              e.printStackTrace();
          }
      }
  }
  return b;
}

以上代码存在bug,如:

请输入用户名:
fds
请输入密码:
fsd‘or‘1‘=‘1
登录成功

这种现象成为SQL注入:用户输入的信息与DQL语句进行了拼接

select * from user where username=‘fds‘ and password=‘fsd‘ or ‘1‘=‘1‘;

二、解决SQL注入现象

SQL注入是因为非法信息与SQL语句进行了拼接并编译,这样会造成数据表结构遭到攻击破坏 解决方法是首先对SQL语句进行结构编译,在接收用户信息的时候不会对特殊字符进行编译了如:or,把用户注入的信息只当作普通数据,最后进行编译 使用java.sql.PreparedStatement 继承java.sql.Statement.进行预编译。

对以上代码中的用户连接数据库部分代码进行修改

Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
boolean b=false;
try {
  //1、加载驱动
  Class.forName("com.mysql.jdbc.Driver");
  //2、建立连接
  String url="jdbc:mysql://127.0.0.1:3306/uses?useUnicode=true&characterEncoding=gbk";
  String user="root";
  String password="root";
  conn = DriverManager.getConnection(url, user, password);
  //3、获取数据库预编译操作对象
  String sql = "select * from user where username=? and password=?";
  //?是占位符,第一个?下标是1,第二个是2
  ps = conn.prepareStatement(sql);//先对SQL语句进行编译
  ps.setString(1,uselogin.get("username"));
  ps.setString(2,uselogin.get("password"));
  //4、执行sql语句(DQL,DML)
  rs=ps.executeQuery();
  //5、处理结果集
  if(rs.next()){
      b=true;
  }

2.1Statement和PreparedStatement的比较

Statement有SQL注入的问题,PreparedStatement没有

Statement 每次执行SQL语句都需要编译,PreparedStatement只需要编译一次,所以PreparedStatement 执行效率略高

PreparementStatement会做安全检查

2.2 Statement在什么时候使用?

需要SQL注入的时候使用。例如需要降序升序,添加关键字

例如按升序或降序进行输出代码:

//连接数据库的部分代码

//加载驱动
Class.forName("com.mysql.jdbc.Driver");
//连接数据库
conn= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/uses?useUnicode&characterEncoding","root","root");
//获取数据库操作对象
state=conn.createStatement();
String sql="select * from user order by username "+da;//da是用户手动输入desc 或asc
//执行sql语句
rs=state.executeQuery(sql);
//处理结果集
while(rs.next()){
  String usename = rs.getString("username");
  String password=rs.getString("password");
  System.out.println(usename+":"+password);

 

2.3封装JDBC

手动写JDBCUtil包,代码如下:

public class DBUtil {
  //工具包里的方法都是静态的,直接类名点方法名调用
  private DBUtil(){}//构造方法私有化,就不能new对象
  //在类加载的时候执行
  static{
      try{
          Class.forName("com.mysql.jdbc.Driver");
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
  }
  public static Connection connection() throws SQLException {
      return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bjpowernode?useUnicode=true&characterEncoding=gbk","root","root");
  }
  public static void close(Connection conn, PreparedStatement ps, ResultSet resultSet){
      if(conn!=null){
          try {
              conn.close();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
      }
      if(ps!=null){
          try {
              ps.close();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
      }
      if(resultSet!=null){
          try {
              resultSet.close();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
      }
  }
}

2.4JDBCUtil工具包的使用与模糊查询代码

public static void main(String[] args) {
  Connection conn=null;
  PreparedStatement ps=null;
  ResultSet rs=null;
  try{
      //加载驱动和建立连接
      conn=DBUtil.connection();
      //获取数据库操作对象
      String sql="select ename from emp where ename like ?";
      ps=conn.prepareStatement(sql);
      //赋值
      ps.setString(1,"_A%");
      //执行sql语句
      rs=ps.executeQuery();
      //处理结果集
      while(rs.next()){
          String ename = rs.getString("ename");
          System.out.println(ename);
      }
?
  } catch (SQLException throwables) {
      throwables.printStackTrace();
  }finally{
      DBUtil.close(conn,ps,rs);
  }
}

运行结果:

WARD

MARTIN

JAMES

三、悲观锁模拟

悲观锁:在select 语句后面加 for update 意思是把这些查询出来的数据行锁住,其他事务无法修改这些数据 如:select ename,sal from emp where job=‘manager‘ for update 符合此条件的行都被锁上,其他事务无法修改数据。

代码:JdbcTestLock与JdbcTestLock01

四、单机事务

模拟转账系统

重要的三行代码

conn.setAutoCommit(false)//设置自动提交为关闭

conn.commit();//提交

conn.rollback();//回滚

部分代码:

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/uses?useUnicode&characterEncoding=gbk","root","root");
      //关闭数据库自动提交设置
    conn.setAutoCommit(false);
      //获取数据库操作对象
      String sql="update a_act set balance=? where actno=? ";
      ps=conn.prepareStatement(sql);
      ps.setDouble(1,10000);
      ps.setInt(2,12);
      int count =ps.executeUpdate();
?
      ps.setDouble(1,10000);
      ps.setInt(2,13);
      count +=ps.executeUpdate();
      //手动提交事务
      conn.commit();
  } catch (ClassNotFoundException | SQLException e) {
      //如果事务执行半路出错,则进行回滚
      if(conn!=null){
          try {
              conn.rollback();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
      }
      e.printStackTrace();
      }

JDBC

上一篇:SpringMVC之RequestMapping注解


下一篇:Android Studio 添加jar或aar依赖的两种方式