随记2 JDBC与数据库连接池

一、原始的JDBC

JDBC全称为Java Data Base Connectivity(java数据库连接),定义了一套Java操作数据库的规范(接口),统一对数据库的操作。这套接口由数据库厂商去实现,开发人员只需要通过JDBC接口操作不同的数据库,屏蔽了不同数据库之间的语法区别。

1.JDBC简单实现过程

1.加载数据库驱动
2.通过驱动获取connect数据库连接
3.通过数据库连接获取可执行SQL的Statement
4.通过Statement对象执行SQL
5.关闭链接,释放资源

 public static void main(String[] args) throws Exception {
        
        String url = "jdbc:mysql://localhost:3306/test";//连接的数据库URL
        String username = "root";//连接的数据库用户名
        String password = "123456"; //连接的数据库密码
        
        //1.加载数据库驱动(要引入驱动的jar包,例如:mysql-connector-java-3.1.10-bin.jar)
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());不推荐使用这种方式来加载驱动
        Class.forName("com.mysql.jdbc.Driver");//推荐使用这种方式来加载驱动
        //2.通过驱动获取connect数据库连接
        Connection conn = DriverManager.getConnection(url, username, password);
        //3.通过数据库连接获取可执行SQL的Statement
        Statement st = conn.createStatement();
        String sql = "select id,name,password,email,birthday from users";
        //4.通过Statement对象执行SQL
        ResultSet rs = st.executeQuery(sql);
        ......
        //5.关闭链接,释放资源
		rs.close();
		st.close();
        conn.close();
}

2.JDBC在工程中的实现

在工程中,为了程序的可复用性、可移植性等原因,一般要做一些配置和封装

1.首先数据库参数配置一般不直接写在代码里,通常以配置文件的形式出现(如db.properties)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy
username=root
password=XDP

2.一般将获取连接和释放连接封装为JDBCUtils.java工具类

public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    
    static{
        try{
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties prop = new Properties();
            prop.load(in);
            //获取数据库连接配置
            driver = prop.getProperty("driver");
            url = prop.getProperty("url");
            username = prop.getProperty("username");
            password = prop.getProperty("password");
            //加载数据库驱动
            Class.forName(driver);
        }catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    
    // 获取数据库连接对象
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(url, username,password);
    }
    
    //释放资源
    public static void release(Connection conn,Statement st,ResultSet rs){
        if(rs!=null){
            try{
                rs.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if(st!=null){
            try{
                st.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        } 
        if(conn!=null){
            try{
                conn.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

3.PreparedStatement对象

PreperedStatement和Statement的比较:
1.PreperedStatement是Statement的子类,也可以通过connect获得,也可以执行SQL
2.PreperedStatement对SQL进行预编译,从而提高数据库的执行效率;而Statement会使数据库频繁编译SQL
3.PreperedStatement对sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写;而Statement需要复杂地拼接SQL字符串
4.PreperedStatement相比于Statement可以避免SQL注入的问题,典型的SQL注入问题随意登录

经典的PreparedStatement用法

conn = JdbcUtils.getConnection();
//要执行的SQL命令,SQL中的参数使用?作为占位符
String sql = "insert into users(id,name) values(?,?)";
//通过conn对象获取负责执行SQL命令的prepareStatement对象
st = conn.prepareStatement(sql);
//为SQL语句中的参数赋值,注意索引是从1开始的
st.setInt(1, 1);
st.setString(2, "CSDN");
//执行操作
......

注意此处的占位符索引,在Java中从1开始,在C#中从0开始
原始的JDBC是学校课堂上的基础知识,也是市场框架或者主流开发技术的基石

二、数据库连接池

应用程序每次做数据库操作都需要先获得连接,再释放。数据库创建连接通常需要消耗较大的系统资源,程序耗时也较长。并发访问量较大时,会创建较多的资源,可能会导致数据库服务器内存溢出甚至宕机。

对于共享资源,有一个很著名的设计模式:资源池(resource pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。

1.简单的数据库连接池实现

编写连接池需实现DataSource接口,在类构造函数中批量创建数据库的连接,并把连接加入LinkedList对象中。实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。当用户使用完Connection,调用Connection.close()方法时,Collection对象返回到LinkedList中

public class MyDataSource implements DataSource {
	//链表
    privateLinkedList<Connection> dataSources = new LinkedList<Connection>();

    //初始化连接数量
    publicMyDataSource() {
        for(int i = 0; i < jdbcPoolSize; i++) {
			try {
				Class.forName("com.mysql.jdbc.Driver");
                Connection con =DriverManager.getConnection(url, username, password);
                dataSources.add(con);
            } catch (Exception e) {
				e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
		final Connection conn = dataSources.removeFirst(); // 取出连接池中一个连接,删除第一个连接返回
		return conn;
    }

    public void releaseConnection(Connection conn) {
		dataSources.add(conn);//将连接放回连接池
    }
}

而一个高可用高性能的数据库连接池远没有这么简单,实际上连接池中连接的归还是由动态代理实现,除此以外还要考虑更多的问题:

  1. 并发问题
  2. 多数据库服务器和多用户
  3. 事务处理
  4. 连接池的分配与释放
  5. 连接池的配置与维护

可以参考这里查看这篇博文查看更详细的解释

2.常见的开源数据库连接池

1、DBCP数据库连接池

DBCP是Apache下开源的数据库连接池组件,Tomcat的连接池正是采用该连接池来实现的

2、C3P0数据库连接池

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定。C3P0数据源在项目开发中使用得比较多,是Hibernate内置的数据库连接池,使用C3P0的还有Spring。本人毕设时在Tomcat环境JSP+Servlet+JavaBean模式下就是用的C3P0连接池。C3P0有多种配置方式:JavaAPI、文件配置等

需要引入相关的jar包,c3p0-0.9.2-pre1.jar和mchange-commons-0.2.jar

  1. Java API方式配置
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.jdbc.Driver" ); //loads the jdbc driver            
cpds.setJdbcUrl("jdbc:mysql://host:port/db");
cpds.setUser("username");                                  
cpds.setPassword("password");                                  
    
cpds.setMinPoolSize(5);                                     
cpds.setMaxPoolSize(20);
cpds.setAcquireIncrement(5);

Connection conn = cpds.getConnection();// 直接从连接池中获取连接
query(conn);
cpds.close();
  1. properties文件配置

需要在classpath路径下添加配置文件:c3p0.properties。创建ComboPooledDataSource对象时,C3P0自动从classpath加载c3p0.properties中的配置信息。

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://host:port/db
c3p0.user=root
c3p0.password=
c3p0.minPoolSize=5
c3p0.maxPoolSize=20
c3p0.acquireIncrement=5

使用c3p0.properties作为配置文件时,每个参数的name前缀必须是“c3p0”

  1. c3p0-config.xml文件配置

c3p0-config.xml配置文件比properties更高级,可配置多数据源。创建ComboPooledDataSource对象时,C3P0自动从classpath加载c3p0.properties中的配置信息

文件名必须为c3p0-config.xml,常用的设置参数以及作用如下

<c3p0-config>
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://host:port/db</property>
        <property name="user">username</property>
        <property name="password">password</property>
        <!-- 连接池初始化时创建的连接数 -->
        <property name="initialPoolSize">3</property>
        <!-- 连接池保持的最小连接数 -->
        <property name="minPoolSize">3</property>
        <!-- 连接池中拥有的最大连接数,连接总数超过这个值则等待其他连接释放 -->
        <property name="maxPoolSize">15</property>
        <!-- 连接池在无空闲连接可用时一次性创建的新数据库连接数 -->
        <property name="acquireIncrement">3</property>
        <!-- 连接的最大空闲时间 0则永远不会断开 单位:-->
        <property name="maxIdleTime">0</property>
        <!-- 用来配置测试空闲连接的间隔时间。可以用来解决MySQL 8小时断开连接的问题。因为它保证连接池会每隔一定时间对空闲连接进行一次测试,从而保证有效的空闲连接能每隔一定时间访问一次数据库,将MySQL8小时无会话的状态打破。为0则不测试。默认值:0,单位:-->
        <property name="idleConnectionTestPeriod">30</property>
    </default-config>
    
	<!-- 定义带名称的数据源 -->
	<named-config name="myDataSource">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test_jdbc</property>
		<property name="user">root</property>
		<property name="password"></property>
		<property name="minPoolSize">5</property>
		<property name="maxPoolSize">20</property>
		<property name="acquireIncrement">5</property>
	</named-config>
</c3p0-config>

3、Druid数据库连接池

Druid是阿里巴巴国产连接池,高效、功能强大、可扩展性好,可替换替换DBCP和C3P0。通常在Spring项目中配置使用
Druid包括三个部分:基于Filter-Chain模式的插件体系、DruidDataSource数据库连接池、SQLParser

  • 监控数据库访问性能
    Druid内置提供了一个StatFilter插件,能够详细统计SQL的执行性能
  • 数据库密码加密
  • SQL执行日志
    Druid提供了不同的LogFilter,监控应用的数据库访问情况。

有时间需要研究Druid详细的使用方法,目前市场主流应用连接池。

4、weblogic商用数据库连接池

虽然Druid连接池在数据库监控,在性能上都比较高效,但商用的weblogic连接池更稳定高效,而且有更多更复杂的功能实现。值得一说的是Test Reserved Connections:对取得的连接进行测试的功能。在项目开发中可能会有java报连接失效的异常,或者获取到失效连接导致SQL执行有问题再重新获取连接的情况,这都会影响应用使用性能。

三、配置Tomcat数据源

如果我们希望Tomcat服务器在启动的时候可以创建一个数据库连接池,那么我们在应用程序中就不需要手动创建,直接使用即可。

1、JNDI技术

JNDI(Java Naming and Directory Interface),Java命名和目录接口。这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

Tomcat服务器创建好数据源之后是以JNDI的形式绑定到JNDI容器中的。
日常javaEE开发,服务器会创建很多资源,比如request对象,response对象,这些资源有两种方式使用:一是通过方法参数的形式传递进来,比如Servlet中使用到的request对象和response对象。二是JNDI的方式,直接从JNDI容器中获取相应的资源即可

对JNDI容器很感兴趣,估计会和spring容器有相似之处,可能也会对了解Tomcat底层原理有帮助

2、配置Tomcat数据源步骤

1、在Web项目的WebRoot目录下的META-INF目录下创建一个context.xml文件如下

<Context>
   <Resource 
       name="jdbc/datasource" 
       auth="Container"
       type="javax.sql.DataSource" 
       username="root" 
       password="XDP"
       driverClassName="com.mysql.jdbc.Driver" 
       url="jdbc:mysql://localhost:3306/jdbcstudy"
       maxActive="8" 
       maxIdle="4"/>
</Context>

2、在tomcat的lib目录下放置数据库的驱动jar包

3、在获取数据库连接的工具类中获取JNDI容器中的数据源

private static DataSource ds = null;
//在静态代码块中创建数据库连接池
static{
	try{
		//初始化JNDI
		Context initCtx = new InitialContext();
		//得到JNDI容器
		Context envCtx = (Context) initCtx.lookup("java:comp/env");
		//从JNDI容器中检索name为jdbc/datasource的数据源
		ds = (DataSource)envCtx.lookup("jdbc/datasource");
	}catch (Exception e) {
		throw new ExceptionInInitializerError(e);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值