[TOC]
0 前言
使用java進(jìn)行數(shù)據(jù)庫(kù)操作時(shí)最痛苦的莫過(guò)于拼接SQL語(yǔ)句蚯舱。在實(shí)際運(yùn)行時(shí)往往需要查看<font color='red'>實(shí)際生成的SQL語(yǔ)句</font>和實(shí)際<font color='red'>傳入的參數(shù)</font>,或許還會(huì)有查看SQL<font color='red'>執(zhí)行時(shí)間</font>等的需求昧诱。
無(wú)論原生JDBC晓淀、dbutils、mybatis還是hibernate盏档,使用log4j等日志框架可以看到生成的SQL,但是占位符和參數(shù)總是分開打印的燥爷。實(shí)在是不太友好蜈亩。顯示如下的效果:select * from t_user where age>? and (sex=? or dept_id=?)
<font color='red'>log4jdbc</font>能很好的解決上述問(wèn)題。使用log4jdbc之后的效果如下:
select * from t_user where age>1 and (sex=0 or dept_id='007')
1 log4jdbc簡(jiǎn)介
沒(méi)有什么比官網(wǎng)對(duì)它的介紹更加貼切了:
log4jdbc is a Java JDBC driver that can log SQL and/or JDBC calls (and optionally SQL timing information) for other JDBC drivers using the Simple Logging Facade For Java (SLF4J) logging system.
但是前翎,眾所周知googlecode在國(guó)內(nèi)的訪問(wèn)問(wèn)題……
官網(wǎng)的News一欄有如下消息:2015-03-30: Due to Google Code shutting down soon, log4jdbc has moved to github at 2015-03-30: Due to Google Code shutting down soon, log4jdbc has moved to github at https://github.com/arthurblake/log4jdbc
2 特性
- 完全支持JDBC3和JDBC4
- 配置簡(jiǎn)單稚配,一般情況下你只需要將你的DriverClass改為:net.sf.log4jdbc.DriverSpy,并在你的jdbcUrl之前拼接jdbc:log4
- 自動(dòng)將占位符(?)替換為實(shí)際的參數(shù)
- 能夠及時(shí)方便地顯示SQL的實(shí)際執(zhí)行時(shí)間
- 顯示SQL Connection的數(shù)量的信息
- 能在JDK1.4+和SLF4J1.X上和大多數(shù)常見(jiàn)的JDBC驅(qū)動(dòng)協(xié)同工作
- open source
3 使用
以下多數(shù)信息來(lái)自于官網(wǎng)
3.0 jar包的選擇
JDK Version | jar file |
---|---|
JDK 1.4 or 1.5 | JDBC 3 version of log4jdbc |
JDK 1.6 or 1.7 | JDBC 4 version of log4jdbc |
不了解 JDBC3 JDBC4港华?
3.1 日志系統(tǒng)的選擇
log4jdbc 使用Simple Logging Facade for Java (SLF4j) 作為日志系統(tǒng)道川,(SLF4J)是一個(gè)簡(jiǎn)單靈活的日志抽象層,可以方便的在以下日志系統(tǒng)之間切換:
- Log4j
- java.util logging in JDK 1.4
- logback
- Jakarta Commons Logging
下載 SLF4j,你將需要slf4j-api-1.5.0.jar和你實(shí)際所用的日志系統(tǒng)的jar包冒萄,或許還會(huì)有一個(gè)適配的中間插件jar包(取決于你使用的實(shí)際日志系統(tǒng))臊岸。
3.2 更改DriverClass
log4jdbc "spy" driver 將會(huì)嘗試著加載以下驅(qū)動(dòng):
Driver Class | Database Type |
---|---|
oracle.jdbc.driver.OracleDriver | Older Oracle Driver |
oracle.jdbc.OracleDriver | Newer Oracle Driver |
com.sybase.jdbc2.jdbc.SybDriver | Sybase |
net.sourceforge.jtds.jdbc.Driver | jTDS SQL Server & Sybase driver |
com.microsoft.jdbc.sqlserver.SQLServerDriver | Microsoft SQL Server 2000 driver |
com.microsoft.sqlserver.jdbc.SQLServerDriver | Microsoft SQL Server 2005 driver |
weblogic.jdbc.sqlserver.SQLServerDriver | Weblogic SQL Server driver |
com.informix.jdbc.IfxDriver | Informix |
org.apache.derby.jdbc.ClientDriver | Apache Derby client/server driver, aka the Java DB |
org.apache.derby.jdbc.EmbeddedDriver | Apache Derby embedded driver, aka the Java DB |
com.mysql.jdbc.Driver | MySQL |
org.postgresql.Driver | PostgresSQL |
org.hsqldb.jdbcDriver | HSQLDB pure Java database |
org.h2.Driver | H2 pure Java database |
<font color="blue">注意:</font> 如果你要使用一個(gè)不在上表中的Driver,請(qǐng)?zhí)峁﹍og4jdbc.drivers配置尊流,多個(gè)之間用逗號(hào)分隔帅戒,不帶空格。
3.3 prepend jdbcUrl屬性
例如:
你的url為: url= <font color="blue">jdbc:mysql://localhost:3306/mvn</font>
應(yīng)該改為: url= <font color="blue"><font color="red">jdbc:log4</font>jdbc:mysql://localhost:3306/mvn</font>
3.4 建立你的日志系統(tǒng)
log4jdbc使用5種logger:
logger | 描述 | since |
---|---|---|
jdbc.sqlonly | 僅僅記錄 SQL 語(yǔ)句崖技,會(huì)將占位符替換為實(shí)際的參數(shù) | 1.0 |
jdbc.sqltiming | 包含 SQL 語(yǔ)句實(shí)際的執(zhí)行時(shí)間 | 1.0 |
jdbc.audit | 除了 ResultSet 之外的所有JDBC調(diào)用信息逻住,篇幅較長(zhǎng) | 1.0 |
jdbc.resultset | 包含 ResultSet 的信息,輸出篇幅較長(zhǎng) | 1.0 |
jdbc.connection | 輸出了 Connection 的 open迎献、close 等信息 | 1.2alpha1 |
此外還有個(gè)叫 log4jdbc.debug 的 logger瞎访,用于log4jdbc的內(nèi)部調(diào)試,會(huì)輸出 log4jdbc spy 加載驅(qū)動(dòng)的時(shí)的信息吁恍,如driver found 或 not found 等信息装诡。
3.5 修改debug選項(xiàng)--可選
可以在類路徑下提供一個(gè)名為 <font color="blue">log4jdbc.properties </font>的配置文件,用以修改一些默認(rèn)的debug屬性践盼。
其常用屬性如下(來(lái)自于 官網(wǎng) 文檔):
property | default | description | since |
---|---|---|---|
log4jdbc.drivers | log4jdbc 加載的一個(gè)或多個(gè)驅(qū)動(dòng)的全類名鸦采。如果有多個(gè),每個(gè)之間用逗號(hào)分隔(不帶空格).對(duì)應(yīng)常見(jiàn)的 JDBC drivers 此選項(xiàng)不是必須的咕幻。但是如果需要多個(gè) driver 渔伯,需要配置該選項(xiàng)。 | 1.0 | |
log4jdbc.auto.load.popular.drivers | true | 自動(dòng)加載常用的jdbc driver肄程,如果設(shè)置為false锣吼,則必須提供 log4jdbc.drivers 屬性。 | 1.2beta2 |
log4jdbc.debug.stack.prefix | The partial (or full) package prefix for the package name of your application. The call stack will be searched down to the first occurrence of a class that has the matching prefix. If this is not set, the actual class that called into log4jdbc is used in the debug output (in many cases this will be a connection pool class.) For example, setting a system property such as this: -Dlog4jdbc.debug.stack.prefix=com.mycompany.myapp Would cause the call stack to be searched for the first call that came from code in the com.mycompany.myapp package or below, thus if all of your sql generating code was in code located in the com.mycompany.myapp package or any subpackages, this would be printed in the debug information, rather than the package name for a connection pool, object relational system, etc. | 1.0 | |
log4jdbc.sqltiming.warn.threshold | 毫秒值.執(zhí)行時(shí)間超過(guò)該值的SQL語(yǔ)句將被記錄為warn級(jí)別. | 1.1beta1 | |
log4jdbc.sqltiming.error.threshold | 毫秒值.執(zhí)行時(shí)間超過(guò)該值的SQL語(yǔ)句將被記錄為error級(jí)別. | 1.1beta1 | |
log4jdbc.dump.booleanastruefalse | false | 當(dāng)該值為 false 時(shí)蓝厌,boolean 值顯示為 0 和 1 玄叠,為 true 時(shí) boolean 值顯示為 true 和 false | 1.2alpha1 |
log4jdbc.dump.sql.maxlinelength | 90 | SQL 分行的最大值 | 1.2alpha1 |
log4jdbc.dump.fulldebugstacktrace | false | 設(shè)置為 true 將會(huì)輸出大篇幅的 debug信息 | 1.2alpha1 |
log4jdbc.dump.sql.select | true | 是否輸出 select 語(yǔ)句 | 1.2alpha1 |
log4jdbc.dump.sql.insert | true | 是否輸出 insert 語(yǔ)句 | 1.2alpha1 |
log4jdbc.dump.sql.delete | true | 是否輸出 delete 語(yǔ)句 | 1.2alpha1 |
log4jdbc.dump.sql.update | true | 是否輸出 update 語(yǔ)句 | 1.2alpha1 |
log4jdbc.dump.sql.create | true | 是否輸出 create 語(yǔ)句 | 1.2alpha1 |
log4jdbc.dump.sql.addsemicolon | false | 是否在 SQL 的行末添加一個(gè)分號(hào) | 1.2alpha1 |
log4jdbc.statement.warn | false | Set this to true to display warnings (Why would you care?) in the log when Statements are used in the log. NOTE, this was always true in releases previous to 1.2alpha2. It is false by default starting with release 1.2 alpha 2. | 1.2alpha2 |
log4jdbc.trim.sql | true | Set this to false to not trim the logged SQL. (Previous versions always trimmed the SQL.) | 1.2beta2 |
log4jdbc.trim.sql.extrablanklines | true | Set this to false to not trim extra blank lines in the logged SQL (by default, when more than one blank line in a row occurs, the contiguous lines are collapsed to just one blank line.) (Previous versions didn't trim extra blank lines at all.) | 1.2 |
log4jdbc.suppress.generated.keys.exception | false | Set to true to ignore any exception produced by the method, Statement.getGeneratedKeys() (Useful for using log4jdbc with Coldfusion.) | 1.2beta2 |
4 實(shí)戰(zhàn)
4.0 純JDBC--log4j
4.0.0 jar包
maven 依賴
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.log4jdbc</groupId>
<artifactId>log4jdbc</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
4.0.1 配置文件
- log4j.properties
log4j.logger.jdbc.sqlonly=OFF log4j.logger.jdbc.sqltiming=INFO log4j.logger.jdbc.audit=OFF log4j.logger.jdbc.resultset=OFF log4j.logger.jdbc.connection=OFF log4j.logger.jdbc.sqlonly=console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n #log4j.logger.jdbc.sqltiming=INFO,console #log4j.logger.jdbc.connection=INFO,console log4j.rootLogger=DEBUG, console
- dbconfig.properties
url:jdbc:log4jdbc:mysql://localhost:3306/mvn driverClassName:net.sf.log4jdbc.DriverSpy username:root password:root
- log4jdbc.properties
log4jdbc.debug.stack.prefix=software_test.log4jdbc log4jdbc.drivers=com.mysql.jdbc.Driver log4jdbc.auto.load.popular.drivers=true log4jdbc.statement.warn=true log4jdbc.sqltiming.warn.threshold=1000 log4jdbc.sqltiming.error.threshold=3000 log4jdbc.dump.booleanastruefalse=true log4jdbc.dump.sql.maxlinelength=90 log4jdbc.dump.fulldebugstacktrace=false log4jdbc.dump.sql.select=true log4jdbc.dump.sql.insert=true log4jdbc.dump.sql.delete=true log4jdbc.dump.sql.update=true log4jdbc.dump.sql.create=true log4jdbc.dump.sql.addsemicolon=false log4jdbc.trim.sql=true log4jdbc.trim.sql.extrablanklines=true log4jdbc.suppress.generated.keys.exception=false
4.0.2 測(cè)試
```java
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import org.junit.Test;
public class SimpleTest {
@Test
public void test1() {
String sql = "select * " + "from t_user where id=?";
Connection conn = null;
PreparedStatement ps = null;
try {
conn = getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 2);
ps.executeQuery();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getConnection() throws IOException, SQLException,
ClassNotFoundException {
String driverClassName = null;
String jdbcUrl = null;
String user = null;
String password = null;
// 讀取類路徑下的配置文件
InputStream in = getClass().getClassLoader().getResourceAsStream(
"dbconfig.properties");
Properties properties = new Properties();
properties.load(in);
driverClassName = properties.getProperty("driverClassName");
jdbcUrl = properties.getProperty("url");
user = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driverClassName);
// 連接信息
Properties info = new Properties();
info.put("user", user);
info.put("password", password);
// 獲取連接
Connection connection = DriverManager.getConnection(jdbcUrl, user,
password);
return connection;
}
}
```
4.1 DataSource--c3p0
截至目前為止,官網(wǎng) 對(duì)于log4jdbc和數(shù)據(jù)源的使用還沒(méi)有一個(gè)很好地例子,以下是官網(wǎng)的截圖:
本人嘗試了一下C3P0數(shù)據(jù)源拓提,不到之處請(qǐng)指正读恃。
4.1.0 jar包
maven 依賴
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.log4jdbc</groupId>
<artifactId>log4jdbc</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
4.1.1 配置文件
此處只需要 log4j.properties 和log4jdbc.properties 兩個(gè)配置文件即可,配置同4.0中的配置文件代态。
4.1.2 測(cè)試
package software_test.log4jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class DataSourceTest {
@Test
public void testC3P0() {
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("1234");
dataSource.setDriverClass("net.sf.log4jdbc.DriverSpy");
dataSource.setJdbcUrl("jdbc:log4jdbc:mysql://localhost:3306/mvn");
dataSource.setMaxPoolSize(50);
Connection conn = dataSource.getConnection();
String sql = "select * " + "from t_user where id=?";
PreparedStatement ps = null;
ps = conn.prepareStatement(sql);
ps.setInt(1, 2);
ps.executeQuery();
ps.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5 聲明
在此將log4jdbc的使用結(jié)合官網(wǎng)的幫助文檔中主要的一部分羅列出來(lái)寺惫。部分不太常用或理解不到位的沒(méi)有翻譯,怕誤人子弟蹦疑,有翻譯不周的地方或理解不到位的地方歡迎指正西雀。
實(shí)戰(zhàn)部分,以后會(huì)抽時(shí)間加入其它的使用方式歉摧。
<font color="red">轉(zhuǎn)載請(qǐng)保留出處艇肴。</font>