利用动态代理去实现JDBC增删改查

什么叫做动态代理:就是不用我们手动去编写代理类了,系统会自动帮你生成,生成的类是在内存里,不占用硬盘空间,也不会像静态代理一样添加一个方法就需要改一遍代理类。动态代理就是一劳永逸。

废话不多说直接看代码

首先需要准备一个.properties配置文件

//此文件为配置文件以key-value键值对的方式出现
//加载mysql驱动
jdbc.driver=com.mysql.jdbc.Driver
//数据库链接地址
jdbc.connection.url=jdbc:mysql://localhost:3306/lx1
//数据库用户名
jdbc.connection.username=root
//数据库密码
jdbc.connection.password=

如图所示的文件:

 接下来就开始编写我们的接口类了

package cn.dao;

import java.sql.ResultSet;
//接口类
public interface JDBC_operation {
    void getConnection();//获得连接的方法
    int executeUpdate(String sql,Object...obj);//增删改  里面需要传入一个sql语句还有一个不定长参数(是为sql语句里面?准备的)
    ResultSet executeQuery(String sql,Object...obj);//查询   里面需要传入一个sql语句还有一个不定长参数(是为sql语句里面?准备的)
    void closeAll();//关闭连接资源
}

 接下来编写实现类

package cn.dao;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBC_impl implements JDBC_operation{
    private static String drivate;
    private static String url;
    private static String username;
    private static String password;
    private Connection connection;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;
   //静态代码块获得我们连接数据库所需要的信息
    static {
        Properties properties=new Properties();//这个方法可以读取配置文件.properties
        InputStream in = JDBC_impl.class.getClassLoader().getResourceAsStream("database.properties");
        try {
            properties.load(in);//load方法可以读取文件
            //获得配置文件里面的对应内容
            drivate=properties.getProperty("jdbc.driver");
            url=properties.getProperty("jdbc.connection.url");
            username=properties.getProperty("jdbc.connection.username");
            password=properties.getProperty("jdbc.connection.password");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   //连接
    @Override
    public void getConnection(){
        try {
            //加载驱动
            Class.forName(drivate);
            //获得数据库的连接
            connection = DriverManager.getConnection(url, username, password);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
   //增删改
    @Override
    public int executeUpdate(String sql,Object...obj) {
        try {
            //用PreparedStatement对象去执行sql代码
            preparedStatement = connection.prepareStatement(sql);
            //不定长参数产生的是一个数组所以我们要用for循环去遍历它的长度(不定长参数可以为空)
            for (int i=0;i<obj.length;i++){
                //给执行的sql语句里面的?赋值
                preparedStatement.setObject(i+1,obj[i]);
            }
            //执行增删改的方法
            int i = preparedStatement.executeUpdate();
            //返回行数
            return i;
        } catch (SQLException e) {
            e.printStackTrace();
            //捕获到异常则返回-1
            return -1;
        }
    }
    //查询
    @Override
    public ResultSet executeQuery(String sql,Object...obj) {
        try {
            //用PreparedStatement对象去执行sql代码
            preparedStatement = connection.prepareStatement(sql);
            //不定长参数产生的是一个数组所以我们要用for循环去遍历它的长度(不定长参数可以为空)
            for (int i=0;i<obj.length;i++){
                //给执行的sql语句里面的?赋值
                preparedStatement.setObject(i+1,obj[i]);
            }
            //执行查询的方法
            resultSet = preparedStatement.executeQuery();
            //返回结果集
            return resultSet;
        } catch (SQLException e) {
            e.printStackTrace();
            //捕获到异常则返回-1
            return null;
        }
    }
    //关闭
    @Override
    public void closeAll() {
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(preparedStatement!=null){
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

好了,接下来就是我们的动态代理了,为什么在这里需要用到动态代理呢是因为JDBC操作的时候总是要连接数据库和释放资源,如果用动态代理就会省略这两步,让系统自动帮我们实现,这样省去了很多的重复操作。

package cn.dao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//创建动态代理需要实现InvocationHandler接口    面向接口实现的动态代理
public class Agent implements InvocationHandler {
    private JDBC_operation jdbc;

    public Agent(JDBC_operation jdbc) {
        this.jdbc = jdbc;
    }
    public Agent() {
    }
    @Override
    //第一个参数proxy是真实对象的真实代理对象,invoke方法可以返回调用代理对象方法的返回结果,也可以返回对象的真实代理对象(可以使用反射获取代理对象的信息)
    //第二个则是需要代理的接口
    //第三个则是参数
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long l = System.currentTimeMillis();
        jdbc.getConnection();//代理实现的方法
        System.out.println("已连接");
        Object o = method.invoke(jdbc, args);//需要传递要代理的接口以及参数
        jdbc.closeAll();//代理实现的方法
        System.out.println("已关闭");
        long l1 = System.currentTimeMillis();
        System.out.println("总用毫秒数:"+(l1-l)+"ms");
        return o;
    }

}

接下来我们就去测试一下

package cn.dao;

import java.lang.reflect.Proxy;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Test {
    public static void main(String[] args) throws SQLException {
        JDBC_operation jdbc=new JDBC_impl();//实例化自己定义的方法实现类对象
        //第一个参数: 用哪个类加载器去加载代理对象
        //第二个参数:动态代理类需要实现的接口
        //第三个参数:动态代理方法在执行时,会调用InvocationHandler里面的invoke方法去执行
       JDBC_operation o = (JDBC_operation)Proxy.newProxyInstance(jdbc.getClass().getClassLoader(), new Class[]{JDBC_operation.class}, new Agent(jdbc));
        String sql="insert into student(stuname) values('哈哈')";
        int i = o.executeUpdate(sql);
        if (i>0){
            System.out.println("插入数据成功");
        }else {
            System.out.println("插入数据失败");
        }
    }
}

结果如下:

这里需要注意一下,查询的话需要把动态代理的关闭资源方法去掉,需要我们调用之后自己手动关闭,因为不关掉就会出现如下错误:

这个的意思是:线程“main”java中的异常。sql。SQLException:在com处ResultSet关闭后,不允许操作。mysgl。jdbc。SQLError。原因是数据库不允许这样操作,所以我们直接用对象调用关闭的方法就好了

增删改不会出现这种错误

努力到无能为力,拼搏到感动自己
原文地址:https://www.cnblogs.com/tkzm/p/11177355.html