JDBC核心技术

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8M1ekrTA-1631929613313)(E:\DataBasesWork\PictureTool\JDBC编写步骤.jpg)]

获取数据库连接

要素1:Driver接口实现类

@Test
public void test1() throws SQLException {
    Driver d=new com.mysql.jdbc.Driver();
    String s="jdbc:mysql://localhost:6636/test";
    /*
    jdbc:mysql  协议
    localhost   ip地址
    6636        端口号
    test        数据库
     */
    Properties p=new Properties();
    //Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。 
    p.setProperty("user","root");//用户名
    p.setProperty("password","12345ssdlh");//密码
    //将用户名1密码封装在Properties里面
    Connection ct = d.connect(s, p);
    System.out.println(ct);
}

要素1进阶阶段1

//获取Driver的实现类对象【运用反射】

@Test
public void test2() throws Exception {
    
    //如下程序不出现第三方的api,使得程序有更好的可移植性
    Class<?> clazz = Class.forName("com.mysql.jdbc.Driver");
    Driver o = (Driver) clazz.newInstance();
    //提供要连接的数据库
    String url="jdbc:mysql://localhost:6636/test";
    //提供连接需要的用户名和密码
    Properties info=new Properties();
    info.setProperty("user","root");
    info.setProperty("password","12345ssdlh");
    Connection connect = o.connect(url, info);
    System.out.println(connect);
}

要素1的进阶阶段2

//使用DriverManager【类】 替换 Driver【接口】

@Test
public void test3()throws Exception{
  
    //获取Driver实现类对象
    Class<?> clazz =  Class.forName("com.mysql.jdbc.Driver");
    Driver o = (Driver) clazz.newInstance();

    //注册驱动
    DriverManager.registerDriver(o);
  //获取连接
    Connection root = DriverManager.getConnection("jdbc:mysql://localhost:6636/test", "root", "12345ssdlh");
    System.out.println(root.toString());

}

要素1的进阶阶段3

//优化阶段3:可以只是加载驱动,不用手动注册驱动了

@Test
    public void test5()throws Exception{
        //获取Driver实现类对象
//       Class.forName("com.mysql.jdbc.Driver");
//mysql-connector-java-5.1.7-bin.jar!/META-INF/services/java.sql.Driver:读该路径下的文件已经直接做Class.forName(xxx)了,不过只针对mysql,如果连接的是orance数据库则不能省略,故最好不要省略
        Class.forName("com.mysql.jdbc.Driver");
//        Driver o = (Driver) clazz.newInstance();

        //注册驱动
//        DriverManager.registerDriver(o);
        //获取连接
        Connection root = DriverManager.getConnection("jdbc:mysql://localhost:6636/test", "root", "12345ssdlh");
        System.out.println(root.toString());
        /*省略如上操作
        在mysql的Driver实现类中,声明了如下的操作,
        static {
            try {
                java.sql.DriverManager.registerDriver(new Driver());
            } catch (SQLException E){
                throw new RuntimeException("Can't register driver!");
            }
        }
         */
    }

要素1的进阶阶段4【最终方法】

DriverManager.getConnection(库的地址,用户名,用户密码);//创建连接,返回值为Connection类型
@Test
public void test_final() throws Exception {
/*将数据库连接需要的4个配置信息声明在配置文件中,
通过读取配置文件的方式,获取连接
 */
    //读取配置文件中的4个基本信息
    InputStream is= ConnectionTest.class.getClassLoader().getResourceAsStream("JDBC.properties");
    Properties p=new Properties();
    p.load(is);
    String user = p.getProperty("user");
    String password = p.getProperty("password");
    String url = p.getProperty("url");
    String driverClass = p.getProperty("driverClass");
    //加载驱动
    Class.forName(driverClass);
    //获取连接
    Connection conn = DriverManager.getConnection("url", "user", "password");
    System.out.println(conn);
}

配置文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEHW0IzI-1631929613319)(E:\DataBasesWork\PictureTool\JDBC配置文件.jpg)]

此方法的好处

1、实现数据与代码的分离,实现了解耦

2、如果需要修改配置文件,可以避免程序重新打包

每次重启电脑后都需重设mysql时区

cmd中,输入mysql -u root -p,再输入密码,进入mysql命令行。之后
修改时区:set global time_zone='+8:00';在这里插入图片描述
即可成功。

操作数据库

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QRKWYoQP-1631929613320)(E:\DataBasesWork\PictureTool\数据库操作步骤.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRIh2PFc-1631929613322)(E:\DataBasesWork\PictureTool\执行SQL.jpg)]

Ststement在操作数据库时有弊端,故不再使用

PreparedStatement执行sql语句的函数:

返回值 函数名及其介绍
boolean execute() 执行此 PreparedStatement对象中的SQL语句,这可能是任何类型的SQL语句。
default long executeLargeUpdate() 执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,如INSERTUPDATEDELETE ; 或不返回任何内容的SQL语句,例如DDL语句。
ResultSet executeQuery() 执行此 PreparedStatement对象中的SQL查询,并返回查询 PreparedStatementResultSet对象。
int executeUpdate() 执行在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,如INSERTUPDATEDELETE ; 或不返回任何内容的SQL语句,例如DDL语句。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m8bvoj54-1631929613326)(E:\DataBasesWork\PictureTool\JAVA与SQL数据类型转换.jpg)]

PreparedStatement操作

增 删 改

插入数据【原始】

  • Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。【PropertiesHashtable的子类】

  • Connection实例化对象.prepareStatement(sql语句);//创建一个 PreparedStatement对象,用于将参数化的SQL语句发送到数据库。返回值为prepareStatement类型
    
    prepareStatement实例化对象.execute();//执行此 PreparedStatement对象中的SQL语句,这可能是任何类型的SQL语句。返回值为boolean类型
    如果是查询操作的话返回true,如果是增删改操作的话返回false
    
@Test
public void test(){
    /*
    使用PreparedStatement来代替Statement进行对数据库的增删改查操作
     */
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        //InputStream ra = ClassLoader.getSystemResourceAsStream("JDBC.properties");
        //获取连接
        InputStream ra = ClassLoader.getSystemClassLoader().getResourceAsStream("JDBC.properties");
        Properties p=new Properties();
        p.load(ra);
        String user = p.getProperty("user");
        String password = p.getProperty("password");
        String url = p.getProperty("url");
        String driverClass = p.getProperty("driverClass");
        Class.forName(driverClass);
        conn = DriverManager.getConnection(url, user, password);
        System.out.println(conn);
        //预编译sql语句,返回PreparedStatement的实例
        String sql="insert into customers(name,email)values (?,?)";/*?起到占位符的作用*/
        
        ps = conn.prepareStatement(sql);
        //填充占位符
        ps.setString(1,"张飞");
        ps.setString(2,"null");
        //执行sql语句
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        //关闭资源
        try {
            if(ps!=null)
            {
                ps.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        try {
            if(conn!=null)
            {
                conn.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

    }

由于涉及到多个异常,资源的关闭,此时就不能使用throws抛出错误,要用try/catch;

***throws与try/catch的使用区别:***throws exception会将异常上抛,在循环中会中断循环;try catch将异常捕获,不影响下一条执行

通用增删改操作

【以删除为例】

@Test
public void test3() {
    try {
        String s="delete from customers where id=?";
        test_tool(s,22);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public void test_tool(String s,Object ...args) {
    Connection conn=null;
    PreparedStatement ps=null;
    try {
        conn = JDBC_utils.get_connection();
        ps=conn.prepareStatement(s);
        for(int i=0;i< args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        ps.execute();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
    JDBC_utils.Close_resource(conn,ps);}
}
【如果表名和`sql`语句关键字一样时,通过`jdbc`调用`sql`语句时表名要写成  `表名`  ;不然会报错】

查询

通用操作初始

**情况:**类中的属性名和数据库中的列名刚好相同

prepareStatement实例化对象.executeQuery();//执行此 PreparedStatement对象中的SQL查询,并返回查询 PreparedStatement的 ResultSet对象。 
ResultSet实例化对象.getMetaData();//获取结果集的元数据,通过元数据来对结果集进行深度解析,返回值类型为ResultSetMetaData
ResultSet实例化对象.getObject(下标数【mysql中下标从1开始】);//获取数据库中该下标指向列的数据;返回类型为Object
ResultSetMetaData实例化对象.getColumnCount();//获取结果集的列数,返回值为int
ResultSetMetaData实例化对象.getColumnName(下标数【mysql中下标从1开始】);//获取数据库中该下标指向列的列名;返回类型为String
@Test
public void test4(){
    String sql="select id,name,email,birth from customers where id=?";
    Customer customer = test4_Tool(sql, 5);
    System.out.println(customer);
}
public Customer test4_Tool(String sql,Object...args){
    //针对customers这个表的通用查询操作
    Connection conn=null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    try {
        conn=JDBC_utils.get_connection();
        ps=conn.prepareStatement(sql);
        for(int i=0;i<args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        ResultSet resultSet = ps.executeQuery();
        //获取结果集的元数据
        ResultSetMetaData rsmd = resultSet.getMetaData();
        //通过结果集元数据【ResultSetMetaData】获取结果集的列数
        int cc = rsmd.getColumnCount();
        if (resultSet.next()){
            Customer c=new Customer();
            for (int i=0;i<cc;i++){
                Object value = resultSet.getObject(i + 1);
                //获取每个列的列名
                String cn = rsmd.getColumnName(i + 1);
                //通过反射设置属性
                Class<?> clazz = Class.forName("com.study.util.Customer");
                Field df = clazz.getDeclaredField(cn);
                df.setAccessible(true);
                //给c对象某个属性,赋值为value;
                df.set(c,value);
            }
            return c;
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBC_utils.Close_resource(conn,ps);
        try {
            if(rs!=null)
                rs.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    return null;
}

ORM编程思想(object relational mapping)

一个数据对应一个java

表中的一条数据对应java类的一个对象
表中的一个字段对应java类的一个属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tWoUap5Q-1631929613327)(E:\DataBasesWork\PictureTool\Customer类.jpg)]

通用操作进阶1

**情况:**类中的属性名和数据库中的列名不相同

ResultSetMetaData实例化对象.getColumnName(下标数【mysql中下标从1开始】);//不推荐使用
ResultSetMetaData实例化对象.getColumnLabel(下标数【mysql中下标从1开始】);//获取数据库中每个列的列名的别名,如果没起别名则获取数据库中该下标指向列的列名;返回类型为String

***例子:***对order表的通用查询

@Test
public void test2() {
    //对order表的通用查询
    String sql="select order_id as orderId,order_name as orderName,order_date as orderDate from `order` where order_id=?";
    Order order = test2_tool(sql, 1);
    System.out.println(order);
}
public Order test2_tool(String sql,Object...args) {
    Connection conn = null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    try {
        conn = Datebases_Tools.get_connection();
        ps=conn.prepareStatement(sql);
        for(int i=0;i<args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        //获取结果集
        rs = ps.executeQuery();
        //获取结果集的元数据
        ResultSetMetaData rsmd = rs.getMetaData();
        if (rs.next()){
            //获取列数
            int count = rsmd.getColumnCount();
            Order or=new Order();
            for(int i=0;i<count;i++){
                //获取每个列的列值(通过结果集)
                Object object = rs.getObject(i + 1);

                /*
                获取数据库中每个列的列名(通过结果集的元数据)
                String columnName = rsmd.getColumnName(i + 1);//不推荐使用
                但是数据库中每列的列名与类中的属性名不一定相同
                此时就需要获取类的别名,并且在编辑sql语句时
                用类的属性名作为列的别名
                */

                //获取数据库中每个列的列名的别名(通过结果集的元数据)
                String columnLabel = rsmd.getColumnLabel(i + 1);
                //通过反射,将对象指定名的属性赋值为指定的值
                Class<?> clazz = Class.forName("com.study.class_tools.Order");
                Field df = clazz.getDeclaredField(columnLabel);

                df.setAccessible(true);
                df.set(or,object);
            }
            return or;
        }
    } catch (Exception throwables) {
        throwables.printStackTrace();
    } finally {
        Datebases_Tools.close_resource(conn,ps,rs);
    }
    return null;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ljFRZKIe-1631929613328)(E:\DataBasesWork\PictureTool\order类.jpg)]

通用操作进阶2

**情况:**针对不同的表的通用的查询操作,返回表中的一条记录

//下面是泛型方法的定义:
[public] [static] <T> 返回值类型 方法名(T 参数列表){函数体}
【根本作用:使得函数返回值类型为泛型T

泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:

无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,

那么就应该使用泛型方法。另外对于一个static的方法而已,无法访问泛型类型的参数。

所以如果static方法要使用泛型能力,就必须使其成为泛型方法。
@Test
public void test3(){
    //对不同表的通用查询操作
    String sql="select id,name,email from customers where id =?";
    Customer customer = test3_tool(Customer.class, sql, 1);
    System.out.println(customer);
    sql="select order_id as orderId,order_name as orderName from `order` where order_id=?";
    Order order = test3_tool(Order.class, sql, 2);
    System.out.println(order);
}
public <T> T test3_tool(Class<T> clazz,String sql,Object...args){
    //泛型方法
    Connection conn =null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    try {
        conn =Datebases_Tools.get_connection();
        ps = conn.prepareStatement(sql);
        for(int i=0;i<args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        rs = ps.executeQuery();
        if(rs.next()){
            T t = clazz.newInstance();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            for(int i=0;i<columnCount;i++){
                String columnLabel = rsmd.getColumnLabel(i + 1);
                Object value = rs.getObject(i + 1);
                Field declaredField = clazz.getDeclaredField(columnLabel);
                declaredField.setAccessible(true);
                declaredField.set(t,value);
            }
            return t;
        }
    } catch (Exception throwables) {
        throwables.printStackTrace();
    } finally {
        Datebases_Tools.close_resource(conn,ps,rs);
    }
    return null;}

通用操作进阶3

**情况:**针对不同的表的通用的查询操作,返回表中的多条记录

forEach(Consumer action);//对Iterable的每个元素执行给定的操作,直到所有元素都被处理或动作引发异常。
@Test
    public void test4(){
        String sql="select order_id as orderId,order_name as orderName,order_date as orderDate from `order` where order_id>?";
        List<Order> orders =test4_test(Order.class, sql, 0);
//        for(int i=0;i
//            System.out.println(orders.get(i));
//        }
        orders.forEach(System.out::println);
        //输出
        sql ="select id,name,email from customers";
        List<Customer> customers = test4_test(Customer.class, sql);
        customers.forEach(System.out::println);
    }
    public <T> List<T> test4_test(Class<T> clazz,String sql, Object...args){
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        List<T> lt=new ArrayList<T>();
        try {
            conn=Datebases_Tools.get_connection();
            ps=conn.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            while (rs.next()){
                T t = clazz.newInstance();
                int columnCount = rsmd.getColumnCount();
                for(int i=0;i<columnCount;i++){
                    Object value = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);
                    Field declaredField = clazz.getDeclaredField(columnLabel);
                    declaredField.setAccessible(true);
                    declaredField.set(t,value);
                }
                lt.add(t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Datebases_Tools.close_resource(conn,ps,rs);
            return lt;
        }
}

PreparedStatement补充知识点:

  • 表示预编译SQL语句的对象,解决SQL注入问题原理;

①、PreparedStatement可操作Blob的数据,而Statement做不到

②、PreparedStatement可以实现更高效的批量操作

executeUpdate()

JDBC操纵数据库实战

**说明:**学生四六级考试查询系统

import com.study.Datebases_Tools;
import com.study.class_tools.ExamStudent;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.Scanner;

public class CET_4 {
    public static void main(String[] args) {
        int i=1;
        while(i>0){
            Scanner in=new Scanner(System.in);
            System.out.println("请输入要执行的操作:1、添加学生信息。2、查询学生信息。3、删除学生信息。4、退出系统");
            int a=in.nextInt();
            if(a==1){
                String sql="insert into examstudent values(?,?,?,?,?,?,?)";
                System.out.print("请输入FlowID:");
                int folwid=in.nextInt();
                System.out.print("请输入类型:");
                int type=in.nextInt();
                System.out.print("请输入身份证:");
                String idcard=in.next();
                System.out.print("请输入考号:");
                String ExamCard=in.next();
                System.out.print("请输入学生姓名:");
                String StudentName=in.next();
                System.out.print("请输入家庭住址:");
                String location=in.next();
                System.out.print("请输入年级:");
                int grade=in.nextInt();
                CET_4_Tool1(ExamStudent.class,a,sql,folwid,type,idcard,ExamCard,StudentName,location,grade);
            }
            else if(a==2){
                String sql="select FlowID as flowid,`Type` as type,IDCard as ID,ExamCard as Exam,StudentName as name,Location as location,Grade as grade from examstudent where FlowID=?;";
                System.out.println("请输入需查询信息的学生的FlowID");
                int flowid=in.nextInt();
                ExamStudent es = CET_4_Tool1(ExamStudent.class, a, sql, flowid);
                if(es==null){
                    System.out.println("查无此人");
                }
                else {
                    System.out.println(es.toString());
                }

            }
            else if(a==3){
                System.out.println("请输入需删除信息的学生的FlowID");
                String sql="delete from examstudent where FlowID=?";
                int flowid=in.nextInt();
                CET_4_Tool1(ExamStudent.class,a,sql,flowid);
            }
            else if(a==4){
                break;
            }
        }
        System.out.println("退出成功");
    }
    public static  T CET_4_Tool1(Class clazz,int chance,String sql,Object...args){
        Connection conn =null;
        PreparedStatement ps =null;
        ResultSet rs =null;
        try {
            conn = Datebases_Tools.get_connection();
            ps = conn.prepareStatement(sql);
            for(int i=0;i

用到的类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hZTG8jvu-1631929613328)(E:\DataBasesWork\PictureTool\四六级系统实战.jpg)]

操纵Blob类型数据

类型 大小(单位:字节)
TinyBlob <=255字节
Blob <=65K
MediumBlob <=16M
LongBlob <=4G

数据库中插入blob对象

.setBlob(int parameterIndex, InputStream inputStream) 
将指定的参数设置为 InputStream对象。
Blob类型:MYSQL中,Blob是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据
***
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找到my.ini文件加上如下的配置参数:max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务
@Test
public void testInsert()throws Exception{
    InputStream is=new FileInputStream("E:\\软件包暂存\\wbb.jpg");
    Connection conn = Datebases_Tools.get_connection();
    String sql="update customers set photo=? where id=?";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setBlob(1,is);
    ps.setObject(2,22);
    ps.execute();
    Datebases_Tools.close_resource(conn,ps);
    is.close();
}

查询数据表中的blob对象

Blob类型对象.getBinaryStream();//将此 Blob实例指定的 BLOB值作为流 Blob;返回值类型为InputStream
@Test
public void test_select_blob(){
    Connection conn=null;
    PreparedStatement ps=null;
    ResultSet rs=null;
    InputStream is=null;
    FileOutputStream fos=null;
    try {
        conn=Datebases_Tools.get_connection();
        String sql="select photo from customers where id=?";
        ps = conn.prepareStatement(sql);
        ps.setObject(1,16);
        rs=ps.executeQuery();
        if(rs.next()){
            Blob photo = rs.getBlob(1);
            is = photo.getBinaryStream();
            fos=new FileOutputStream("E:\\Desktop\\朱颖.jpg");
            byte[] b=new byte[1024];
            int len;
            while((len=is.read(b))!=-1){
                fos.write(b,0,len);
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        Datebases_Tools.close_resource(conn,ps,rs);
        try {
            if(is!=null)
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if(fos!=null)
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

批量插入数据

初始

Connection类型实例.setAutoCommit(boolean t);//将此连接的自动提交模式设置为给定状态。

Connection类型实例.commit();// 使自上次提交/回滚以来所做的所有更改都将永久性,并释放此 Connection对象当前持有的任何数据库锁。

PreparedStatement实例对象.addBatch();//向这个PreparedStatement对象的一批命令添加一组参数。

PreparedStatement实例对象.executeBatch();//向数据库提交要运行的一批命令。如果所有命令都成功运行,则返回一个更新计数数组;
向数据库提交命令后,此方法将清除批中的所有命令;
通常是通过先使用addBatch()来缓存数据,将多条sql语句缓存起来,再通过executeBatch()方法一次性发给数据库,提高执行效率;
注意:
使用addBatch()缓存数据时要在循环中设置条件,当循环达到指定次数后执行executeBatch(),将缓存中的sql全部发给数据库,然后执行clearBatch()清楚缓存,否则数据过大是会出现OutOfMemory(内存不足)。

PreparedStatement实例对象.clearBatch();//
【javaAPI查不到】
@Test
public void test_batch_insert(){
    //快速批量插入
    Connection conn = null;
    PreparedStatement ps = null;
    try {
        long start= System.currentTimeMillis();
        conn = Datebases_Tools.get_connection();
        //设置并不允许自动提交
        conn.setAutoCommit(false);
        String sql="insert into a1 set age=?";
        ps = conn.prepareStatement(sql);
        for(int i=0;i<66666;i++){
            ps.setObject(1,"name_"+i);
            //1、"攒”SQL
            ps.addBatch();
            if(i%1000==0||i==66665){
                //2、执行batch
                ps.executeBatch();
                //3、清空batch
                ps.clearBatch();
            }

        }
        conn.commit();
        long end= System.currentTimeMillis();
        System.out.println((end-start)+"ms");
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        Datebases_Tools.close_resource(conn,ps);
    }
}

PreparedStatementStatement对比(面试题)

PreparedStatement是statement的子接口

PreparedStatement是预编译sql语句

1、Statement存在拼串和SQL注入问题

2、PreparedStatement可对blob类型数据进行操作,Statement则不能

(因为PreparedStatement存在预编译这一操作)

3、在批量插入数据时PreparedStatement更高效

(因为PreparedStatement存在预编译这一操作:

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

数据库事务

事务定义:

一组逻辑操作单元,使数据从一种状态变换到另一种状态

事务处理原则:

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

数据一旦提交就不可回滚。

为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应当全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

哪些操作会导致数据的自动提交

1、DDL操作一旦执行,都会自动提交

set autocommit=falseDDL操作失效)

2、DML默认情况下,一旦执行,就会自动提交

(可以通过set autocommit=false的方式取消DML操作的自动提交)

3、默认在关闭连接时,会自动提交数据

事务演示

事例:AA给BB转账100(运用事务)

@Test
public void test1(){
    Connection conn = null;
    try {
        conn = JDBC_utils.get_Connection();
        //取消数据的自动提交
        System.out.println(conn.getAutoCommit());
        conn.setAutoCommit(false);

        String sql1="update user_table set balance=balance-100 where user=?";
        test1_tool(conn,sql1,"AA");

        System.out.println(10/0);//模拟系统出现故障

        String sql2="update user_table set balance=balance+100 where user=?";
        test1_tool(conn,sql2,"BB");
        System.out.println("转账成功");

        //提交数据
        conn.commit();
    } catch (Exception e) {
        e.printStackTrace();
        //回滚数据
        try {
            conn.rollback();
            System.out.println("转账失败");
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    } finally {
        JDBC_utils.close_resources(conn,null);
    }

}
public int test1_tool(Connection conn,String sql,Object...args){
    PreparedStatement ps = null;
    int i1 =0;
    try {
        ps = conn.prepareStatement(sql);
        for(int i=0;i<args.length;i++){
            ps.setObject(i+1,args[i]);
        }
        i1=ps.executeUpdate();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        JDBC_utils.close_resources(null,ps);
        return i1;
    }

}

事务的ACID(acid)特性

1、原子性

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

2、一致性

事务必须使数据库从一个一致性状态变换到另外一个一致性状态

3、隔离性

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

4、持久性

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

事务处理的原则:(面试题)

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

并发

对于同时运行的多个事务,当这些事务访问数据库中相同的数据时,如果没有采取必要的隔离机制,就会导致各种并发问题:

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

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

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

【详情见MYSQL笔记】

JAVA代码设置数据库隔离级别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jMhZa6JS-1631929613329)(E:\DataBasesWork\PictureTool\数据库隔离级别.jpg)]

Connection实例对象.getTransactionIsolation()//获取当前连接的隔离级别,返回值为int类型,具体含义见下文
int TRANSACTION_NONE             = 0;
int TRANSACTION_READ_UNCOMMITTED = 1;
int TRANSACTION_READ_COMMITTED   = 2;
int TRANSACTION_REPEATABLE_READ  = 4;
int TRANSACTION_SERIALIZABLE     = 8;
Connection实例对象.setAutoCommit(false);//关闭当前连接的自动提交功能
Connection实例对象.getAutoCommit()//查看当前连接的自动提交功能是否打开
Connection实例对象.commit();//关闭当前连接的自动提交功能之后进行手动提交操作
Connection实例对象.rollback();//关闭当前连接的自动提交功能之后进行手动回滚操作
【两者刚好对立】
Connection实例对象.setTransactionIsolation(Connection.隔离级别||int level);//设置当前连接的隔离级别

所有隔离级别将在重启数据库服务时返回默认状态(包括在控制台更改的隔离级别)

DAO及其实现类

DAO:data(base) access object(数据库访问对象)

封装了针对于数据库的通用操作

大体思维图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMyjcG4D-1631929613329)(E:\DataBasesWork\PictureTool\BAO模式思维图.jpg)]

陌生知识点

Class实例对象.getGenericSuperclass();//返回值Type类型,表示此所表示的实体(类,接口,基本类型或void)的直接超类(即父类) 。
Type类型:TypeJava编程语言中所有类型的通用超级接口。 这些包括原始类型,参数化类型,数组类型,类型变量和原始类型。 
ParameterizedType类型:ParameterizedType表示一个参数化类型,如Collection <String>,set<int>。 参数化类型在第一次通过反射方法需要时创建,当创建参数化类型p时,p实例化的泛型类型声明被解析,并且p的所有类型参数都是递归创建的。
ParameterizedType实例对象.getActualTypeArguments();//返回一个表示此类型的实际类型参数的Type数组对象。 (Type数组索引从0开始)

class Customer

//具体类《Customer》

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QHclqsgS-1631929613330)(E:\DataBasesWork\PictureTool\Customer类.jpg)]

class BaseDAO

**//通用方法带泛型父类《BaseDAO》:**封装了针对于数据表的通用的操作

import tools.JDBC_utils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/*
封装了针对于数据表的通用的操作
 */
public abstract class BaseDAO<T> {
    /**
     * 通用增删改查操作
     */
    private Class<T> clazz=null;
    //获取当前BaseDAO的子类继承的父类中的具体泛型(也就是本代码中创建子类时的"BaseDAO"中的"Customer")
    public BaseDAO() {
        //获取父类的泛型
        Type gsc = this.getClass().getGenericSuperclass();
        ParameterizedType pt= (ParameterizedType) gsc;
        Type[] ata = pt.getActualTypeArguments();
        clazz= (Class<T>) ata[0];
    }
    //查一个
    public T select(Connection conn, String sql, Object...args){
        PreparedStatement ps =null;
        ResultSet rs =null;
        try {
            ps = conn.prepareStatement(sql);
            for(int i=0;i< args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            rs=ps.executeQuery();
            if(rs.next()){
                ResultSetMetaData rsmd = rs.getMetaData();
                T t=clazz.newInstance();
                for(int i=0;i<rsmd.getColumnCount();i++){
                    String columnLabel = rsmd.getColumnLabel(i + 1);
                    Object value = rs.getObject(i + 1);
                    Field filed = clazz.getDeclaredField(columnLabel);
                    filed.setAccessible(true);
                    filed.set(t,value);
                }
                return t;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(null,ps,rs);
        }
        return null;
    }
    //查多个
    public List<T> selects(Connection conn, String sql, Object...args){
        PreparedStatement ps=null;
        ResultSet rs=null;
        List<T> t1=new ArrayList<>();
        try {
            ps=conn.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            rs=ps.executeQuery();
            while(rs.next()){
                ResultSetMetaData rsmd = rs.getMetaData();
                T t=clazz.newInstance();
                for(int i=0;i<rsmd.getColumnCount();i++){
                    Object value = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i + 1);
                    Field field = clazz.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(t,value);
                }
                t1.add(t);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(null,ps,rs);
            return t1;
        }

    }
    //增删改
    public int update(Connection conn,String sql,Object...args){
        PreparedStatement ps = null;
        int i1 =0;
        try {
            ps = conn.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            return ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBC_utils.close_resources(null,ps);

        }
        return 0;
    }
    //查询特殊值,比如“select count(*) from 表名”
    public <T> T getValue(Connection conn,String sql,Object...args){
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            ps = conn.prepareStatement(sql);
            for(int i=0;i<args.length;i++){
                ps.setObject(i+1,args[i]);
            }
            rs = ps.executeQuery();
            if(rs.next()){
                return (T) rs.getObject(1);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBC_utils.close_resources(null,ps,rs);
        }
        return null;}
}

interface CustomerDAO

**//用于规定具体对象方法操作的接口《CustomerDAO》:**此接口用于规范针对于Customers表的常用操作

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

/*
此接口用于规范针对于Customers表的常用操作
 */
public interface CustomerDAO {
    //将cust对象添加到数据库中
    void insert(Connection conn, Customer cust);
    //根据指定的id删除表中一条记录
    void deleteById(Connection conn,int id);
    //针对内存中的cust对象,去修改数据表中指定的记录
    void update(Connection conn,Customer cust);
    //针对指定的id查询得到对应的Customer对象
    Customer getCustomerById(Connection conn,int id);
    //查询表中的所有记录构成的集合
    List getAll(Connection conn);
    //返回数据表中数据的条目数
    Long getCount(Connection conn);
    //返回数据表中最大的生日
    Date getMaxBirth(Connection conn);
}

class CustomerDAOImpl

**//具体对象方法实现类《CustomerDAOImpl》:**用于实现接口中所规定的类

import interface_tools.CustomerDAO;
import test.DAO.BaseDAO;

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

public class CustomerDAOImpl extends BaseDAO<Customer> implements CustomerDAO {


    @Override
    public void insert(Connection conn, Customer cust) {
        String sql="insert into customers(id,name,email,birth) values(?,?,?,?)";
        update(conn,sql,cust.getId(),cust.getName(),cust.getEmail(),cust.getBirth());
    }

    @Override
    public void deleteById(Connection conn, int id) {
        String sql="delete from customers where id=?";
        update(conn,sql,id);
    }

    @Override
    public void update(Connection conn, Customer cust) {
        String sql="update customers set name=?,email=?,birth=? where id=?";
        update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
    }

    @Override
    public Customer getCustomerById(Connection conn, int id) {
        String sql="select id,`name`,email,birth from customers where id=?";
        return select(conn,sql,id);
    }

    @Override
    public List<Customer> getAll(Connection conn) {
        String sql="select id,name,email,birth from customers";
        return selects(conn,sql);
    }

    @Override
    public Long getCount(Connection conn) {
        String sql="select count(*) from customers";
        return getValue(conn,sql);
    }

    @Override
    public Date getMaxBirth(Connection conn) {
        String sql="select max(birth) from customers";
        return getValue(conn,sql);
    }
}

class CustomerDAOImplTest

**//具体对象方法实现类的测试函数《CustomerDAOImplTest》:**测试所写的实现类方法是否正确或测试整个DAO代码是否正确

import Class_tools.Customer;
import Class_tools.CustomerDAOImpl;
import org.junit.Test;
import tools.JDBC_utils;

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

public class CustomerDAOImplTest {
    private CustomerDAOImpl cdit=new CustomerDAOImpl();
    @Test
    public void insert()  {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            Customer cust=new Customer(26,"包师语","@shiyu.com",new Date(89889654145L));
            cdit.insert(conn,cust);
            System.out.println("添加成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void deleteById() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            cdit.deleteById(conn,6);
            System.out.println(" 删除成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void update() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            Customer cust=new Customer(26,"包师语","@shiyu.com",new Date(489889654145L));
            cdit.update(conn,cust);
            System.out.println(" 修改成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void getCustomerById() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            Customer cust = cdit.getCustomerById(conn, 21);
            System.out.println(cust);
            System.out.println(" 查找成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void getAll() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            List<Customer> all = cdit.getAll(conn);
            for(int i=0;i<all.size();i++){
                System.out.println(all.get(i));
            }
            System.out.println(" 查看成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void getCount() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            Long count = cdit.getCount(conn);
            System.out.println(count);
            System.out.println(" 统计成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

    @Test
    public void getMaxBirth() {
        Connection conn = null;
        try {
            conn = JDBC_utils.get_Connection();
            Date mb = cdit.getMaxBirth(conn);
            System.out.println(mb);
            System.out.println(" 成功!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
    }

}

DBCP数据库连接池

  • DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
    • Commons-dbcp.jar:连接池的实现
    • Commons-pool.jar:连接池实现的依赖库
  • **Tomcat 的连接池正是采用该连接池来实现的。**该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
  • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
  • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
  • 配置属性说明
属性 默认值 说明
initialSize 0 连接池启动时创建的初始化连接数量
maxActive 8 连接池中可同时连接的最大的连接数
maxIdle 8 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle 0 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait 无限制 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatements false 开启池的Statement是否prepared
maxOpenPreparedStatements 无限制 开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis 连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout 300 超过时间限制,回收没有用(废弃)的连接
removeAbandoned false 超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收
BasicDataSource:通过JavaBeans属性配置的javax.sql.DataSource的基本实现。 这不是结合commons-dbcp和commons-pool软件包的唯一方法,但是为基本需求提供了“一站式”解决方案。
BasicDataSource实例对象.setDriverClassName(String driverClassName);//设置jdbc驱动程序类名称。 
BasicDataSource实例对象.setUrl(String url);//设置库地址
BasicDataSource实例对象.setUsername(String username);//设置用户名
BasicDataSource实例对象.setPassword(String password);//设置用户名密码
  • 获取连接方式一:
public static Connection getConnection3() throws Exception {
	BasicDataSource source = new BasicDataSource();
		
	source.setDriverClassName("com.mysql.jdbc.Driver");
	source.setUrl("jdbc:mysql:///test");
	source.setUsername("root");
	source.setPassword("abc123");
		
	//
	source.setInitialSize(10);
		
	Connection conn = source.getConnection();
	return conn;
}
  • 获取连接方式二:
//使用dbcp数据库连接池的配置文件方式,获取数据库的连接:推荐
private static DataSource source = null;
static{
	try {
		Properties pros = new Properties();
		
		InputStream is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
			
		pros.load(is);
		//根据提供的BasicDataSourceFactory创建对应的DataSource对象
		source = BasicDataSourceFactory.createDataSource(pros);
	} catch (Exception e) {
		e.printStackTrace();
	}
		
}
public static Connection getConnection4() throws Exception {
		
	Connection conn = source.getConnection();
	
	return conn;
}

其中,src下的配置文件为:【dbcp.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&useServerPrepStmts=false
username=root
password=abc123

initialSize=10
#...

德鲁伊(Druid)数据库连接池

Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。

BasicDataSourceFactory.createDataSource(Properties properties);//静态方法;根据给定的属性创建和配置BasicDataSource实例。 返回值为DataSource类型
DataSourceInterface ,一个连接到这个DataSource对象所代表的物理数据源的工厂。DriverManager设备的DriverManagerDataSource对象是获取连接的首选方法。 实现DataSource接口的对象通常将基于Java“命名和目录(JNDI)API”的命名服务注册。
DataSource实例对象.getConnection();//无参
DataSource实例对象.getConnection(String username, String password);//有参
//**/尝试与此 DataSource 对象表示的数据源建立连接。 【通俗讲就是数据库连接池创建数据库连接】返回值类型为 Connection。	

DruidDataSource:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L9yAsv3C-1631929613331)(E:\DataBasesWork\PictureTool\DruidDataSource回顾详解.jpg)]

@Test
public void getConnection3()throws Exception{
    Properties p=new Properties();
    InputStream is=ClassLoader.getSystemResourceAsStream("Druid.properties");
    p.load(is);
    DataSource ds = DruidDataSourceFactory.createDataSource(p);
    Connection conn = ds.getConnection();
    System.out.println(conn);
}

Druid.properties:

#基本属性
url=jdbc:mysql://localhost:6636/test
username=root
password=12345ssdlh
driverClassName=com.mysql.cj.jdbc.Driver
#其他涉及数据库连接池管理的相关属性
initialSize=10#初始连接数
maxActive=10#最大连接数
  • 详细配置参数:
配置 缺省 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter
driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

Dbutils工具类库实现增删改查操作

简介

commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。

增删改

QueryRunner:使用可插拔策略执行SQL查询以处理ResultSet。 此类是线程安全的;该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量
QueryRunner实例对象.update(Connection conn, String sql, Object... params);//用来执行一个更新操作(插入,删除,更改);返回值为更改了几条数据,int类型
->conn为数据库连接,这里可以依靠德鲁伊连接池创建链接
->sql为sql语句,占位符同样为?
->params为填充占位符的参数,个数不确定

样例:插入数据(删改以此类推)

@Test
public void test(){
    //插入数据
    Connection conn = null;
    try {
        QueryRunner qr = new QueryRunner();
        conn = JDBC_utils.get_Connection2();
        String sql="insert into customers(name,email,birth)values (?,?,?)";
        int insertCount = qr.update(conn, sql, "球球", "[email protected]", "1998-2-5");
        System.out.println("添加了"+insertCount+"条数据");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBC_utils.close_resources(conn,null);
    }
}

ResultSetHandler:该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。
QueryRunner实例对象.query(Connection conn, String sql,xxx, Object... params);//查询具体的数据,返回类型为ResultSetHandler的具体实现类的具体参数类T
->conn为数据库连接,这里可以依靠德鲁伊连接池创建链接
->sql为sql语句,占位符同样为?
->xxx:ResultSetHandler的具体实现类
->params为填充占位符的参数,个数不确定
ResultSetHandler接口的主要实现类:

- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。封装表中的一条数据
Class BeanHandler
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- ScalarHandler:查询单个值对象

样例:用BeanHander查询数据

@Test
public void test1(){
    //查询一条
    /*
    BeanHander:是ReasultSetHander接口的实现类,用于封装表中的一条记录
     */
    Connection conn = null;
    try {
        System.out.println("返回一条记录");
        QueryRunner qr=new QueryRunner();
        conn = JDBC_utils.get_Connection2();
        String sql="select id,name,email,birth from customers where  id=?;";
        BeanHandler<Customer> hander=new BeanHandler<>(Customer.class);
        Customer cust = qr.query(conn, sql, hander, 21);
        System.out.println(cust.toString());

    /*
    查询多条记录
    BeanListHander:ResultSetHander接口的实现类
     */
        System.out.println("返回多条记录");
        String sql1="select id,name,email,birth from customers where  id>?;";
        BeanListHandler<Customer> blh=new BeanListHandler<>(Customer.class);
        List<Customer> custs = qr.query(conn, sql1, blh, 21);
        for(Customer c:custs){
            System.out.println(c);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBC_utils.close_resources(conn,null);
    }
}

样例:用MapHander查询数据

@Test
public void test2(){
    //查询一条
    /*
    MapHander:是ResultSetHander接口的实现类,对应表中的一条记录
    将字段及相应字段的值作为map中的key和value
     */
    Connection conn = null;
    try {
        System.out.println("返回一条记录");
        QueryRunner qr=new QueryRunner();
        conn = JDBC_utils.get_Connection2();
        String sql="select id,name,email,birth from customers where  id=?;";
        MapHandler handler=new MapHandler();
        Map<String, Object> map = qr.query(conn, sql, handler, 22);
        System.out.println(map);
    /*
    返回多条记录
    MapListHander:ResultSetHander接口的实现类
     */
        System.out.println("返回多条记录");
        String sql1="select id,name,email,birth from customers where  id>?;";
        MapListHandler mlh=new MapListHandler();
        List<Map<String, Object>> list = qr.query(conn, sql1, mlh, 21);
        list.forEach(System.out::println);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        JDBC_utils.close_resources(conn,null);
    }
}

查询特殊字段

ScalarHandler:查询单个值对象(单个值)
QueryRunner实例对象.query(连接名,sql语句,ScalarHandler实例对象,[参数]);				//查询特殊字段
@Test
    public void test3(){
        Connection conn = null;
        try {
            QueryRunner qr=new QueryRunner();
            ScalarHandler sh = new ScalarHandler();
            conn = JDBC_utils.get_Connection2();
            String sql="select max(birth) from customers";
            //返回最大生日
            String sql1="select count(*) from customers";
            //返回总条数

            Object value = qr.query(conn, sql, sh);
            Object value1 = qr.query(conn, sql1, sh);
            System.out.println(value);
            System.out.println(value1);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBC_utils.close_resources(conn,null);
        }
     }

自定义ResultSetHander实现类

(自己写一个)

匿名实现类:与匿名内部类相似

例子(用到了匿名实现类):

@Test
public void test5()throws Exception{
    /*
    自定义ResultSetHander实现类(自己写个)
     */
    Connection conn = JDBC_utils.get_Connection2();
    QueryRunner qr=new QueryRunner();
    String sql="select id,name,birth,email from customers where id=?";
    //匿名实现类
    ResultSetHandler<Customer>  rshc=new ResultSetHandler<Customer>() {
        @Override
        public Customer handle(ResultSet rs) throws SQLException {
            if(rs.next()){
                Customer c=new Customer();
                ResultSetMetaData rsmd = rs.getMetaData();
                for(int i=0;i<rsmd.getColumnCount();i++){
                    Object value = rs.getObject(i + 1);
                    String columnLabel = rsmd.getColumnLabel(i+1);
                    try {
                        Field filed = Customer.class.getDeclaredField(columnLabel);
                        filed.setAccessible(true);
                        filed.set(c,value);
                    } catch (Exception e) {
                        e.printStackTrace();
                        return null;
                    }
                }
                return c;
            }
            else{
                return null;
            }
        }
    };
    Customer query = qr.query(conn, sql, rshc, 23);
    System.out.println(query);
}

匿名实现类

静态代码块

你可能感兴趣的:(JDBC,mysql,数据库,java)