热门标签 | HotTags
当前位置:  开发笔记 > 数据库 > 正文

编写自己的JDBC框架-mysql教程

编写自己的JDBC框架
一、数据库连接池:

  在一般用JDBC 进行连接数据库进行CRUD操作时,每一次都会:

    通过:java.sql.Connection cOnn= DriverManager.getConnection(url,user,password); 重新获取一个数据库的链接再进行操作,这样用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。

     5、要遵循先入先出的原则。

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Properties;


/**
 * 一个基本的数据连接池:  
 * 1、初始化时就建立一个容器,来存储一定数量的Connection 对象
 * 2、用户通过调用MyDataSource 的getConnection 来获取Connection 对象。
 * 3、再通过release 方法来回收Connection 对象,而不是直接关闭连接。
 * 4、遵守先进先出的原则。
 *  
 *     
 * @author 贺佐安
 *
 */
public class MyDataSource {
    private static String url = null;
    private static String password = null;
    private static String user = null ;
    private static String DriverClass = null;
    private static LinkedList pool = new LinkedList() ;
//    注册数据库驱动
    static {
        try {
            InputStream in = MyDataSource.class.getClassLoader()
                    .getResourceAsStream("db.properties");
            Properties prop = new Properties(); 
            prop.load(in);
            user = prop.getProperty("user"); 
            url = prop.getProperty("url") ;
            password = prop.getProperty("password") ; 
            DriverClass = prop.getProperty("DriverClass") ;  
            Class.forName(DriverClass) ;  
            
        } catch (Exception e) {
            throw new RuntimeException(e) ;
        }  
    }
    //初始化建立数据连接池
    public MyDataSource ()  {
        for(int i = 0 ; i <10 ; i ++) {
            try {
                Connection cOnn= DriverManager.getConnection(url, user, password) ;
                pool.add(conn) ;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //、从连接池获取连接
    public Connection getConnection() throws SQLException {
        return pool.remove() ;
    } 
    // 回收连接对象。
    public void release(Connection conn) {
        System.out.println(conn+"被回收");
        pool.addLast(conn) ;
    } 
    public int getLength() {
        return pool.size() ;
    }
}

这样当我们要使用Connection 连接数据库时,则可以直接使用连接池中Connection 的对象。测试如下:

import java.sql.Connection;
import java.sql.SQLException;

import org.junit.Test;


public class MyDataSourceTest {
    
    
    /**
     * 获取数据库连接池中的所有连接。
     */
    @Test
    public void Test() {
        MyDataSource mds = new MyDataSource() ; 
        Connection cOnn= null ;
        try {
            
            for (int i = 0 ; i <20 ; i ++) {
                cOnn= mds.getConnection() ;
                System.out.println(conn+"被获取;连接池还有:"+mds.getLength()); 
                mds.release(conn) ;
            } 
        } catch (SQLException e) {
            e.printStackTrace();
        } 
    }
}

再运行的时候,可以发现,循环10次后,又再一次获取到了第一次循环的得到的Connection对象。所以,这样可以大大的减轻数据库的压力。上面只是一个简单的数据库连接池,不完美的便是,回收需要调用数据池的release() 方法来进行回收,那么可以不可以直接调用Connection 实例的close 便完成Connection 对象的回收呢?


二、数据源: 

    > 编写连接池需实现javax.sql.DataSource接口。
   > 实现DataSource接口,并实现连接池功能的步骤:
    1、在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。

      2、实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。

    利用动态代理和包装设计模式来标准的数据源。

    1、包装设计模式实现标准数据源:

      这里的用包装设计模式,便是将Connection 接口进行包装。简单总结一下包装设计模式的步骤:

          a)定义一个类,实现与被包装类()相同的接口。
             |----可以先自己写一个适配器,然后后面继承这个适配器,改写需要改写的方法,提高编程效率。
         b)定义一个实例变量,记住被包装类的对象的引用。
         c)定义构造方法,转入被包装类的对象。

          e)对需要改写的方法,改写。
    f)对不需要改写的方法,调用原来被包装类的对应方法。

      所以先编写一个类似适配器的类,将Connection 接口的方法都进行实现:

View Code

      然后再对Connection 接口进行包装,将close 方法修改掉:

import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
/**
 * 对MyConnectionAdapter 进行包装处理 
 * @author 贺佐安
 *
 */
public class MyConnectionWrap extends MyConnectionAdapter {

    private LinkedList pool = new LinkedList() ;
    public MyConnectionWrap(Connection conn ,LinkedList pool ) {
        super(conn); 
        this.pool = pool ; 
    }
    
    //改写要实现的方法
    public void close() throws SQLException {
        pool.addLast(conn) ;
    }
}

编写标准数据源:

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.ResourceBundle;

import javax.sql.DataSource;


/** 
 * 编写标准的数据源:
 * 1、实现DataSource 接口
 * 2、获取在实现类的构造方法中批量获取Connection 对象,并将这些Connection 存储
 * 在LinkedList 容器中。
 * 3、实现getConnection() 方法,调用时返回LinkedList容器的Connection对象给用户。
 * @author 贺佐安
 *
 */
public class MyDataSource implements DataSource{
    private static String url = null;
    private static String password = null;
    private static String user = null ;
    private static String DriverClass = null;
    private static LinkedList pool = new LinkedList() ;

    //    注册数据库驱动
    static {
        try {  
            ResourceBundle rb = ResourceBundle.getBundle("db") ;
            url = rb.getString("url") ; 
            password = rb.getString("password") ; 
            user = rb.getString("user") ; 
            DriverClass = rb.getString("DriverClass") ;
            Class.forName(DriverClass) ;  
            
            //初始化建立数据连接池
            for(int i = 0 ; i <10 ; i ++) {
                Connection cOnn= DriverManager.getConnection(url, user, password) ;
                pool.add(conn) ;
            }
        } catch (Exception e) {
            throw new RuntimeException(e) ;
        } 
        
    }
    public MyDataSource ()  {  
    }
    
    //、从连接池获取连接:通过包装模式
    public synchronized Connection getConnection() throws SQLException {
        if (pool.size() > 0) {
            MyConnectionWrap mcw = new MyConnectionWrap(pool.remove(), pool) ;
            return mcw ;
        }else {
            throw new RuntimeException("服务器繁忙!"); 
        }
    }
    
    // 回收连接对象。
    public void release(Connection conn) {
        System.out.println(conn+"被回收");
        pool.addLast(conn) ;
    }
    
    public int getLength() {
        return pool.size() ;
    }
    
    
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        
    }
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        
    }
    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }
    @Override
    public  T unwrap(Class iface) throws SQLException {
        return null;
    }
    @Override
    public boolean isWrapperFor(Class iface) throws SQLException {
        return false;
    }
    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        return null;
    }
    
}

 2、动态代理实现标准数据源:

    相对于用包装设计来完成标准数据源,用动态代理则方便许多:

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.ResourceBundle;

import javax.sql.DataSource;


/** 
 * 编写标准的数据源:
 * 1、实现DataSource 接口
 * 2、获取在实现类的构造方法中批量获取Connection 对象,并将这些Connection 存储
 * 在LinkedList 容器中。
 * 3、实现getConnection() 方法,调用时返回LinkedList容器的Connection对象给用户。
 * @author 贺佐安
 *
 */
public class MyDataSource implements DataSource{
    private static String url = null;
    private static String password = null;
    private static String user = null ;
    private static String DriverClass = null;
    private static LinkedList pool = new LinkedList() ;

    //    注册数据库驱动
    static {
        try {  
            ResourceBundle rb = ResourceBundle.getBundle("db") ;
            url = rb.getString("url") ; 
            password = rb.getString("password") ; 
            user = rb.getString("user") ; 
            DriverClass = rb.getString("DriverClass") ;
            Class.forName(DriverClass) ;  
            
            //初始化建立数据连接池
            for(int i = 0 ; i <10 ; i ++) {
                Connection cOnn= DriverManager.getConnection(url, user, password) ;
                pool.add(conn) ;
            }
        } catch (Exception e) {
            throw new RuntimeException(e) ;
        }  
    }
    public MyDataSource ()  { 
        
    }
    
    //、从连接池获取连接:通过动态代理
    public Connection getConnection() throws SQLException {
        if (pool.size() > 0) {
            final Connection cOnn= pool.remove() ; 
            Connection proxyCon = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), 
                    new InvocationHandler() {
                        //策略设计模式:
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            if("close".equals(method.getName())){
                                //谁调用,
                                return pool.add(conn);//当调用close方法时,拦截了,把链接放回池中了
                            }else{
                                return method.invoke(conn, args);
                            } 
                        }
                    });
          return proxyCon ;
        }else {
            throw new RuntimeException("服务器繁忙!"); 
        }
    } 
    
    public int getLength() {
        return pool.size() ;
    }
    
    
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }
    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        
    }
    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        
    }
    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }
    @Override
    public  T unwrap(Class iface) throws SQLException {
        return null;
    }
    @Override
    public boolean isWrapperFor(Class iface) throws SQLException {
        return false;
    }
    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        return null;
    } 
}


当然觉得麻烦的则可以直接使用一些开源的数据源如:DBCP、C3P0等。DBCP的原理是用包装设计模式开发的数据源,而C3P0则是动态代理的。

    1、DBCP的使用:

import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;

/**
 * 创建DBCP 工具类
 * @author 贺佐安
 *
 */
public class DbcpUtil {
    private static DataSource ds = null ;
    static {
        try {
            //读取配置文件
            InputStream in = DbcpUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties") ;
            Properties prop = new Properties() ; 
            prop.load(in) ;
            
            //通过BasicDataSourceFactory 的creatDataSurce 方法创建 BasicDataSource 对象。
            ds = BasicDataSourceFactory.createDataSource(prop) ;
            
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
    public static DataSource getDs() {
        return ds ; 
    }
    public static Connection getConnection () {
        try { 
            return ds.getConnection() ;
        } catch (SQLException e) {
            throw new RuntimeException() ;
        } 
    }
}


2、C3P0 的使用:

import java.sql.Connection;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
 * C3P0 开源数据源的使用
 * @author 贺佐安
 *
 */
public class C3p0Util {
    private static ComboPooledDataSource cpds  = null ;
    static {
        
        cpds = new ComboPooledDataSource() ; 
    }
    public static Connection getConnection() {
        try {
            return cpds.getConnection() ;
        } catch (SQLException e) {
            throw new RuntimeException() ;
        }
    }
}

使用这两个数据源时,直接调用获取到的Connection 连接的close 方法,也是将连接放到pool中去。

    

三、元数据(DatabaseMetaData)信息的获取

  > 元数据:数据库、表、列的定义信息。

  > 元数据信息的获取:为了编写JDBC框架使用。

      1、数据库本身信息的获取:java.sql.DataBaseMateData java.sql.Connection.getMetaData() ;

      DataBaseMateData 实现类的常用方法:

        getURL():返回一个String类对象,代表数据库的URL。

        getUserName():返回连接当前数据库管理系统的用户名。

        getDatabaseProductName():返回数据库的产品名称。

        getDatabaseProductVersion():返回数据库的版本号。

        getDriverName():返回驱动驱动程序的名称。

        getDriverVersion():返回驱动程序的版本号。

        isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。

      2、ParameterMetaData: 代表PerparedStatment 中的SQL 参数元数据信息: java.sql.ParameterMetaData java.sql.PerparedStatement.getParameterMetaData() ;       

      ParameterMetaData 实现类常用方法:

        getParameterCount() :获得指定参数的个数

        getParameterType(int param) :获得指定参数的sql类型(驱动可能不支持)

      3、ResultSetMetaData : 代表结果集的源数据信息:相当于SQL 中的 :DESC java.sql.ResultSetMetaData java.sql.ResultSet.getMetaData() ;         

      java.sql.ResultSetMetaData 接口中常用的方法:

        a) getColumnCount() : 获取查询方法有几列。

        b) getColumnName(int index) : 获取列名:index从1开始。

        c) getColumnType(int index) : 获取列的数据类型。返回的是TYPES 中的常量值。

四、编写自己的JDBC框架:

    JDBC框架的基本组成:  

    1、核心类:

      a、定义一个指定javax.sql.DataSource 实例的引用变量,通过构造函数获取指定的实例并给定义的变量。
      b、编写SQL运行框架。

         DML 语句的编写:
          1、通过获取的javax.sql.DataSource 实例,获取Connection 对象。
           2、通过ParamenterMeteData 获取数据库元数据。

        DQL 语句的编写:
          1、通过获取的DataSource 实例,获取Connection 对象。
          2、通过ParamenterMeteData、ResultSetMetaData 等获取数据库元数据。
          3、用抽象策略设计模式:设计一个ResultSetHandler 接口,作用:将查找出的数据封装到指定的JavaBean中。
                |————这里的JavaBean,由用户来指定。
                抽象策略模式,用户可以更具具体的功能来扩展成具体策略设计模式。如:查找的一条信息、查找的所有信息。

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

/**
 * 实现JDBC 框架的核心类。
 * 在该类中定义了SQL语句完成的方法;
 * @author 贺佐安
 *
 */
public class  MyJdbcFrame {
    /**
     * javax.sql.DataSource 实例的引用变量
     */
    private DataSource ds = null ;
    /**
     * 将用户指定的DataSource 指定给系统定义的DataSource 实例的引用变量
     * @param ds
     */
    public MyJdbcFrame(DataSource ds ) {
        this.ds = ds ; 
    }
    /**
     * 执行UPDATE、DELETE、INSERT 语句。
     * @param sql 
     * @param obj
     */
    public void update(String sql , Object[] obj) {
        Connection cOnn= null ; 
        PreparedStatement stmt = null ; 
        try {
            //获取Connection 对象
            cOnn= ds.getConnection() ;
            stmt = conn.prepareStatement(sql) ; 
            
            // 获取ParameterMetaData 元数据对象。
            ParameterMetaData pmd = stmt.getParameterMetaData() ;
            
            //获取SQL语句中需要设置的参数的个数
            int parameterCount = pmd.getParameterCount() ;
            if (parameterCount > 0) { 
                if (obj == null || obj.length != parameterCount) {
                    throw new MyJdbcFrameException( "parameterCount is error!") ;
                } 
                //设置参数:
                for ( int i = 0 ; i 

2、接口:策略模式的接口:ResultSetHandler 。

 import java.sql.ResultSet;
//抽象策略模式
public interface ResultSetHandler {
     public Object handler(ResultSet rs) ;
     }

这里对ResultSetHandler 接口实现一个BeanHandler 实例 :

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

/**
 * 该类获取ResultSet 结果集中的第一个值,封装到JavaBean中
 * @author 贺佐安
 *
 */
public class BeanHandler implements ResultSetHandler { 
    //获取要封装的JavaBean的字节码
    private Class clazz ;
    public BeanHandler (Class clazz) {
        this.clazz = clazz ;
    }

    public Object handler(ResultSet rs) {
        try {
            if (rs.next()) {
                //1、获取结果集的元数据。
                ResultSetMetaData rsm = rs.getMetaData() ;
                //2、创建JavaBean的实例:
                Object obj = clazz.newInstance() ;
                //3、将数据封装到JavaBean中。  
                for (int i = 0 ; i 

3、自定义异常类:继承RuntimeException。如:

public class MyJdbcFrameException extends RuntimeException {
    public MyJdbcFrameException() {
        super() ; 
    }
    public MyJdbcFrameException(String e) {
        super(e) ;
    }
}

然后就可以将其打包发布,在以后写数据库操作时就可以用自己的JDBC框架了,如果要完成查询多条语句什么的,则要实现ResultSetHandler 接口。来完成更多的功能。

  当然,使用DBUtils 则更简单:Apache 组织提供的一个开源JDBC 工具类库。

推荐阅读
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
author-avatar
Graceedelweiss_602
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有