我已实施数据库日志记录,以将我的java weblogic门户应用程序中的某些详细信息记录到Oracle DB。 为此,我使用连接池来获取连接并使用它来对存储过程进行jdbc调用。
我有一个静态java方法logService,它获取连接对象并调用SP。 这个logservice方法是从我的java应用程序中的不同位置调用的,其中相关的详细信息在参数中传递,其中inturn传递给SP调用。
因此,每个访问门户网站的用户都会有大约10-20个logservice方法调用。 所以,我将拨打10-20个SP电话,每次拨打SP都必须提交。
但问题是,我的DBA建议不要为每个事务提交并且一次提交所有10-20个事务,因为他说的提交频率很高。 我该如何做到这一点?如果不清楚,我可以发布示例代码。
是的我已经为我的网络应用程序安装了Filter,如下所示,
public final class RequestFilter implements Filter{
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
try {
((HttpServletResponse) response).sendRedirect(redirectUrl);
if (chain != null) {
chain.doFilter(request, response);
}
}
finally { // commit transaction here }
}
}
重定向到redirectURL后,我在应用程序的许多地方调用logservice,如下所示,
DBLog.logService(param1, param2); // static method call
此logservice使用连接池对象准备调用SP。 我正在提供一份简短的示例代码,说明我的工作方式。
public static void logService(String param1, String param2) {
try {
con=getConnection();
stmt = con.prepareCall("{call DB_LOG_SP (?, ?}");
stmt.setString(1, param1);
stmt.setString(2, param2);
stmt.execute();
stmt.close();
}finally {
stmt.close();
con.close();
}
}
但是由于我在SP调用后关闭连接,如何在doFilter方法中的finally {}块中提交我的事务?
过滤方式 -
public class DBLog {
static Connection con = null;
static PreparedStatement stmt = null;
static {
try{
new net.verizon.whatsnext.dblog.JDCConnectionDriver("oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@localhost:7001:xe","user", "password");
con=getConnection(); // Getting connection from Connection pool
}catch(Exception e){}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:jdc:jdcpool");
}
public static void logService(String param1, String param2) {
...
finally {
stmt.close();
}
}
public static void closeTrans() {
con.commit();
con.close();
}
} //End of DBLog class
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
...
finally {
DBLog.closeTrans();
}
}
所以,我在doFilter方法的finally块中提交之后最终关闭了连接。 我做对了吗?它会起作用吗?
答案 0 :(得分:1)
考虑使用每个请求的事务。您可以创建一个Filter,在处理请求之前打开数据库事务并在之后提交。这样就可以了。
答案 1 :(得分:1)
事务划分,就像安全和日志记录一样,可以看作是代码的装饰。
假设您有一个接口BusinessTask
,它是您当前所有数据库操作正在实现的接口:
public interface BusinessTask {
public void doWork();
}
例如,您有一个AddSavingsAccountTask
和一个TransferMoneyTask
。
现在,您最初可以以每个任务单独提交(或回滚)的方式实现每个任务。但是,如果您想创建一个包含创建帐户并立即转账的任务,那么您将遇到麻烦,因为现在您需要更改事务划分。
因此,一种方法是定义TransactionDecorator,您可以在其中放置事务划分。
public class TransactionDecorator implements BusinessTask{
private final BusinessTask task;
public TransactionDecorator(BusinessTask task){
this.task = task;
}
@Override
public void doWork() {
//beging transaction
task.doWork();
//commit or rollback
}
}
然后,您可以使用事务划分简单地装饰现有任务:
final AddSavingsAccount addSavingsAccountTask = new AddSavingsAccount();
final TransferFunds transferFundsTask = new TransferFunds();
BusinessTask transaction = new TransactionDecorator(new BusinessTask(){
@Override
public void doWork() {
addSavingsAccountTask.doWork();
transferFundsTask.doWork();
}
});
transaction.doWork(); //all the magic happens here
现在,我只是建议一个模式。在您的代码中实现这一点将根据您的设计而有所不同,具体取决于您的代码的结构。
进一步参考
答案 2 :(得分:1)
首先,不要在每个请求上打开连接,而是重用连接池中的连接(可能是DataSource),并放入共享空间 - 在您的情况下,最简单的方法是利用static { {3}}字段。
在过滤器中:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
Connection conn = obtainConnection(); // obtain connection and store it ThreadLocal
conn.setAutoCommit(false);
try {
chain.doFilter(request, response);
} finally {
conn.commit();
}
}
在您的记录器类中:
public static void logService(String param1, String param2) {
Connection conn = getConnection(); // obtain thread-local connection
// your database statements go here
}
一般情况下,请避免使用静力学。也许可以考虑重新考虑您的日志记录方法。