以入侵的方式实现基于Grails+db4o的数据稽查(Audit Trail)

要做Audit Trail,即跟踪记录对数据的所有CRUD操作,实现方式大体分两种类型:

1. Brute Force型,即在代码中写大量的类似于这样的代码

new OpLog(
    // properties ...
).save()

2. 技术技巧型,即使用优雅的事件监听机制。

 

由于使用grails/groovy和db4o,实现Audit Trail显得尤为简洁。

 

首先,写一个简单得不能再简单的打入db4o内部的(不在乎侵入不侵入了,反正已经吊死在grails和db4o上了,反正大家都已经紧密地结合了,反正谁都离不开谁了)持久化事件引擎:

package com.db4o.internal

public class InvasivePersistentEventEngine {
	
	private static HANDLERS = [:]
	static on(evt, handler) {
	    if(HANDLERS[evt] == null) {
	        HANDLERS[evt] = new HashSet()
	    }
	    HANDLERS[evt] << handler
	}
	static fireEvent(evt, obj) {
	    if(!HANDLERS[evt]) {
	        return
	    }
	    for(h in HANDLERS[evt]) {
	        h(obj)
	    }
	}
}
 

然后,稍稍修改一下db4o的两个类(从代码可以看出,是受到db4o的Log Message的启发,只要在configure一下messageLevel,把它设置为自己指定的Integer.MIN_VALUE就可以了)

public abstract class ObjectContainerBase  implements TransientClass, Internal4, ObjectContainerSpec, InternalObjectContainer {
    //...	
    public final int store3(Transaction trans, Object obj, int updateDepth, boolean checkJustSet) {
        //。。。
        if (configImpl().messageLevel() > Const4.STATE) {
            message("" + ref.getID() + " new " + ref.classMetadata().getName());
        }
        //+S.C.
        else if (configImpl().messageLevel() == Integer.MIN_VALUE) {
            InvasivePersistentEventEngine.fireEvent("new", ref.getObject());
        }
	//。。。
    }
}

 

 

public class ObjectReference extends PersistentBase implements ObjectInfo, Activator {
    //...
    private void logEvent(ObjectContainerBase container, String event, final int level) {
        if (container.configImpl().messageLevel() > level) {
            container.message("" + getID() + " " + event + " " + _class.getName());
        }
        //+S.C.
        else if (container.configImpl().messageLevel() == Integer.MIN_VALUE) {
            if(event == "update") {
                InvasivePersistentEventEngine.fireEvent(event, getObject());     
            }
        }
    }
}
 

小小地测试一下,先注入handlers。在Web Console中执行

import com.db4o.internal.InvasivePersistentEventEngine
import framework.utils.GroovyDataUtils
InvasivePersistentEventEngine.on('new', {o->
    println "object created [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o)
})
InvasivePersistentEventEngine.on('update', {o->
    println "object ${o.deleted?'deleted':'updated'} [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o)
})
 

然后执行

new User(username:'t@t.cc', password:'**').save()

输出object created [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 0]: [username:t@t.cc, locked:null, password:**, profile:null, profileId:null]

继续执行

def user = User.find(username:'t@t.cc')
user.password = 'password_changed'
user.save()

输出object updated [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: [password:password_changed, username:t@t.cc, locked:null, profile:null, profileId:null]

object created [16eb7e6c-8453-4e3d-8ddb-281470ed64f7_bak_0, 0]: [username:t@t.cc, locked:null, password:**, profile:null, profileId:null]

最后执行删除看一下

def user = User.find(username:'t@t.cc')
user.delete()

输出object deleted [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: [password:password_changed, username:t@t.cc, locked:null, profile:null, profileId:null]

 

入侵成功。

### 实现Grails中通过GSP页面实现Excel文件的数据导入功能 要在Grails应用程序中实现Excel数据导入功能,可以按照以下方法设计并开发: #### 1. 添加依赖项 为了处理Excel文件,需要引入第三方库来解析Excel文档。常用的库有Apache POI或JXL。可以通过`build.gradle`文件添加这些依赖。 ```groovy dependencies { implementation 'org.apache.poi:poi-ooxml:5.2.3' // Apache POI用于读取Excel文件 } ``` 此操作允许程序支持`.xls`和`.xlsx`格式的文件[^3]。 --- #### 2. 创建控制器逻辑 创建一个专门负责上传和解析Excel文件的控制器。例如,名为`ImportController.groovy`。 ```groovy class ImportController { def index() {} def upload() { MultipartFile file = request.getFile('excelFile') if (file && !file.isEmpty()) { try { Workbook workbook = new XSSFWorkbook(file.getInputStream()) Sheet sheet = workbook.getSheetAt(0) for (Row row : sheet) { Cell cellA = row.getCell(0) Cell cellB = row.getCell(1) String valueA = cellA?.stringCellValue ?: "" String valueB = cellB?.stringCellValue ?: "" println "Value A: $valueA, Value B: $valueB" // 将数据保存到数据库或其他存储位置 } flash.message = "成功导入 ${sheet.getLastRowNum()} 条记录." } catch (Exception e) { flash.error = "发生错误: ${e.getMessage()}" } } else { flash.error = "未选择任何文件!" } redirect(action: 'index') } } ``` 在此代码片段中,使用了Apache POI中的`XSSFWorkbook`类来加载Excel文件,并逐行遍历工作表的内容[^4]。 --- #### 3. 设计GSP视图 在`grails-app/views/import/index.gsp`中定义HTML表单以供用户上传Excel文件。 ```html <!DOCTYPE html> <html> <head> <title>Excel 数据导入</title> </head> <body> <h1>Excel 文件导入</h1> <g:if test="${flash.message}"> <div class="message">${flash.message}</div> </g:if> <g:if test="${flash.error}"> <div class="error">${flash.error}</div> </g:if> <form action="${createLink(controller:'import', action:'upload')}" method="post" enctype="multipart/form-data"> <label for="excelFile">请选择 Excel 文件:</label><br/> <input type="file" id="excelFile" name="excelFile"/><br/><br/> <button type="submit">上传并导入</button> </form> </body> </html> ``` 这段代码提供了一个简单的界面让用户可以选择本地计算机上的Excel文件并通过POST请求提交给服务器[^5]。 --- #### 4. 配置路由 确保应用能够识别新的动作路径,在`UrlMappings.groovy`中加入如下配置: ```groovy "/import"(controller:"import", action:"index") "/import/upload"(controller:"import", action:"upload") ``` 这一步使得URL `/import`指向默认显示页而`/import/upload`则接收来自用户的文件上传请求[^6]。 --- #### 5. 测试与优化 测试整个流程是否正常运作,包括但不限于验证不同类型的Excel文件兼容性、异常情况下的反馈机制以及性能表现等方面。如果发现某些特定场景下存在问题,则需进一步调整和完善相应部分。 --- ### 注意事项 - **安全性**:应考虑对上传文件进行病毒扫描及大小限制。 - **用户体验**:可增加进度条等功能提升交互体验。 - **扩展性**:未来可能还需要支持更多种类电子表格软件产生的文件格式转换需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值