【JAVASE(14)】JAVASE学习--JDBC篇

一、JDBC:
    java database connectivity-----java数据库连接,java语言操纵数据库
    1、概念:
        独立于特定的数据库管理系统,通用的SQL数据库存取和操作的公共接口

    2、简单理解:
        JDBC,是sun公司提供的一套API,使用这套API,可以实现对具体数据库的操作{
            获取连接,
            关闭连接,
            DML
            DDL
            DCL
        }

    3、作用:
        实现对sql数据库的同一存取和操作

    4、JDBC体系结构:
        面向应用API;面向数据库API
        面向接口编程思想

{
    1、JDBC是sun公司定义并提供的一套用于操作所有关系型数据库的(规则)接口,
    2、不同的数据库厂商,需要针对这套接口,提供不同实现。不同实现的集合,
        即为不同数据库的驱动。即不同的数据库厂商需要提供数据库驱动jar包。
    3、java开发者只需要面向接口编程即可。可以使用这套接口(JDBC)编程,
        真正执行的代码是驱动jar包中的实现类。
}

    5、好处:
        程序员:不需要关注具体的数据库的细节
        数据库厂商:只需要提供标准的具体实现

    6、数据库驱动:
        数据库厂商针对于JDBC这套接口,提供的具体实现类的集合


二、针对MySQL特别小芝士:
    1、持久化:
        把数据保存到可掉电式的存储设备中以供以后使用

    2、事务:
        1、概念:
            一组逻辑操作单元,使数据从一种状态变换到另一种状态;
            一个包含多步骤的业务操作。若此业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
                >一组逻辑单元:一个或多个DML操作(增删改)

        2、事务的处理原则:
            保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有事务都被提交(commit)那么这些修改就被永久保存下来,要么数据库管理系统将放弃所有操作,整个事务回滚(rollback)到最初状态。

        3、数据一旦提交,就不可回滚
            哪些操作会导致数据自动提交了:
                >DDL操作一旦执行,都会自动提交
                    >set autocommit = false 的方式对DDL操作失效
                >DML操作默认情况下,一旦执行,就会自动提交
                    >可以通过set autocommit = false 的方式取消DML操作的自动提交
                >默认在关闭连接时,会自动提交数据
        4、事务的ACID属性:

A--Atomicity原子性:
    指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生

C--Consistency一致性:
    事务必须使得数据库从一个一致性状态转换到另外一个一致性状态

I--Isolation隔离性:
    一个事务的执行不能被其他事务干扰,
    即一个事务的内部操作即使用的数据对并发的其他事务是隔离的,
    并发执行的各个事务之间不能相互干扰

D--Durability持久性:
    持久性是指一个事务一旦被提交,它对数据库中的数据的改变是永久的吗,
    接下来其他操作和数据库故障不应该对其有任何影响

        5、数据库的并发问题:
            多个事务访问数据库中的相同数据,若没有采取必要隔离机制,则导致以下三大类并发问题:

脏读:
    对于两个事务T1、T2,T1读取了已经被T2更新但还没有被提交的字段。
    之后,若T2回滚,T1读取的内容就是临时且无效的

不可重复读:
    对于两个事务T1、T2,T1读取了一个字段,然后T2更新了该字段。
    之后T1再次读取同一个字段,值就不同了

幻读:
    对于两个事务T1、T2,T1从表中读取了一个字段,然后T2在该表中插入了一些新的行。
    之后若T1再次读取同一个表,就会多出几行

        6、数据库的四种隔离级别:

READ UNCOMMITTED(读未提交):
                对三大中并发问题都没解决,在开发中不使用

READ COMMITED(读已提交数据):
                仅仅解决脏读问题

REPEATABLE READ(可重复读):
                仅解决脏读,不可重复读问题

SERIALIZABLE(串行化):
                解决了脏读,不可重复读和幻读问题,一致性很好,
                但其代价就是效率极差,在开发中不使用

            对于oracle支持:
                READ COMMITED(读已提交数据),SERIALIZABLE(串行化)。
                其中默认为READ COMMITED(读已提交数据)

            对于MySQL支持四种隔离级别,其中默认为REPEATABLE READ(可重复读)

        7、 一致性与并发性:一致性越好,并发性越差

    3、Bolb类型数据:
        MySql-BOLB类型:
        MySQL中的BOLB是一个二进制大对象,有四种类型:
            TinyBlob--最大255字节
            Blob--最大65KB
            MediumBlob--最大16MB
            LongBlob--最大4GB
        当mysql数据库管理系统中对允许插入的blob大对象的packet值有限制时,可以在mysql的my.ini对packet上限制进行修改
            如当mediumBlob最大packet默认为1MB,而需要存储的数据超过1MB时,可以找到my.ini,在其中的最后一行添加:
            max_allowed_packet=16M 然后MySQL重启服务

    4、ORM编程思想:

{
    object relational mapping----对象关系映射
    一个数据表对应一个javabean,
    表中的一条记录(元组)对应javabean中的一个实例对象
    表中的一个字段对应javabean中的一个属性,当表中名称和javabean中的名称不一致时,
    由SQL查询语句中的起别名方法解决
}


三、使用步骤:
    1、步骤:
        1.导入java.sql包,将接口和抽象方法引入程序
        2.导入厂商具体的jar包,通过厂商jar包将接口和抽象方法实现
        3.加载并注册驱动程序:Driver driver=new com.mysql.jdbc.Driver();
        4.创建连接对象:Connection对象
        5.创建Statement对象,对数据表进行增删改查操作的对象,具体使用PreparedStatement对象
        6.执行sql语句
        7.使用ResultSet对象,用于查询和接收结果集,至于增删改不用结果集
        8.关闭ResultSet对象
        9.关闭PreparedStatement对象
        10.关闭Connection对象

    2、实例:
        实例一(初级连接方式)

{
    @Test
    public void testConnection() throws SQLException {
        //获取Driver的实现类对象
        Driver driver=new com.mysql.jdbc.Driver();

        /*
                明确数据库连接
                url:"MySQL数据库的url";
                协议:jdbc:mysql
                ip地址:MySQL数据库的ip地址
                端口号:MySQL数据库的端口号
                数据库名称:MySQL数据库管理系统中某个数据库的名称
        */

         String url="MySQL数据库的url";

         //将用户名和密码封装在Properties中
         Properties info=new Properties();
         info.setProperty("a","数据库用户名称");
         info.setProperty("password","数据库密码");

         //获取连接
         Connection conn = driver.connect(url,info);

         System.out.println(conn);

         if(conn!=null){
             conn.close();
         }
    }
}

        实例二(高级连接方式)

{

    jdbc.Properties声明在工程下的src下
    jdbc.properties文件中的内容{
            a=数据库用户名
            password=数据库密码
            url=数据库url
            driver=数据库驱动
    }

    @Test
    public void testConnection() throws Exception {
    /*
        将数据库连接的基本信息声明在配置文件中,通过读取配置文件的方式,获取连接
                好处:
                    1.实现数据与代码的分离,实现了解耦
                    2.如果需要修改配置文件信息,可以避免程序重新打包
    */

    //读取配置文件中的信息
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
    Properties pros = new Properties();
    pros.load(is);

    String a = pros.getProperty("a");
    String password = pros.getProperty("password");
    String url = pros.getProperty("url");
    String driver = pros.getProperty("driver");

    //加载驱动
    Class.forName(driver);

    //获取连接
    Connection conn = DriverManager.getConnection(url, a, password);
        System.out.println(conn);

        if(conn!=null){
            conn.close();
        }   
    }
}


    3、步骤即实例中的对象详解:

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{
                throw new RuntimeException("Can't register driver!");
            }
        }
        注意:mysql5之后的驱动jar包可以省略注册驱动的步骤

        2.获取数据库连接
            方法:
                static Connection getConnection(
                    String url,
                    String a, 
                    String password
                )
            参数:
                url:指定连接的路径
                语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
                例子:
                    "jdbc:mysql://localhost(127.0.0.1):3306/目标数据库",
                    "用户名称","密码"
                细节:
                    若连接的是本机mysql服务器,
                    并且mysql服务器默认端口是3306,
                    则url可以简写为:"jdbc:mysql:///数据库名称
                a:用户名
                password:密码

2. Connection:数据库连接对象
    1. 功能:
        1. 获取执行SQL的对象:
            PreparedStatement createPreparedStatement();
            preparedPreparedStatement preparePreparedStatement(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,deleter)语句,DDL(creat,alter,drop)
        返回值:
            影响的行数,可以通过影响的行数判断DML语句是否执行成功,
            返回值大于0,则执行成功,反之则失败

    3.ResultSet executeQuery(String sql):执行DQL(select)语句

4.ResultSet:结果集对象,封装查询结果,在数据库中查询语句看到的表就是一个结果集对象
    boolean next();
        游标(光标)向下移动一行,判断当前行是否是最后一行末尾(是否有数据){
                是false;否true
        }
    getXxx(参数);
        获取数据:
            Xxx()代表数据类型,
                如int <-> getInt(); string <-> getString();
        参数:
            1.int:代表列的编号,从1开始,如getString(1);
            2.String:代表列的名称,如getDouble("列名")
        注:使用步骤:1.游标下移,判断数据存在,3取出数据

5. PreparedStatement:执行SQL的对象
    1、SQL注入问题:
        在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
       1. 输入用户随便,输入密码:a' or 'a'='a
       2.sql:
            SELECT * 
            FROM 表A 
            WHERE aname='akjdbfvdjf' 
            AND password='  a' or 'a'='a  '

    2、解决sql注入问题:使用PreparedStatement对象解决
    3、预编译的sql:参数使用?作为占位符
    4、步骤:
        1.导入驱动jar包 mysql-connector-java-5.1.25-bin.jar
        2.注册驱动
        3.获取数据库连接对象Connection
        4.定义sql
            注:sql的参数使用?作为占位符。如:
                SELECT * 
                FROM 表A 
                WHERE aname=? 
                AND password=?;
        5.获取执行sql语句的对象 PreparedStatement 
            Connection.PreparedStatement(String sql);
        6.给?赋值
            方法:setXxx(参数1,参数2)
                参数1:?的位置编号,从1开始
                参数2:?的值
        7.执行sql,接收返回结果
        8.处理结果
        9.释放资源
        10.后期都会使用PreparedStatement对象来完成增删改查的所有操作
                    1.可以防止sql注入
                    2.效率更高


    4、关于Statement和PreparedStatement:
        使用Statement/PreparedStatement实现CRUD操作
            数据库连接被用于向数据库服务器发送命令和SQL语句,并接受数据库服务器返回的结果,其实一个数据库连接就是一个Socket连接
            Statement的弊端:
                1、需要拼写sql语句,即拼串问题
                2、存在SQL注入问题
                    sql注入指:利用某些系统没有对用户输入的数据进行充分检查,而在用户输入数据中注入非法的SQL语句段或命令{

如:sql注入实例:
    SELECT * FROM 表 WHERE 属性1=值1 OR AND属性2=值2 OR 值1=值1;
由实例可知,只要查询条件中有一个恒成立的等式,那么可以将目标表格中的所有信息全部查出

                }从而利用系统的SQL引擎完成恶意行为的做法
            基于Statement的这两个缺点,才采用将PreparedStatement替代Statement执行对数据库的CRUD操作

            PreparedStatement与Statement的异同
                1、PreparedStatement继承于Statement
                2、开发中用PreparedStatement替换Statement
                3、PreparedStatement是预编译sql语句的,可以提高程序运行效率,有效防止sql注入,有效解决Statement的两个问题
                4、易于操作blob类型数据
                5、使用PreparedStatement进行批量操作优于Statement,PreparedStatement可以实现更高效的批量插入,减少sql的校验次数
                小节:
                    两个接口均是sun公司提供的数据库接口(即JDBC定义的规范),其中PreparedStatement是Statement的子接口,两个接口的作用均是将java程序中写好的sql语句传送到sql数据库服务器中,让服务器执行语句。在实际开发中使用PreparedStatement替换Statement进行对数据库的CRUD操作。因为Statement存在拼串操作繁琐并且存在sql注入问题。使用PreparedStatement可以对数据库中的blob数据类型进行操作,因为其含有预编译操作,而Statement无法对blob数据类型进行操作


四、JDBCUtils:
    1、抽取JDBC工具类:JDBCUtils
        目的:简化书写
        分析:
            1.抽取注册驱动
            2.抽取一个方法获取连接
                需求:不想传递参数(麻烦),还得保证公局类的通用性
                解决:配置文件

 jdbc.properties{
                        url=目标数据库的url
                        a=目标数据库的用户名称
                        password=密码
}


            3.抽取一个方法释放资源    
    2、实现

{
    import java.io.FileReader;
    import java.io.IOException;
    import java.net.URL;
    import java.sql.*;
    import java.util.Properties;


    public class JDBCUtils {
        private static String url;
        private static String a;
        private static String password;
        private static String driver;

        /**
        * 文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
        */
        static{
            //读取在源文件,获取值,静态代码块只能访问静态变量

            try {
                //1.创建Properties集合类
                Properties pro=new Properties();

                //获取src路径下的文件方式--> ClassLoader类加载器
                ClassLoader classLoader = JDBCUtils.class.getClassLoader();
                URL res = classLoader.getResource("jdbc.properties");
                String path = res.getPath();

                //2.加载文件
                pro.load(new FileReader(path));

                //3.获取属性赋值
                JDBCUtils.url =pro.getProperty("url");
                a=pro.getProperty("a");
                password=pro.getProperty("password");
                driver=pro.getProperty("driver");

                //注册驱动
                Class.forName(driver);

             } catch (Exception e) {
                    e.printStackTrace();
             }       
        }


         /**
         * 获取连接
         * @return 连接对象
         */
        public static Connection getConnection() throws SQLException{
                return DriverManager.getConnection(url,a,password);
        }


        /**
        * 释放资源
        * @param ps
        * @param conn
        */
        public static void closeResource(PreparedStatement ps,Connection conn){
                if(ps!=null){
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }

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

        /**
        * 释放资源
        * @param rs
        * @param ps
        * @param conn
        */
        public static void closeResource(
            ResultSet rs,
            PreparedStatement ps, 
            Connection conn
        ){
                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();
                    }
                }
        }
    }
}


五、对Blob类型数据;批量;数据库事务的具体实现
    1、使用PreparedStatement操作Blob类型的数据(
        对Blob类型变量的操作:
        写入:setBlob(InputStream is);
        读取:
            Blob blob=getBlob(int index);
            InputStream is=blob.getBinaryStream();
    )

{
    建立数据库中目标表格的标准JavaBean
    public class Images {
            private int id;
            private String name;
    }


    对Bolb数据类型进行操作
    import org.junit.Test;

    import java.io.*;
    import java.sql.Blob;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;

    public class BlobTest {
       //查询数据表images中的Blob类型的字段
       @Test
       public void testQuery(){
            Connection conn =null;
            PreparedStatement ps =null;
            ResultSet rs =null;
            InputStream is =null;
            FileOutputStream fos =null;

            try {
                conn = JDBCUtils.getConnection();
                String sql="SELECT id,`name`,image FROM images WHERE id=?;";
                ps=conn.PreparedStatement(sql);
                ps.setObject(1,1);
                rs = ps.executeQuery();
                if(rs.next()){
                    int id = rs.getInt(1);
                    String name = rs.getString(2);

                    Images images = new Images(id, name);

                    System.out.println(images);

                    //将blob类型的图片下载下来,,以文件方式保存在本地
                    Blob image = rs.getBlob("image");
                    is = image.getBinaryStream();
                    fos = new FileOutputStream("全路径\\目标Copy.jpg");
                    byte[] buffer = new byte[1024];
                    int len=0;
                    while ((len=is.read(buffer))!=-1){
                        fos.write(buffer,0,len);
                    }
                }
            }catch (Exception e){ e.printStackTrace(); }finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }   
                    JDBCUtils.closeResource(conn,ps,rs);
            }
        }


        //向数据表images中插入Blob类型的字段
        @Test
        public void testInsert(){
            Connection conn =null;
            PreparedStatement ps =null;
            try {
                conn = JDBCUtils.getConnection();
                String sql="INSERT INTO images(`name`,image)VALUES(?,?);";
                ps=conn.PreparedStatement(sql);
                ps.setObject(1,"02");
                FileInputStream is = new FileInputStream(
                    new File("全路径\\目标文件.jpg")
                );
                ps.setBlob(2,is);
                ps.execute();
            }catch (Exception e){ e.printStackTrace(); }finally {
                    JDBCUtils.closeResource(conn,ps);
            }
        }
    }
}


    2、JDBC控制事务(
        1.操作:
            1.开启事务;
            2.提交事务;
            3.回滚事务
        2.使用Connection对象来管理事务:
            开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
                在执行sql之前开启事务
            提交事务:commit();
                当所有sql都执行完提交事务
            回滚事务:rollback();
                在catch中回滚事务
    )

{
    import org.junit.Test;
    import java.lang.reflect.Field;
    import java.sql.*;


    public class TransactionTeat {

        //**********************数据库隔离级别*********************************

        @Test
        public void testTransactionSelect00(){
            Connection conn =null;
            try {
                conn = JDBCUtils.getConnection();
                //获取当前连接的隔离级别
                System.out.println(conn.getTransactionIsolation());

                //设置数据库的隔离级别                               
                //conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

                //取消自动提交数据,开启事务
                conn.setAutoCommit(false);

                String sql="SELECT id,aname,`password`,birthday FROM `表A` WHERE id=?;";
                表A 表A = getInstance(conn, 表A.class, sql, 1);
                System.out.println(表A);

            }catch (Exception e){e.printStackTrace();}finally {
                    JDBCUtils.closeResource(conn,null);
            }
        }

        @Test
        public void testTransactionUpdate00(){
            Connection conn =null;
            try {
                conn = JDBCUtils.getConnection();
                //取消自动提交数据,开启事务
                conn.setAutoCommit(false);

                String sql="UPDATE `表A` SET `password`=? WHERE id=?;";
                update01(conn,sql,311,1);
                Thread.sleep(15000);//睡眠15秒
                 System.out.println("修改结束");

             }catch (Exception e){e.printStackTrace();}finally {
                    JDBCUtils.closeResource(conn,null);
             }
        }


        //通用的查询操作,用于返回数据表中的一条记录(version--2.0,考虑事务)
        public  T getInstance(Connection conn,Class clazz,String sql,Object...args){
            PreparedStatement ps =null;
            ResultSet rs =null;
            try {
                ps=conn.PreparedStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }

                //执行获取结果集
                rs= ps.executeQuery();

                //获取结果集的元数据
                ResultSetMetaData rsmd = rs.getMetaData();
                //获取列数
                int columnCount = rsmd.getColumnCount();

                if(rs.next()){
                    T t = clazz.newInstance();

                    for (int i = 0; i < columnCount; i++) {
                        //获取每个列的列值:通过ResultSet
                        Object columnValue = rs.getObject(i + 1);

                        //获取每个列的名称:结果集的元数据
                        String columnLabel = rsmd.getColumnLabel(i + 1);

                        //通过反射将对象指定名columnName的属性赋值为指定的值columnValue
                        Field field = clazz.getDeclaredField(columnLabel);
                        field.setAccessible(true);
                        field.set(t,columnValue);
                    }
                    return t;
                }

            }catch (Exception e){e.printStackTrace();}finally {
                JDBCUtils.closeResource(null,ps,rs);
            }
            return null;
        }


        //******************考虑数据库事务的一个转账操作*******************

        @Test
        public void testUpdateWithTx00(){

            Connection conn =null;
            try {
                conn=JDBCUtils.getConnection();
                System.out.println(conn.getAutoCommit());//true
                //取消数据的自动提交,开启事务
                conn.setAutoCommit(false);
                String sqlOne="UPDATE 表A_table SET balance=balance+100 WHERE aname=?;";
                update01(conn,sqlOne,"张三");

                //模拟网络异常
                //System.out.println(10/0);

                String sqlTwo="UPDATE 表A_table SET balance=balance-100 WHERE aname=?;";
                update01(conn,sqlTwo,"赵六");

                System.out.println("转账成功");

                //提交数据,提交事务
                conn.commit();
            }catch (Exception e){
                e.printStackTrace();
                //回滚数据,事务的回滚
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }finally {
                JDBCUtils.closeResource(conn,null);
            }
        }

        //通用的增删改操作--version2.0(考虑上事务)
        public int update01(Connection conn,String sql,Object...args){

            PreparedStatement ps =null;
            try {

                //预编译sql语句,返回PreparedStatement的实例
                ps = conn.PreparedStatement(sql);

                //填充占位符
                /*
                    SQL当中占位符的个数与可变形参的长度形同
                */
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }

               /*
                   ps.execute();
                       若执行查询操作,有返回结果,则此方法返回的是true;
                       若执行的是增删改操作,没有返回结果,则此方法返回false
               */
               return ps.executeUpdate();

            }catch (Exception e){
                    e.printStackTrace();
            }finally {
                    JDBCUtils.closeResource(null,ps);
            }
            return 0;
        }


        //*********未考虑数据库事务的一个转账操作*****************
        @Test
        public void testUpdate(){
            /*
                举例理解事务:
                针对于数据表表A_table来说
                        “张三”给“赵六”转账100
                UPDATE 表A_table SET balance=balance-100 WHERE aname='张三';
                UPDATE 表A_table SET balance=balance+100 WHERE aname='赵六';
            */

            String sqlOne="UPDATE 表A_table SET balance=balance-100 WHERE aname=?;";
            update00(sqlOne,"张三");

            //模拟网络异常
            //System.out.println(10/0);

            String sqlTwo="UPDATE 表A_table SET balance=balance+100 WHERE aname=?;";
            update00(sqlTwo,"赵六");

            System.out.println("转账成功");
        }


        //通用的增删改操作--version1.0
        public int update00(String sql,Object...args){

            Connection conn =null;
            PreparedStatement ps =null;

            try {
                //获取连接
                conn=JDBCUtils.getConnection();

                //预编译sql语句,返回PreparedStatement的实例
                ps = conn.PreparedStatement(sql);

                //填充占位符
                /*
                    SQL当中占位符的个数与可变形参的长度形同
                */
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }

                /*
                    ps.execute();
                        若执行查询操作,有返回结果,则此方法返回的是true;
                        若执行的是增删改操作,没有返回结果,则此方法返回false
                */
                return ps.executeUpdate();

             }catch (Exception e){
                 e.printStackTrace();
             }finally {
                 /*
                     修改其为自动提交数据
                     主要针对使用数据库连接池时的使用
                     try {
                         conn.setAutoCommit(true);
                     } catch (SQLException e) {
                         e.printStackTrace();
                     }
                 */

                  JDBCUtils.closeResource(conn,ps);
             }
             return 0;
        }
    }
}

    3、JDBC下的批量操作

{
    import org.junit.Test;
    import java.sql.Connection;
    import java.sql.PreparedStatement;

    /*
        演示使用PreparedPreparedStatement实现批量数据的操作

        update、delete本身就具有批量操作的效果
        此时的批量操作,主要指定是批量插入,使用PreparedStatement如何实现更高效的批量插入

        向goods表中插入25条数据
        CREATE TABLE goods(
            id INT PRIMARY KEY AUTO_INCREMENT,
            gname VARCHAR(25)
        );

        方式一:
            使用PreparedStatement
            Connection conn JDBCUtils.getConnection();
            PreparedStatement st=conn.createPreparedStatement();
            for(int i=1;i<25;i++){
                String sql="INSERT INTO goods(gname)VALUE('name_"+i+"')"
                st.execute(sql)
            }

        DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,
        所以语句在DBServer的编译器编译后的执行代码被缓存下来,
        那么下次调用时只要是相同的编译语句就不需要编译,
        只要将参数直接传入编译过的语句执行代码中就会得到执行

        使用PreparedPreparedStatement进行批量操作优于PreparedStatement的原因:
            在PreparedStatement语句中,即使是相同的操作会因为数据内容不一样,
            所以整个语句本身不能匹配,没有缓存语句的意义,
            事实是没有数据库会对普通语句编译后的执行代码缓存,
            这样每执行一次都要对传入的语句编译一次。每一次都要进行语法检测,
            语义检测,翻译成二进制命令,缓存

         PreparedStatement与Statement的异同:
            两个接口均是sun公司提供的数据库接口(即JDBC定义的规范),
            其中PreparedStatement是Statement的子接口,
            两个接口的作用均是将java程序中写好的sql语句传送到sql数据库服务器中,
            让服务器执行语句。
            在实际开发中使用PreparedStatement替换Statement进行对数据库的CRUD操作。
            因为Statement存在拼串操作繁琐并且存在sql注入问题。
            使用PreparedStatement可以对数据库中的blob数据类型进行操作,
            因为其含有预编译操作,而Statement无法对blob数据类型进行操作
    */

    public class InsertTest {
        //批量插入的方式二,使用PreparedStatement
        @Test
        public void testInsert00(){
            Connection conn =null;
            PreparedStatement ps =null;

            try {
                long start = System.currentTimeMillis();
                conn=JDBCUtils.getConnection();
                String sql="INSERT INTO goods(gname)VALUE(?);";
                ps=conn.PreparedStatement(sql);
                for (int i = 0; i < 25; i++) {
                    ps.setObject(1,"name_"+i);
                    ps.execute();
                 }
                 long end = System.currentTimeMillis();

                 System.out.println("花费的时间:"+(end-start));//花费的时间:800

             }catch (Exception e){e.printStackTrace();}finally {
                    JDBCUtils.closeResource(conn,ps);
             }
        }

        /*
            批量插入的方式三:
            1.addBatch();executBatch();clearBatch();
            2.mysql服务器默认是关闭批处理的,需要通过一个参数,让mysql开启批处理的支持。
                ?rewriteBatchedPreparedStatements=true 写在配置文件的url后面
            3.更新驱动jar包
       */
       @Test
       public void testInsert01(){
           Connection conn =null;
           PreparedStatement ps =null;
           try {
               long start = System.currentTimeMillis();
               conn=JDBCUtils.getConnection();
               String sql="INSERT INTO goods(gname)VALUE(?);";
               ps=conn.PreparedStatement(sql);
               for (int i = 0; i <=25; i++) {
                   ps.setObject(1,"name_"+i);
                   //1.“攒”sql
                   ps.addBatch();
                   if(i%5==0){
                       //2.执行Batch
                       ps.executeBatch();

                       //3.清空Batch
                       ps.clearBatch();
                    }

                }
                long end = System.currentTimeMillis();

                System.out.println("花费的时间:"+(end-start));//花费的时间:721
            }catch (Exception e){e.printStackTrace();}finally {
                    JDBCUtils.closeResource(conn,ps);
            }
        }

        /*
                批量插入的方式四:设置连接不允许自动提交数据
        */
        @Test
        public void testInsert02(){
            Connection conn =null;
            PreparedStatement ps =null;
            try {
                long start = System.currentTimeMillis();

                conn=JDBCUtils.getConnection();

                //设置不允许自动提交数据
                conn.setAutoCommit(false);

                String sql="INSERT INTO goods(gname)VALUE(?);";
                ps=conn.PreparedStatement(sql);
                for (int i = 0; i <=25; i++) {
                    ps.setObject(1,"name_"+i);
                    //1.“攒”sql
                    ps.addBatch();
                    if(i%5==0){
                        //2.执行Batch
                        ps.executeBatch();

                        //3.清空Batch
                        ps.clearBatch();
                    }
                }
                //提交数据
                conn.commit();

                 long end = System.currentTimeMillis();

                 System.out.println("花费的时间:"+(end-start));//花费的时间:265

            }catch (Exception e){e.printStackTrace();}finally {
                    JDBCUtils.closeResource(conn,ps);
            }
        }
    }
}


六、对数据库的通用操作
    1、关于内存泄露
        java中的内存泄露指内存中有对象不能够被回收的情况;
        C语言的内存泄露指对应指向对象的指针丢失,进而不能将该指针指向的对象进行主动回收
    2、具体实现对数据看的通用操作(增删改查)
        DAO:data(base)access object--封装了针对于数据表的通用操作
        baseDAO的具体实现

{
    import JDBCUtils;

    import java.lang.reflect.Field;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    import java.util.ArrayList;
    import java.util.List;

    /*
        DAO:data(base)access object
        封装了针对于数据表的通用操作

        java中的内存泄露指内存中有对象不能够被回收的情况
        C语言的内存泄露指对应指向对象的指针丢失,进而不能将该指针指向的对象进行主动回收

    */
    public abstract class BaseDAO {

        private Class clazz=null;

        //    public BaseDAO() {
        //
        //    }

        {
            //获取当前baseDAO的子类继承的父类中的泛型
            Type genericSuperclass = this.getClass().getGenericSuperclass();
            ParameterizedType paramType=(ParameterizedType)genericSuperclass;
            Type[] typeArguments = paramType.getActualTypeArguments();//获取了父类的泛型参数
            clazz= (Class) typeArguments[0];//泛型的第一个参数
        }

        //通用的增删改操作(考虑上事务)
        public int update(Connection conn, String sql, Object...args){
            PreparedStatement ps =null;
            try {
                //预编译sql语句,返回PreparedPreparedStatement的实例
                ps = conn.PreparedStatement(sql);
                //填充占位符
                /*
                    SQL当中占位符的个数与可变形参的长度形同
                */
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }
                /*
                    ps.execute();
                        若执行查询操作,有返回结果,则此方法返回的是true;
                        若执行的是增删改操作,没有返回结果,则此方法返回false
                */
                return ps.executeUpdate();

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                JDBCUtils.closeResource(null,ps);
            }
            return 0;
        }


        //通用的查询操作,用于返回数据表中的一条记录(考虑事务)
        public  T getInstance(Connection conn,String sql,Object...args){
            PreparedStatement ps =null;
            ResultSet rs =null;
            try {

                ps=conn.PreparedStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }

                //执行获取结果集
                rs= ps.executeQuery();
                //获取结果集的元数据
                ResultSetMetaData rsmd = rs.getMetaData();
                //获取列数
                int columnCount = rsmd.getColumnCount();

                if(rs.next()){
                    T t = clazz.newInstance();
                    for (int i = 0; i < columnCount; i++) {
                         //获取每个列的列值:通过ResultSet
                         Object columnValue = rs.getObject(i + 1);

                         String columnLabel = rsmd.getColumnLabel(i + 1);

                         //通过反射将对象指定名columnName的属性赋值为指定的值columnValue
                         Field field = clazz.getDeclaredField(columnLabel);
                         field.setAccessible(true);
                         field.set(t,columnValue);
                    }
                    return t;
                }

            }catch (Exception e){e.printStackTrace();}finally {
                JDBCUtils.closeResource(null,ps,rs);
            }
            return null;
        }

        //通用的查询操作,用于返回数据表中的多条条记录(version--2.0,考虑事务)
        public  List getForList(Connection conn,String sql, Object...args){
            PreparedStatement ps =null;
            ResultSet rs =null;
            try {
                ps=conn.PreparedStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i+1,args[i]);
                }

                //执行获取结果集
                rs= ps.executeQuery();
                //获取结果集的元数据
                ResultSetMetaData rsmd = rs.getMetaData();
                //获取列数
                int columnCount = rsmd.getColumnCount();
                //创建集合对象
                ArrayList list = new ArrayList<>();

                while (rs.next()){
                    T t = clazz.newInstance();
                    //处理结果集一行数据中的每一列:给t对象指定的属性赋值
                    for (int i = 0; i < columnCount; i++) {
                        //获取每个列的列值:通过ResultSet
                        Object columnValue = rs.getObject(i + 1);

                        String columnLabel = rsmd.getColumnLabel(i + 1);

                        //通过反射将对象指定名columnName的属性赋值为指定的值columnValue
                        Field field = clazz.getDeclaredField(columnLabel);
                        field.setAccessible(true);
                        field.set(t,columnValue);
                    }
                    list.add(t);
                }
                return list;
            }catch (Exception e){e.printStackTrace();}finally {
                JDBCUtils.closeResource(null,ps,rs);
            }
            return null;
        }

       //用于查询特殊值的通用方法
       public  E getValue(Connection conn,String sql,Object...args){
           PreparedStatement ps =null;
           ResultSet rs =null;
           try {
               ps = conn.PreparedStatement(sql);
               for (int i = 0; i < args.length; i++) {
                   ps.setObject(i+1,args[i]);
               }
               rs=ps.executeQuery();
               if(rs.next()){
                   return (E)rs.getObject(1);
               }
            }catch (Exception e){e.printStackTrace();}finally {
                JDBCUtils.closeResource(null,ps,rs);
            }
            return null;
        }
    }
}

        针对某一表的DAO接口

{
            import 数据库中某一表A类;

            import java.sql.Connection;
            import java.sql.Date;
            import java.util.List;

            /*
                此接口用于规范针对于表A的常用操作
             */
            public interface 表A的DAO接口 {
                /**
                 * 将表A对象添加到数据库当中
                 * @param conn
                 * @param a
                 */
                void insert(Connection conn, 表A a);

                /**
                 * 通过id删除表中的一条记录
                 * @param conn
                 * @param id
                 */
                void deleteById(Connection conn, int id);

                /**
                 * 针对内存中的表A对象,去修改数据表中指定的记录
                 * @param conn
                 * @param a
                 */
                void update(Connection conn, 表A a);

                /**
                 * 针对指定id,查询对应的一个表A对象
                 * @param conn
                 * @param id
                 */
                表A get表AById(Connection conn, int id);

                /**
                 * 查询表中的所有记录构成的集合
                 * @param conn
                 * @return
                 */
                List<表A> getAll(Connection conn);

                /**
                 * 返回数据表中数据的条目数
                 * @param conn
                 * @return
                 */
                Long getCount(Connection conn);

                /**
                 * 返回数据表中最大的生日
                 * @param conn
                 * @return
                 */
                Date getMaxBirth(Connection conn);
            }
}

        针对某一表的DAO接口的实现

{
    import 数据库中某一表A类;

    import java.sql.Connection;
    import java.sql.Date;
    import java.util.List;

    public class 表A的DAOImpl extends BaseDAO<表A> implements 表A的DAO接口 {

        @Override
        public void insert(Connection conn, 表A a) {
            String sql="INSERT INTO `表A`(id,aname,password,birthday)VALUES(?,?,?,?);";           
            update(conn,sql,a.getId(),a.getaname(),a.getPassword(),a.getBirthday());
        }

        @Override
        public void deleteById(Connection conn, int id) {
            String sql="DELETE FROM `表A` WHERE id=?;";
            update(conn,sql,id);
        }

        @Override
        public void update(Connection conn, 表A a) {
            String sql="UPDATE `表A` SET aname=?,`password`=?,birthday=? WHERE id=?;";                       
            update(conn,sql,a.getaname(),a.getPassword(),a.getBirthday(),a.getId());
        }

        @Override
        public 表A get表AById(Connection conn, int id) {
            String sql="SELECT id,aname,`password`,birthday FROM `表A` WHERE id=?;";
            表A a = getInstance(conn, sql, id);
            return a;
        }

        @Override
        public List<表A> getAll(Connection conn) {
            String sql="SELECT id,aname,`password`,birthday FROM `表A`;";
            List<表A> list = getForList(conn, sql);
            return list;
        }

        @Override
        public Long getCount(Connection conn) {
            String sql="SELECT COUNT(*) FROM `表A`;";
            return getValue(conn,sql);
        }

        @Override
        public Date getMaxBirth(Connection conn) {
            String sql="SELECT MAX(birthday) FROM `表A`;";
            return getValue(conn,sql);
        }
    }
}

        对通用操作的测试

{
    import JDBCUtils;
    import 数据库中某一表A类;
    import 表A的DAOImpl;
    import org.junit.Test;

    import java.sql.Connection;
    import java.sql.Date;
    import java.util.List;

    public class 表A的DAOImplTest {
        private 表A的DAOImpl dao=new 表A的DAOImpl();
        @Test
        public void testInsert(){
            Connection conn =null;
            try {
                conn = JDBCUtils.getConnection();
                表A a = new 表A(6, "小王", "1133", new Date(73468137632L));
                dao.insert(conn,a);
                System.out.println("添加成功");

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                JDBCUtils.closeResource(conn,null);
            }
        }
    }
}

    3、小节:
        1、获取连接:
            Connection conn=JDBCUtils.getConnection();
            conn.setAutoCommit(false)
        2、如下的多个DML操作,作为一个事务出现
            操作1:需要使用通用的增删改查操作
            操作2:需要使用通用的增删改查操作
            操作3:需要使用通用的增删改查操作
            conn.commit();
        3、若出现异常,则:
            conn.rollback();
        4、关闭资源:
            JDBCUtils.closeResource(,,);


七、数据库连接池、dbutils及Spring JDBC:
    1、数据库连接池:
        1、概念:
            一个容器(集合),存放数据库连接的容器当系统初始化好后,容器被创建,容器会申请一些连接对象,
            当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器

        2、好处:
            1、提高程序的响应速度(减少了创建连接响应的时间)
            2、节约资源。降低资源的消耗(可以重复使用已进提供好的连接)
            3、便于连接的管理
            4、用户访问高效

        3、实现:
            1.标准接口:DataSource  javax.sql包下的
                1.方法:
                    获取连接:
                        getConnection();
                    归还连接:
                        Connection.close();若连接对象Connection是从连接池中获取的,
                        那么调用Connection.close()方法,则不会再关闭连接,而是归还连接

            2.一般由数据库厂商实现
                1. C3P0:数据库连接池技术
                2. Druid:数据库连接池实现技术,由阿里巴巴提供

        4、C3P0:数据库连接池技术:
            步骤:
                1.导入jar包(两个):c3p0-0.9.5.2.jar、mchange-commons-java-0.2.12.jar和数据库的驱动jar包
                2.定义配置文件
                    名称:c3p0-config.xml或者c3p0.properties
                    路径:直接将文件放在src目录下即可
                3.创建核心对象 数据库连接池对象 ComboPooledDataSource
                4.获取连接对象:getConnection
    2、几个相关配置文件

{
        druid.properties{
            driverClassName=com.mysql.jdbc.Driver
            url=目标数据库的URL
            username=目标数据库用户名
            password=目标数据库密码
            # 初始化连接数量
            initialSize=5
            # 最大连接数量
            maxActive=10
            # 最大等待时间
            maxWait=3000
        }

        jdbc.properties{
            url=目标数据库的URL
            username=目标数据库用户名
            password=目标数据库密码
            driver=com.mysql.jdbc.Driver
        }
}

    3、C3P0,德鲁伊的使用

{
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    import org.apache.commons.dbutils.DbUtils;

    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.PreparedStatement;
    import java.util.Properties;

    public class JDBCUtils {

        /**
        * 使用c3p0的数据库连接池技术
        * @return
        * @throws SQLException
        */
        //1.创建数据库连接池对象,将此对象设置为全局常量,避免每次调用方法时重新创建对象,保证数据库连接池只有一个
        private static DataSource ds=new ComboPooledDataSource();
        public static Connection getConnection00() throws SQLException {

            //2.获取连接对象
            Connection conn = ds.getConnection();
            return conn;
        }

        /**
        * 使用druid的数据库连接池技术
        */
        private static DataSource source00;
        static {
            try {
                Properties pros = new Properties();
                InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
                pros.load(is);
                source00 = DruidDataSourceFactory.createDataSource(pros);
            }catch (Exception e){e.printStackTrace();}
        }

        public static Connection getConnection01() throws Exception {

            Connection conn = source00.getConnection();
            return conn;
        }

            
        public static void closeResource(Connection conn, PreparedStatement ps, ResultSet rs){
                  try {
                      DbUtils.close(conn);
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
                  try {
                      DbUtils.close(ps);
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }
                  try {
                      DbUtils.close(rs);
                  } catch (SQLException e) {
                      e.printStackTrace();
                  }

                /**
                * 也可以使用dbutils.jar中提供的DbUtils类,实现资源关闭
                *
                *   DbUtils.closeQuietly(conn,ps,rs);
                */

                
            }
    }
}

    4、commons-dbutils的使用(
        commons-dbutils 是Apache组织提供的一个JDBC工具类,封装了针对于数据库增删改查操作
    )

{

    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.ResultSetHandler;
    import org.apache.commons.dbutils.handlers.*;
    import org.junit.Test;

    import java.sql.Connection;
    import java.sql.Date;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;

    /*
        commons-dbutils 是Apache组织提供的一个JDBC工具类,封装了针对于数据库增删改查操作
    */
    public class QueryRunnerTest {
        //测试插入
        @Test
        public void testInsert00(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql = "INSERT INTO `user`(id,username,password,birthday)VALUES(?,?,?,?);";
                int insertCount = runner.update(conn, sql, 6, "张三丰", "1133", "1997-09-08");
                System.out.println("添加了" + insertCount + "条记录");
            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        /*
            测试查询
                BeanHandler:
                    是ResultSetHandler接口的实现类,用于封装表中一条记录

                BeanListHandler:
                    是ResultSetHandler接口的实现类,用于封装表中多条记录构成的集合
                
                MapHandler:
                    是ResultSetHandler接口的实现类,对应表中一条记录,
                    将字段即相应字段的值作为map中的键key和值value
                    
                MapListHandler:
                    是ResultSetHandler接口的实现类,对应表中多条记录,
                    将字段即相应字段的值作为map中的键key和值value,构成map集合
                    
                ScalarHandler:
                    是ResultSetHandler接口的实现类,用于执行sql中的特殊查询
        */
        @Test
        public void testQuery00(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT id,username,`password`,birthday FROM `user` WHERE id=?;";
                BeanHandler handler = new BeanHandler<>(User.class);

                User user = runner.query(conn, sql, handler, 2);
                System.out.println(user);
            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        @Test
        public void testQuery01(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT id,username,`password`,birthday FROM `user` WHERE id listHandler = new BeanListHandler<>(User.class);

                List list = runner.query(conn, sql, listHandler, 5);
                for (User user : list) {
                    System.out.println(user);
                }

            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        @Test
        public void testQuery02(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT id,username,`password`,birthday FROM `user` WHERE id=?;";
                MapHandler handler = new MapHandler();

                Map map = runner.query(conn, sql, handler, 5);

                System.out.println(map);

            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                 }
            }
        }

        @Test
        public void testQuery03(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT id,username,`password`,birthday FROM `user` WHERE id> mapList = runner.query(conn, sql, listHandler, 5);

                mapList.forEach(System.out::println);

            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        @Test
        public void testQuery04(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT COUNT(*) FROM `user`;";
                ScalarHandler handler = new ScalarHandler<>();
                long count = runner.query(conn, sql, handler);

                System.out.println(count);

            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        @Test
        public void testQuery05(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();
                String sql="SELECT MAX(birthday) FROM `user`;";
                ScalarHandler handler = new ScalarHandler<>();
                Date date = runner.query(conn, sql, handler);

                System.out.println(date);

            }catch (Exception e){e.printStackTrace();}finally {
                if(conn!=null) {
                    try {
                        conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }

        /*
            自定义一个ResultSetHandler接口的实现类
       */
        @Test
        public void testQuery06(){
            Connection conn =null;
            try {
                QueryRunner runner = new QueryRunner();
                conn = JDBCUtils.getConnection();

                String sql="SELECT id,username,`password`,birthday FROM `user` WHERE id=?;";

                ResultSetHandler handler=new ResultSetHandler() {
                    @Override
                    public User00 handle(ResultSet resultSet) throws SQLException {
                        System.out.println("handler");
                        //  return null;
                        //  return new User(1,"小美","5331",new Date(3232637648L));
                        if(resultSet.next()){
                            int id = resultSet.getInt(1);
                            String username = resultSet.getString(2);
                            String password = resultSet.getString(3);
                            Date birthday = resultSet.getDate(4);
                            User user = new User(id, username, password, birthday);
                            return user;
                        }
                        return null;
                     }
                 };
                 User user = runner.query(conn, sql, handler, 2);
                 System.out.println(user);


            }catch (Exception e){e.printStackTrace();}finally {
                 if(conn!=null) {
                     try {
                         conn.close();
                    }catch (Exception e){e.printStackTrace();}
                }
            }
        }
    }
}

    5、Spring JDBC(
        Spring框架对JDBC的简单封装,提供了一个JDBCTemplate对象简化JDNC开发
        步骤:
            1.导入jar包
            2.创建JDBCTemplate对象。依赖于数据源dataSource
                JdbcTemplate template = new JdbcTemplate(ds);
            3.调用JdbcTemplate的方法来完成CRUD(增删查改)的操作
                update();执行DML语句。增删改语句
                queryForMap();查询结果将结果集封装为map集合,将列名作为key,将值作为value,将这条记录封装为一个map集合
                    注:查询的结果集只能是1
                queryForList();查询结果将结果集封装为list集合
                    注:将每一条记录封装为一个map集合,再将map集合装载到List集合中
                query();查询结果,将结果封装为javaBean对象
                    注:query的参数:RowMapper
                        一般使用BeanPropertyRowMapper实现类。可以完成数据到javaBean的自动封装
                        new BeanPropertyRowMapper<类型>(类型.class)
                queryForObject();查询结果,将结果封装为对象
                    一般用于聚合函数的查询
    )

{

    利用德鲁伊数据库连接池建立一个JDBCUtils
    import com.alibaba.druid.pool.DruidDataSourceFactory;

    import javax.sql.DataSource;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.PreparedStatement;
    import java.util.Properties;

    /**
    * Druid连接池工具类
    */
    public class JDBCUtils {
        //1.定义一个成员变量DataSource
        private static DataSource ds ;

        static{

        try {
            //1.加载配置文件
            Properties pro = new Properties();           pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //2.获取DataSource
            ds= DruidDataSourceFactory.createDataSource(pro);
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
    * 获取连接的方法
    */
    public static Connection getConnection01() throws SQLException {
        return ds.getConnection();
    }

    /**
    * 释放资源
    */
    public static void close01(PreparedStatement ps,Connection conn){
        close01(null,ps,conn);
    }
    
    public static void close01(ResultSet rs,PreparedStatement ps, Connection conn){
        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();
             }
         }
    }

    /**
    * 获取连接池方法
    */
    public static DataSource getDataSource01(){
        return ds;
    }

}




JdbcTemplate入门




public class testJdbcTemplate {
    public static void main(String[] args) {
        //1.导入jar包
        //2.创建JDBCTemplate对象
        JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource01());
        //3.定义sql
        String sql="UPDATE user SET password=246 WHERE id=?";
        //3.调用方法
        int count = template.update(sql, 1);
        System.out.println(count);

    }
}





        Spring JDBC练习
        需求:
            1.修改1号数据的 password 为100
            2.增加一条记录
            3.删除一条记录
            4.查询id为1的记录,将其封装为Map集合
            5.查询所有记录,将其封装为list集合
            6.查询所有记录,将其封装为user00对象的集合
            7.查询总记录数






{
    import org.junit.Test;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.RowMapper;

    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.List;
    import java.util.Map;


    public class testJdbcTemplateOne {

        //1.获取JDBCTemplate对象
        JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource01());


        /**
        * 1.修改1号数据的 password 为100
        */
        @Test
        public void tset01(){
            /*
                //2.定义sql
                String sql="UPDATE user SET password='321' WHERE id=1";
                //3.执行sql
                int count = template.update(sql);
                System.out.println(count);

            */

            String sql1="UPDATE user SET password=? WHERE id=?";
            int update = template.update(sql1, "123", 1);
            System.out.println(update);


         }


        /**
        * 2.增加一条记录
        */
        @Test
        public void tset02(){
            //2.定义sql
            String sql="INSERT INTO `user`VALUES(?,?,?)";
            int count = template.update(sql, null, "田七", "900");
            System.out.println(count);
        }

        /**
        * 3.删除一条记录
        */
        @Test
        public void tset03(){
            //2.定义sql
            String sql="DELETE FROM user WHERE id=?";
            int count = template.update(sql, 4);
            System.out.println(count);
        }

        /**
        * 4.查询id为1的记录,将其封装为Map集合
        * 注:查询的结果集只能是1
        */
        @Test
        public void tset04(){
            //2.定义sql
            String sql="SELECT * FROM user WHERE id=?";
            Map map = template.queryForMap(sql, 1);
            System.out.println(map);//{id=1, username=张三, password=321}
         }

        /**
        * 5.查询所有记录,将其封装为list集合
        */
        @Test
        public void tset05(){
            //2.定义sql
            String sql="SELECT * FROM user";
            List> list = template.queryForList(sql);
            for (Map ignored : list) {
                System.out.println(ignored);
            }
        }

        /**
        * 6.查询所有记录,将其封装为user对象的集合
        */
        @Test
        public void tset06(){
            //2.定义sql
            String sql="SELECT * FROM user";
            List list=template.query(sql, new RowMapper() {//匿名内部类
                @Override
                public User mapRow(ResultSet rs, int i) throws SQLException {
                    User user=new User();
                    int id = rs.getInt("id");
                    String username = rs.getString("username");
                    String password = rs.getString("password");

                    user.setId(id);
                    user.setUsername(username);
                    user.setPassword(password);
                    return user;
                 }
             });

             for (User user : list) {
                 System.out.println(user);

             }
        }

        /**
        * 6.查询所有记录,将其封装为user对象的集合
        */
        @Test
        public void tset06_1(){
            //2.定义sql
           String sql="SELECT * FROM user";
           List list = template.query(sql, new BeanPropertyRowMapper(User.class));
            for (User user : list) {
                System.out.println(user);
            }
         }

        /**
        * 7.查询总记录数
        */
        @Test
        public void tset07(){
            //2.定义sql
            String sql="SELECT COUNT(id) FROM user";
            Long total = template.queryForObject(sql, Long.class);
            System.out.println(total);
        }

        /**
        * 8.查询总记录数
        */
        @Test
        public void tset08(){
            //2.定义sql
            String sql="SELECT COUNT(id) FROM user";
            Integer total = template.queryForObject(sql, Integer.class);
            System.out.println(total);
        }
    }
}

tip:
    1、两种思想:
        面向接口编程思想
        ORM编程思想

    2、两种技术:
        反射:
            创建对应的运行时类的对象
            在运行时,动态调用指定运行时类的属性,方法
        结果集的元数据:ResultSetMetaData
            >getColumnCount():获取列数
            >getColumnLable():获取列的别名

以上是本篇小节,不喜勿喷,感谢理解

相关链接:

【JAVAEE基础学习(1)】--HTML&CSS篇_lixxkv的博客-CSDN博客

【JAVASE(1)】JAVASE学习--基础篇_lixxkv的博客-CSDN博客_javase学习

你可能感兴趣的:(JAVASE学习,java)