• Java Web学习总结(11)JDBC


    一,简介

    JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序

    二,编写JDBC程序

    首先在mysql中创建一个库,并创建user表和插入表的数据,SQL脚本如下:

    CREATE DATABASE jdbcLibrary CHARACTER SET utf8 COLLATE utf8_general_ci;
     
    USE jdbcLibrary;
     
    CREATE TABLE users(
        id INT PRIMARY KEY,
        uname VARCHAR(40),
        upwd VARCHAR(40)
    );
    INSERT INTO users(id,uname,upwd) VALUES(1,'Zender','123456');
    INSERT INTO users(id,uname,upwd) VALUES(2,'张三','123456');
    INSERT INTO users(id,uname,upwd) VALUES(3,'李四','123456');

    项目中导入驱动:

    JDBCDemo代码如下:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
     
    public class JDBCDemo {
        public static void main(String[] args) throws Exception {
            //要连接的数据库URL
            String url = "jdbc:mysql://localhost:3306/jdbcLibrary";
            //用户名
            String username = "root";
            //密码
            String password = "123456";
            
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取与数据库的链接
            Connection conn = DriverManager.getConnection(url, username, password);
            
            //3.获取用于向数据库发送sql语句的statement
            Statement st = conn.createStatement();
            
            String sql = "select id,uname,upwd from users";
            //4.向数据库发sql,并获取代表结果集的resultset
            ResultSet rs = st.executeQuery(sql);
            
            //5.取出结果集的数据
            while(rs.next()){
                System.out.println("id=" + rs.getObject("id"));
                System.out.println("uname=" + rs.getObject("uname"));
                System.out.println("upwd=" + rs.getObject("upwd"));
            }
            
            //6.关闭链接,释放资源
            rs.close();
            st.close();
            conn.close();
        }
    }

    运行结果:

    1,DriverManager类

    DriverManager用于加载驱动,并创建与数据库的链接。

    API的常用方法:

    DriverManager.registerDriver(new Driver())

    DriverManager.getConnection(url, user, password)

    注意:在实际开发中并不推荐采用registerDriver方法注册驱动。原因有二:

    1、查看Driver的源代码可以看到,如果采用此种方式,会导致驱动程序注册两次,也就是在内存中会有两个Driver对象。

    2、程序依赖mysql的api,脱离mysql的jar包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。

    推荐方式:Class.forName("com.mysql.jdbc.Driver")

    采用此种方式不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅仅只需要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。

    2,Connection类

    用于代表数据库的链接,Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的。

    常用方法:

    createStatement()

    创建向数据库发送sql的statement对象。

    prepareStatement(sql)

    创建向数据库发送预编译sql的PrepareSatement对象。

    prepareCall(sql)

    创建执行存储过程的callableStatement对象。

    setAutoCommit(boolean autoCommit)

    设置事务是否自动提交。

    commit()

    在链接上提交事务。

    rollback()

    在此链接上回滚事务。

    3,Statement类

    用于向数据库发送SQL语句

    常用方法:

    executeQuery(String sql)

    用于向数据发送查询语句。

    executeUpdate(String sql)

    用于向数据库发送insert、update或delete语句

    execute(String sql)

    用于向数据库发送任意sql语句

    addBatch(String sql)

    把多条sql语句放到一个批处理中。

    executeBatch()

    向数据库发送一批sql语句执行。

    4,ResultSet类

    该类代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。

    常用方法:

    获取任意类型的数据:

        getObject(int index)

        getObject(string columnName)

    获取指定类型的数据:

        getString(int index)

        getString(String columnName)

    next()

    移动到下一行

    Previous()

    移动到前一行

    absolute(int row)

    移动到指定行

    beforeFirst()

    移动resultSet的最前面。

    afterLast()

    移动到resultSet的最后面。

    二,数据库的CRUD

    创建一个db.properties文件用于存放MySQL数据库的连接信息,代码如下所示:

    driver=com.mysql.jdbc.Driver

    url=jdbc:mysql://localhost:3306/jdbcLibrary

    username=root

    password=123456

    编写一个JdbcUtils工具类,用于连接数据库,获取数据库连接和释放数据库连接,代码如下:

    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
     
    public class JdbcUtils {
        private static String driver = null;
        private static String url = null;
        private static String username = null;
        private static String password = null;
        
        static{
            try{
                //读取db.properties文件中的数据库连接信息
                InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
                Properties prop = new Properties();
                prop.load(in);
                
                //获取数据库连接驱动
                driver = prop.getProperty("driver");
                //获取数据库连接URL地址
                url = prop.getProperty("url");
                //获取数据库连接用户名
                username = prop.getProperty("username");
                //获取数据库连接密码
                password = prop.getProperty("password");
                
                //加载数据库驱动
                Class.forName(driver);
                
            }catch (Exception e) {
                throw new ExceptionInInitializerError(e);
            }
        }
        
        /**
         * 
         * @方法名: getConnection
         * @描述: 获取数据库连接对象
         * @return
         * @throws SQLException
         * @创建人 Zender
         */
        public static Connection getConnection() throws SQLException{
            return DriverManager.getConnection(url, username,password);
        }
        
        /**
         * 
         * @方法名: release
         * @描述: 释放资源
         * @param conn
         * @param st
         * @param rs
         * @创建人 Zender
         */
        public static void release(Connection conn,Statement st,ResultSet rs){
            try{
                if(rs!=null){
                    rs.close();
                    rs = null;
                }
                if(st!=null){
                    st.close();
                    st = null;
                }
                if(conn!=null){
                   conn.close();
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    JDBCDemo代码如下:

    package com.zender;
     
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
     
    import org.junit.Test;
     
    import com.zender.util.JdbcUtils;
     
    public class JDBCDemo {
        
        private Connection conn = null;
        private PreparedStatement st = null;
        private ResultSet rs = null;
        
        @Test
        public void insertUser(){
            try{
                //获取一个数据库连接
                conn = JdbcUtils.getConnection();
                //构建执行的SQL,SQL中的参数使用?作为占位符
                String sql = "INSERT INTO users(id,uname,upwd) VALUES(?,?,?);";
                
                //获取prepareStatement对象
                st = conn.prepareStatement(sql);
                st.setInt(1, 6);
                st.setString(2, "Zender");
                st.setString(3, "123456");
                //执行插入操作,executeUpdate方法返回成功的条数
                int num = st.executeUpdate();
                if(num>0){
                    System.out.println("插入成功!!");
                }
                
            }catch (Exception e) {
                e.printStackTrace();
            }finally{
                //SQL执行完成之后释放相关资源
                JdbcUtils.release(conn, st, rs);
            }
        }
        
        @Test
        public void deleteUser(){
            try{
                conn = JdbcUtils.getConnection();
                String sql = "delete from users where id=4";
                st = conn.prepareStatement(sql);
                int num = st.executeUpdate();
                if(num>0){
                    System.out.println("删除成功!!");
                }
            }catch (Exception e) {
                e.printStackTrace();
                
            }finally{
                JdbcUtils.release(conn, st, rs);
            }
        }
        
        @Test
        public void deleteUpdate(){
            try{
                conn = JdbcUtils.getConnection();
                String sql = "update users set uname='修改的Name', upwd='123' where id=1";
                st = conn.prepareStatement(sql);
                int num = st.executeUpdate();
                if(num>0){
                    System.out.println("更新成功!!");
                }
            }catch (Exception e) {
                e.printStackTrace();
                
            }finally{
                JdbcUtils.release(conn, st, rs);
            }
        }
        
        @Test
        public void findUser(){
            try{
                conn = JdbcUtils.getConnection();
                String sql = "select * from users where id=3";
                st = conn.prepareStatement(sql);
                rs = st.executeQuery();
                if(rs.next()){
                    System.out.println(rs.getString("id"));
                    System.out.println(rs.getString("uname"));
                    System.out.println(rs.getString("upwd"));
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally{
                JdbcUtils.release(conn, st, rs);
            }
        }
    }

    这里使用了PreperedStatement,该类是Statement的子类,PreperedStatement可以避免SQL注入的问题,并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换。

    三,事务

    事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。

    例如:模拟银行转账,User1向User2转账

    数据库中创建名字为account的table:

    JDBCDemo代码如下:

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
     
    import org.junit.Test;
     
    import com.zender.util.JdbcUtils;
     
    public class JDBCDemo {
        
        private Connection conn = null;
        private PreparedStatement st = null;
        private ResultSet rs = null;
        
        @Test
        public void TransactionDemo(){
            try{
                //获取一个数据库连接
                conn = JdbcUtils.getConnection();
                //通知数据库开启事务
                conn.setAutoCommit(false);
                //构建执行的SQL
                String sql = "update account set money=money-100 where id=1";
                //获取prepareStatement对象
                st = conn.prepareStatement(sql);
                //执行修改操作
                st.executeUpdate();
                sql = "update account set money=money+100 where id=2";
                st = conn.prepareStatement(sql);
                st.executeUpdate();
                //提交事务
                conn.commit();
            }catch (Exception e) {
                //出现异常回滚事务
                e.printStackTrace();
            }finally{
                //释放资源
                JdbcUtils.release(conn, st, rs);
            }
        }
    }

    运行以上代码,数据库数据如下:

    现在修改JDBCDemo代码,让代码在执行中途出错,导致有一部分SQL执行失败后,让数据库自动回滚事务,代码如下:

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
     
    import org.junit.Test;
     
    import com.zender.util.JdbcUtils;
     
    public class JDBCDemo {
        
        private Connection conn = null;
        private PreparedStatement st = null;
        private ResultSet rs = null;
        
        @Test
        public void TransactionDemo(){
            try{
                //获取一个数据库连接
                conn = JdbcUtils.getConnection();
                //通知数据库开启事务
                conn.setAutoCommit(false);
                //构建执行的SQL
                String sql = "update account set money=money-100 where id=1";
                //获取prepareStatement对象
                st = conn.prepareStatement(sql);
                //执行修改操作
                st.executeUpdate();
                //出错代码
                int x = 1/0;
                sql = "update account set money=money+100 where id=2";
                st = conn.prepareStatement(sql);
                st.executeUpdate();
                //提交事务
                conn.commit();
            }catch (Exception e) {
                //出现异常回滚事务
                e.printStackTrace();
            }finally{
                //释放资源
                JdbcUtils.release(conn, st, rs);
            }
        }
    }

    运行以上代码,控制台报错:

    数据库数据如下:

    四,事务的四大特性

    原子性(Atomicity)

    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。

    一致性(Consistency)

    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。以转账为例子,user1向user2转账,假设转账之前这两个用户的钱加起来总共是200,那么user1向user2转账之后,不管这两个账户怎么转,user1用户的钱和user2用户的钱加起来的总额还是200,这个就是事务的一致性。

    隔离性(Isolation)

    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

    持久性(Durability)

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

    事务的四大特性中最麻烦的是隔离性,下面重点介绍一下事务的隔离级别

    五、事务的隔离级别

    作用:

    在多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

    不考事务的虑隔离性,可能出现以下问题:

    1,脏读

    脏读指一个事务读取了另外一个事务未提交的数据。

    例如:

    假设user1向user2转帐100元,对应sql语句如下所示:

        SQL1:update account set money=money+100 where name='user2'

        SQL2:update account set money=money-100 where name='user1'

     当SQL1执行完,SQL2还没执行(user1未提交时),如果此时user2查询自己的帐户,就会发现自己多了100元钱。如果user1等user2走后再回滚,user2回来再次查询时候就会少100元。 

    2,不可重复读

    不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。

    例如:

    1,首先银行查询用户user1的余额,第一次查询结果为100元。

    2,user1来到银行查询自己余额,查询的余额为100元,然后user1向账户中存入了500元并提交。

    3,银行接着又进行了一次查询,此时A帐户为600元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。

    3,虚读(幻读)

    虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

    例如:

    user1去银行存款500元(事务没有提交),这时候银行做总存款统计,所有用户存款为1000元,然后user1这个时候提交事务,这时银行再统计发现帐户钱多了500元,造成虚读。可能就会很困惑,不知道哪次查询是准的。

  • 相关阅读:
    C++类成员变量多用指针不用对象
    C++列表初始化是初始化本类自身含有的成员变量,不能直接初始化继承过来的成员变量
    std::unorder_set你插入元素的顺序不一定就是元素在里面的元素
    yolo3使用darknet卷积神经网络训练pascal voc
    windows下执行tensorflow/models的代码显示No module named 'object_detection'
    extern const 不能一起用
    常见PID里面的像素大小
    ajax 异步请求webservice(XML格式)
    剖析下聊天室
    session更换存储,实现在多台服务器共享
  • 原文地址:https://www.cnblogs.com/Zender/p/7811150.html
Copyright © 2020-2023  润新知