一、介绍
JNDI数据源配置在JNDI-Resources-HOWTO中得到广泛的覆盖。但是,反馈表明,单个配置的具体细节可能相当棘手。
这里是一些用于流行数据库的示例配置,以及一些关于db使用的一般提示。
注意,JNDI资源配置在Tomcat 7.x和Tomcat 8.x之间有所改变,因为它们使用不同版本的Apache Commons DBCP库。您很可能需要修改旧的JNDI资源配置,以匹配下面示例中的语法,以使其在Tomcat 9中工作。有关详细信息,请参阅“Tomcat迁移指南”。
二、DriverManager,服务提供者机制和内存泄漏
java.sql.DriverManager支持服务提供机制。此功能是通过来自META-INF/services/java.sql.Driver文件来发布所有可用JDBC驱动程序,它们自动发现,加载和注册,从而避免在创建数据库驱动程序之前明确加载数据库驱动程序。 但是,在servlet容器环境的所有Java版本中,其实现都是破坏的。原因是java.sql.DriverManager只会扫描一次驱动程序。
Apache Tomcat附带的JRE内存泄漏防护侦听器通过在Tomcat启动期间触发驱动扫描来解决此问题。这个功能默认情况下启用。这意味着只有在监听器可见的库(例如$ CATALINA_BASE/lib中的库)将被扫描用于数据库驱动程序。如果您正在考虑禁用此功能,请注意,扫描将由使用JDBC的第一个Web应用程序触发,重新加载此Web应用程序以及依赖此功能的其他Web应用程序会出现故障。
因此,在WEB-INF/lib目录中的数据库驱动程序,不能依赖于服务提供者机制,应明确注册驱动程序。
java.sql.DriverManager中的驱动程序列表也是内存泄漏的已知来源。 Web应用程序注册的任何驱动程序必须在Web应用程序显式的停止、注销。当Web应用程序停止时,Tomcat将尝试自动发现和注销由Web应用程序类加载器加载的任何JDBC驱动程序。但是,预期应用程序通过ServletContextListener自己执行此操作。
三、数据库连接池(DBCP 2)配置
Apache Tomcat中的默认数据库连接池实现依赖于Apache Commons项目中的库。 使用以下库:
- Commons DBCP
- Commons Pool
这些库位于$ CATALINA_HOME/lib/tomcat-dbcp.jar的单个JAR中。 但是,仅包括连接池所需的类,并且已重命名包,以避免干扰应用程序。
DBCP 2.0已提供对JDBC 4.1的支持。
3.1、下载以及使用介绍
参考:http://commons.apache.org/proper/commons-dbcp/configuration.html
3.2、已知问题以及解决方案
目的:防止数据库连接池泄漏
优势:数据库连接池创建并管理与数据库的连接池。回收和重用已经存在的与数据库的连接比打开新连接更有效率。
问题:连接池有一个问题。 Web应用程序必须明确地关闭ResultSet,Statement和Connection。 Web应用程序关闭这些资源的失败可能导致它们再次无法再次使用数据库连接源以及数据库连接池“泄漏”。如果没有可用的连接,最终可能导致Web应用程序数据库连接失败。
解决方案:有一个解决这个问题的办法。 Apache Commons DBCP可以配置为跟踪和恢复这些废弃的数据库连接。它不仅可以恢复它们,还可以为打开这些资源的代码生成一个堆栈跟踪,而不会关闭它们。
要配置DBCP DataSource,以便删除和回收已放弃的数据库连接,请将以下属性之一或全部添加到DBCP DataSource的资源配置中:
removeAbandonedOnBorrow =真
removeAbandonedOnMaintenance =真
这两个属性的默认值为false。请注意,除非通过将timeBetweenEvictionRunsMillis设置为正值来启用池维护,否则removeAbandonedOnMaintenance无效。有关这些属性的完整文档,请参阅DBCP文档。
使用removeAbandonedTimeout属性设置数据库连接在被视为已被放弃之前已经空闲的秒数。
removeAbandonedTimeout = “60”
删除废弃连接的默认超时时间为300秒。
如果要让DBCP记录放弃数据库连接资源的代码的堆栈跟踪,则可以将logAbandoned属性设置为true。
logAbandoned = “真”
默认值为false。
3.3、MySql案例
1、驱动下载:https://www.mysql.com/products/connector/
2、配置context.xml:在/META-INF/context.xml配置如下:
<Context>
<!-- maxTotal: Maximum number of database connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to -1 for no limit.
-->
<!-- maxIdle: Maximum number of idle database connections to retain in pool.
Set to -1 for no limit. See also the DBCP documentation on this
and the minEvictableIdleTimeMillis configuration parameter.
-->
<!-- maxWaitMillis: Maximum time to wait for a database connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<!-- username and password: MySQL username and password for database connections -->
<!-- driverClassName: Class name for the old mm.mysql JDBC driver is
org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
-->
<!-- url: The JDBC connection url for connecting to your MySQL database.
-->
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxTotal="100" maxIdle="30" maxWaitMillis="10000"
username="javauser" password="javadude" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javatest"/>
</Context>
3、配置web.xml
路径:WEB-INF/web.xml
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<description>MySQL Test App</description>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
4、测试使用
创建test.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<sql:query var="rs" dataSource="jdbc/TestDB">
select id, foo, bar from testdata
</sql:query>
<html>
<head>
<title>DB Test</title>
</head>
<body>
<h2>Results</h2>
<c:forEach var="row" items="${rs.rows}">
Foo ${row.foo}<br/>
Bar ${row.bar}<br/>
</c:forEach>
</body>
</html>
备注:上面的代码使用了JSTL,需要下载相应的库,下载地址:
http://tomcat.apache.org/taglibs/standard/
只需要将:jstl.jar、 standard.jar 拷贝到应用程序的WEB-INF/lib directory.中即可。
3.4、Oracle 8i, 9i & 10g案例配置
1、配置Context
<Resource name="jdbc/myoracle" auth="Container"
type="javax.sql.DataSource" driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:mysid"
username="scott" password="tiger" maxTotal="20" maxIdle="10"
maxWaitMillis="-1"/>
2、配置web.xml
<resource-ref>
<description>Oracle Datasource example</description>
<res-ref-name>jdbc/myoracle</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
3、代码使用案例
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
DataSource ds = (DataSource)envContext.lookup("jdbc/myoracle");
Connection conn = ds.getConnection();
//etc.
3.5、使用代码示例:
Connection conn = null;
Statement stmt = null; // Or PreparedStatement if needed
ResultSet rs = null;
try {
conn = ... get connection from connection pool ...
stmt = conn.createStatement("select ...");
rs = stmt.executeQuery();
... iterate through the result set ...
rs.close();
rs = null;
stmt.close();
stmt = null;
conn.close(); // Return to connection pool
conn = null; // Make sure we don't close it twice
} catch (SQLException e) {
... deal with errors ...
} finally {
// Always make sure result sets and statements are closed,
// and the connection is returned to the pool
if (rs != null) {
try { rs.close(); } catch (SQLException e) { ; }
rs = null;
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { ; }
stmt = null;
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { ; }
conn = null;
}
}
关注我的技术公众号,查看更多优质技术文章推送
微信扫一扫下方二维码即可关注: