接到一个任务,需要对项目中用到的JDBC进行改造,需求如下:
- 不要将实际数据库的连接方式(包括url,user,password)暴露给客户端
- 记录每个执行的sql内容,包括sql及其执行参数
- 尽可能少的降低代码修改**
- 性能上不能与直接使用jdbc有很大差距
按此需求,需要实现以下几点:
- sql是一定要放在server端的
- 需要将sql记录到日志中
- 客户端仍然需要使用jdbc的方式,才能保证尽可能少的修改
- 性能要高
这不就是将jdbc也使用客户端-服务器的方式吗?而且要求高性能?
JDBC接口的实现
代码尽可能少的改动,这就要求必须在客户端实现JDBC的接口,然后在客户端的jdbc实现中调用服务器上的服务完成业务的处理。
这样在客户端仅需替换jdbc驱动的jar包,然后代码中替换相应的驱动代码即可。
这样又回到了面向服务的SOA设计中来了,可以提供给多个客户端使用。
按此思路,就想到了最常用的几种方式:
1. soap/restful webservice
显然,用webservice的缺点就是性能太低,马上予以排除。
2. 直接使用socket变成实现
实现复杂,对技术要求高,待定。
3. java rmi
EJB中使用的通信技术,效率不错,可以考虑。
在开源的时代,开源框架是加快项目进度的利器,所以我也是首先找找开源框架。
别说,还真有,就是RmiJdbc,那就是你啦。
大体看了一下源码,已经是非常完善可以作为产品使用了,感谢作者大神的工作。
既然如此,只需要把写日志功能加上就好了,马上开始修改吧
第一步,记录SQL
这个算是比较简单的了。
1.在RJStatementServer中声明一个List,用来记录sql
为什么要用List?主要是因为会有batch执行的情况。
2.针对Statement,在RJStatementServer的executeQuery,execute,addBatch等方法中,把需要执行的sql保存到List
3.针对PreparedStatement,在RJConnectionServer中,preparedStatement方法中把sql保存到List
第二步,记录日志
增加一个记录日志的类RJLog
public class RJLog {
public static void saveLog(List<String> sqlList) {
String sql = Arrays.toString(sqlList.toArray());
String execTime = DateTime.now().toString(DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss"));//此处使用joda-time2.2.jar
//以下是记录日志的方法,详见后面
......
}
}
日志的设计与实现
目前的要求是将日志存入数据库中。
由于访问量没那么大,因此产生的日志不会很多,大概一天上万条左右。高峰时期估计每秒产生几十条日志。
日志的写入,也分为同步写入、log4j的半异步写入和完全的异步写入三种。
同步写入数据库
没什么好说的,在执行完成sql之后,直接将sql通过jdbc写入日志数据库中,也就是说,产生一个日志记录,就要写入一次。
缺点:效率低,非常低。创建数据库连接是非常耗时的,如果一个业务执行过程中有大量的sql,那就积少成多影响体验了。
log4j的半异步
说到日志,首先想到的就是log4j,可以写入文件,也可以写入数据库,并且还能异步写入!
由于一开始要求能够支持jdk1.5,因此只能使用log4j-1.x版本,此处用的是1.2.17版。后来虽然改为jdk1.6,但是也没有使用log4j2.x
log4j.properties配置
使用log4j将日志写入数据库比较简单,网上有很多配置实例,唯一需要注意的地方是,log4j也是可以异步记录日志的,通过配置参数,设置累积多少条记录一次。
举个log4j配置的栗子:
log4j.appender.db=org.apache.log4j.jdbc.JDBCAppender #使用JDBCAppender ,这样才能写入库
log4j.appender.db.BufferSize=10 #此数据值代表每次累积到10条sql就写入数据
log4j.appender.db.driver=oracle.jdbc.driver.OracleDriver #jdbc驱动
log4j.appender.db.URL=jdbc:oracle:thin:@10.xx.xx.xx:1521:orcl #数据库连接,此处是oracle
log4j.appender.db.user=xxxx #数据库user
log4j.appender.db.password=xxxx #数据库password
log4j.appender.db.sql=insert into LOG_INFO (sql,createTime) values (‘%X{sql}’,’%X{exectime}’) #写日志的sql
log4j.appender.db.layout=org.apache.log4j.PatternLayout
改造日志类RJLog
如下,就能把日志信息记录下来了
public class RJLog {
static Logger logger = LoggerFactory.getLogger(RJLog.class);
public static void saveLog(List<String> sqlL