解决在页面删除记录后刷新造成的重复提交小技巧

本文介绍了在基于SSH的Web应用中,删除数据后刷新页面导致Hibernate找不到对象异常的问题及两种解决方案:一是将forward跳转改为redirect跳转;二是使用JavaScript模态窗口进行删除操作。
在做基于SSH的web应用的时候通常会遇到这样一种情况:我们在页面上的数据列表中删除一条数据,然后刷新一次当前页面hibernate会报一个异常:找不到对象 这是因为在第一次点击删除链接的时候已经将那条数据删除了,此时你可以在页面上单击右键-->属性 查看一下当前的链接它会是刚才你删除数据时候的链接例如:http://localhost/xxxx/org!del.action?pid=7 所以在这个时候如果你刷新一下当前页面就会把上一步的操作再次的执行一次 因为刚才的那一条数据已经删除了所以当然会因为找不到对象而报错 对此有两种解决方案:
(1)我们在列表中删除一条数据的时候,显示效果一般是一条数据删除完成后还会跳转到当前页面:这种跳转是forward跳转地址栏不会发生变化。所以刷新页面仍然会再次的提交请求。所以我们可以把这种forward跳转改成redirect跳转,这样地址就发生变化不会出现错误了

public String del() {
this.orgService.del(org.getId());
return "del_success";
}


<!--org.action默认执行execute()方法,传递参数pid是因为列表是一棵树有多级目录要做到在哪一级目录下面删除的还要跳转到哪一级目录下面才可以 -->
<result name="del_success">/org.action?pid=${pid}</result>


public String execute() {
List<Organization> orgs = this.orgService.search(pid);
ActionContext.getContext().put("orgs", orgs);
return index;
}


(2)因为删除操作是在当前页面进行的而且地址没有变,所以刷新会出问题,我们可以利用javascript在点击删除链接的时候打开一个模态的窗口,在这个窗口中删除,删除完成之后刷新一下列表页面就可以了,这样就阻止了用户的刷新页面操作。

<td align="center" vAlign="center"><a href="javascript:del('org!del.action?id=<s:property value="id"/>','确实删除记录?')">删除</a></td>


function openDeleteDialog(url,confirmString){
var c = confirmString;
if(c == null || c == ''){
c = "你确认要删除记录吗?";
}
if(confirm(c)){
return window.showModalDialog(url,"window123","dialogHeight:234px;dialogWidth:271px;resizable:no;help:yes;status:no;scroll:no");
}
return false;
}

/*
* 删除记录
*/
function del(url,info){
if(openDeleteDialog(url,info)){
window.location.reload(true);
}
}
<think>我们需要在Linux环境中通过命令行操作SQLite数据库,并观察插入数据前后文件大小的变化。 步骤: 1. 使用`du -b`或`ls -l`获取文件大小(字节)。 2. 使用sqlite3命令行工具执行SQL语句。 3. 分别对每个表插入一记录,每次插入后立即检查文件大小变化。 但是,由于sqlite3命令行是交互式的,我们可以通过脚本方式(例如使用here document)来执行。 假设数据库文件名为test.db,包含三个表:table1, table2, table3。 具体步骤: 1. 创建数据库和表(如果不存在)。 2. 记录初始大小。 3. 对第一个表插入一记录提交记录新大小,计算变化。 4. 重复步骤3,对第二个表和第三个表进行操作。 注意:为了避免表之间相互影响(例如因为同一个数据页面被多个表共享),我们可以在每次插入后立即执行VACUUM?但VACUUM会重组整个数据库,可能会影响测量。这里我们按照题目要求,仅插入一记录后立即查看大小变化,且不执行VACUUM。 由于SQLite使用页分配,可能第一次插入会分配一个新页(导致文件增加4096字节),后续插入可能使用已有的空闲页而不增加文件大小。因此,为了独立测量每个表,我们可以分别创建三个独立的数据库文件?但题目要求是同一个.db文件中的三个表。所以我们还是在一个文件中操作,但要注意顺序。 另一种方法:为了减少相互影响,我们可以先为每个表插入一记录(确保每个表都有至少一页),然后删除所有记录(但保留页),再重新插入?这样可能更复杂。 考虑到实际场景,我们可以接受这样的测量:第一个插入操作可能导致文件增大(分配新页),第二个和第三个可能不会(如果第一个分配的新页有足够空间容纳其他表的记录?但实际上,不同的表通常存储在不同的页中)。所以,我们依次插入,每次插入后立即查看大小变化。 以下是命令行步骤(假设使用bash): 步骤1:初始化数据库,创建三个表 步骤2:定义测量函数,包括: - 获取当前文件大小 - 执行插入操作 - 获取新文件大小 - 计算变化 由于需要依次对三个表操作,我们可以编写一个shell脚本。 示例脚本: ```bash #!/bin/bash DB_FILE="test.db" # 初始化数据库:创建表(如果不存在) sqlite3 "$DB_FILE" <<EOF CREATE TABLE IF NOT EXISTS table1 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE IF NOT EXISTS table2 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE IF NOT EXISTS table3 (id INTEGER PRIMARY KEY, name TEXT); EOF # 获取初始大小 get_size() { # 使用ls获取文件大小,格式为:-rw-r--r-- 1 user group 12345 Jan 1 12:00 test.db # 我们使用awk打印第五列 ls -l "$DB_FILE" | awk '{print $5}' } # 由于每次插入后需要提交,我们执行一插入命令后退出 # 注意:为了避免多次连接的开销(每次连接可能触发额外的写入),我们也可以在一个连接中完成,但为了测量准确,我们每次插入后断开连接,确保写入磁盘。 # 但是,我们可以在一个事务中完成所有操作?但这里我们每次插入后提交,所以每次插入都会立即写入。 # 测量单个表插入的函数 measure_table() { table_name=$1 size_before=$(get_size) # 执行插入操作 sqlite3 "$DB_FILE" "INSERT INTO $table_name (name) VALUES ('test');" size_after=$(get_size) echo "表 $table_name 插入后大小: $size_before -> $size_after, 变化: $((size_after - size_before)) 字节" } # 依次测量三个表 measure_table table1 measure_table table2 measure_table table3 ``` 注意:上述脚本中,每次调用measure_table都会重新连接数据库并执行插入。这会导致每次插入后都提交,从而确保写入磁盘。 但是,由于SQLite可能会延迟写入(写缓存),我们可能需要同步。在Linux中,我们可以使用`sync`命令确保写入磁盘,但通常sqlite3在退出时会提交刷新。为了保险,可以在插入后执行`sync`?但这里我们依赖sqlite3的正常行为。 另外,如果文件大小没有立即变化,可能是因为文件系统缓存。在脚本中,我们每次插入后都调用`sqlite3`命令,它会关闭数据库连接,应该会刷新。但为了确保,我们可以在插入后增加延迟?或者使用`sync`命令。 改进:在每次插入后调用`sync`来强制将缓存写入磁盘。但是,如果数据库文件在远程文件系统上,可能需要。这里我们假设是本地文件系统,且sqlite3退出已刷新。 然而,实际测试中,我们可能会发现第一次插入后文件大小增加了(因为分配了页),后续插入可能不会增加,因为数据库文件已经分配了足够的页。这符合预期。 运行脚本示例输出: 表 table1 插入后大小: 0 -> 4096, 变化: 4096 字节 表 table2 插入后大小: 4096 -> 4096, 变化: 0 字节 表 table3 插入后大小: 4096 -> 4096, 变化: 0 字节 解释:第一次插入时,数据库文件从0增长到4096(分配了一个页),随后的插入使用了该页中剩余的空间(因为一个页可以存储多记录),所以没有分配新页。 但是,如果每个表都存储在不同的页中,那么每个表的第一次插入都会导致分配一个新页?实际上,SQLite在创建表时不会立即分配页,而是等到插入数据时分配。而且,每个表的数据存储在B树中,每个B树至少有一个根页。所以,每个表的第一次插入都应该导致一个新页的分配。但为什么这里第二个表插入没有变化?因为第一个表已经占用了一页,第二个表插入时,数据库文件可能已经预先分配了多个页?或者是因为SQLite的文件头和其他元数据已经占用了空间,而第一个插入分配了第一页,第二个表插入时,数据库文件内部还有空闲空间(例如,文件头预留空间)? 实际上,SQLite数据库文件被分成固定大小的页,当第一次创建表时,系统表会记录信息,但不会为表的数据分配页,直到插入数据。当插入第一个表的数据时,会分配一个页(假设是页2,因为页1是文件头)。当插入第二个表时,由于文件已经有一个页(页2)被使用,但第二个表需要自己的页(页3),所以会分配一个新页,导致文件大小增加一个页的大小(4096)。但为什么上面脚本没有显示?因为SQLite可能一次分配多个页?或者是因为第一个页(页2)被第一个表使用,第二个表插入时,数据库文件已经包含了多个页(例如,初始创建时文件大小已经是足够多页)? 为了验证,我们可以查看数据库文件创建后的初始大小: 在创建表后(没有任何插入)的数据库文件大小是多少?通常是0?或者是一个页(4096字节)?实际上,当我们创建表时,这些信息存储在系统表(sqlite_master)中,而系统表位于页1。所以,创建三个表后,文件大小应该是4096字节(一个页)。然后,当我们插入第一个表时,会分配一个新页(页2)来存储这个表的数据,所以文件大小增加到8192字节(两个页)。插入第二个表时,再分配一个新页(页3),文件大小变为12288字节。第三个表同理,16384字节。 因此,我们预期每个表插入第一记录都会增加4096字节。 但是,我们之前的脚本在创建表后没有记录大小,而是在插入前记录的大小是创建表后的大小。所以: 步骤: 1. 创建表后,调用`get_size`,假设为size0(可能是4096)。 2. 插入表1:文件大小变为size0+4096(如果分配了新页)。 3. 插入表2:文件大小变为size0+4096*2。 4. 插入表3:文件大小变为size0+4096*3。 但是,实际情况可能更复杂,因为系统表(sqlite_master)也可能增长(当创建表时,系统表已经存储了表结构,插入数据不会改变系统表?)。 因此,为了准确测量每个表插入第一记录带来的变化,我们需要在创建表后,删除数据库文件,然后重新创建表(但不插入任何数据),然后分别测量每个表插入第一记录的变化。但题目要求是同一个文件中的三个表。 我们可以在创建表后,先不插入任何记录,然后分别对每个表插入第一记录,并记录变化。注意,创建表后,数据库文件已经有一个页(页1)存储元数据。 修改脚本: ```bash #!/bin/bash DB_FILE="test.db" rm -f "$DB_FILE" # 删除旧的数据库文件 # 创建数据库和表(此时文件大小不为0) sqlite3 "$DB_FILE" <<EOF CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE table2 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE table3 (id INTEGER PRIMARY KEY, name TEXT); EOF # 获取初始大小(创建表后,没有任何数据) size0=$(ls -l "$DB_FILE" | awk '{print $5}') echo "初始大小: $size0 字节" # 测量函数 measure_table() { table_name=$1 size_before=$(ls -l "$DB_FILE" | awk '{print $5}') sqlite3 "$DB_FILE" "INSERT INTO $table_name (name) VALUES ('test');" size_after=$(ls -l "$DB_FILE" | awk '{print $5}') echo "表 $table_name 插入后大小: $size_before -> $size_after, 变化: $((size_after - size_before)) 字节" } # 依次测量 measure_table table1 measure_table table2 measure_table table3 ``` 预期输出(假设页大小为4096): 初始大小: 4096 # 因为创建三个表,系统表占用一个页 表 table1 插入后大小: 4096 -> 8192, 变化: 4096 字节 表 table2 插入后大小: 8192 -> 12288, 变化: 4096 字节 表 table3 插入后大小: 12288 -> 16384, 变化: 4096 字节 实际运行可能会发现,初始大小就是4096,然后每次插入增加4096。 但是,如果SQLite在创建表时已经预分配了额外的页(例如,为了减少未来分配的开销),那么初始大小可能不止4096,这样第一次插入可能就不会增加4096。但默认情况下,创建表不会预分配数据页。 因此,使用上述脚本可以测量每个表插入第一记录后的文件大小变化。 另外,如果已经存在数据,再插入记录可能不会增加文件大小(因为页内有空闲空间),但此处我们测量的是第一记录。 注意:在Linux命令行中,我们也可以使用`stat`命令来获取文件大小,例如`stat -c %s test.db`,这样更简洁。因此,我们可以修改`get_size`函数: ```bash get_size() { stat -c %s "$DB_FILE" } ``` 如果系统不支持`stat -c`,则可以使用`ls -l`的方法。 因此,最终脚本(使用`stat`): ```bash #!/bin/bash DB_FILE="test.db" rm -f "$DB_FILE" # 确保从干净状态开始 # 创建表 sqlite3 "$DB_FILE" <<EOF CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE table2 (id INTEGER PRIMARY KEY, name TEXT); CREATE TABLE table3 (id INTEGER PRIMARY KEY, name TEXT); EOF # 获取初始大小(创建表后) size0=$(stat -c %s "$DB_FILE") echo "初始大小: $size0 字节" # 测量函数 measure_table() { table_name=$1 size_before=$(stat -c %s "$DB_FILE") sqlite3 "$DB_FILE" "INSERT INTO $table_name (name) VALUES ('test');" size_after=$(stat -c %s "$DB_FILE") echo "表 $table_name 插入后大小变化: $((size_after - size_before)) 字节" } # 测量三个表 measure_table table1 measure_table table2 measure_table table3 ``` 注意:每次插入后,我们立即获取文件大小,确保sqlite3进程已经退出并刷新了文件。 如果三个表的变化都是4096,则符合预期。 如果遇到权限问题,请确保当前用户有读写权限。 这就是在Linux命令行中不使用Python,而使用shell脚本和sqlite3命令行工具的方法。 §§相关问题§§ 1. 如何修改SQLite的默认页面大小以优化存储效率? 2. 在Linux中,除了使用`stat`和`ls -l`,还有哪些命令可以查看文件大小? 3. SQLite数据库文件大小为什么会在插入数据时按固定大小(如4096字节)增加? 4. 如何通过SQLite命令直接查看当前数据库的页面大小和总页面数? 5. 在同一个SQLite数据库文件中,多个表的数据存储位置是如何管理的? </think>在 Linux 环境中,无需编程即可通过命令行直接查看 SQLite 数据库文件大小变化。以下是详细操作步骤: ### 操作步骤: 1. **准备环境**: ```bash # 创建测试数据库和表 sqlite3 test.db "CREATE TABLE table1 (id INTEGER PRIMARY KEY, name TEXT);" sqlite3 test.db "CREATE TABLE table2 (id INTEGER PRIMARY KEY, name TEXT);" sqlite3 test.db "CREATE TABLE table3 (id INTEGER PRIMARY KEY, name TEXT);" ``` 2. **查看初始大小**: ```bash # 获取初始文件大小(字节) initial_size=$(stat -c %s test.db) echo "初始文件大小: $initial_size 字节" ``` 3. **逐表插入记录并测量变化**: ```bash # 表1:插入记录并测量 sqlite3 test.db "INSERT INTO table1 (name) VALUES ('test1');" size_table1=$(stat -c %s test.db) echo "表1插入后大小: $size_table1 字节, 变化: $((size_table1 - initial_size)) 字节" # 表2:插入记录并测量 sqlite3 test.db "INSERT INTO table2 (name) VALUES ('test2');" size_table2=$(stat -c %s test.db) echo "表2插入后大小: $size_table2 字节, 变化: $((size_table2 - size_table1)) 字节" # 表3:插入记录并测量 sqlite3 test.db "INSERT INTO table3 (name) VALUES ('test3');" size_table3=$(stat -c %s test.db) echo "表3插入后大小: $size_table3 字节, 变化: $((size_table3 - size_table2)) 字节" ``` ### 关键说明: 1. **核心命令**: - `stat -c %s filename`:精确获取文件字节大小 - `$(( ))`:bash 算术计算差值 - 每次插入后单独执行 `sqlite3` 命令确保数据写入磁盘 2. **典型输出示例**: ``` 初始文件大小: 12288 字节 表1插入后大小: 16384 字节, 变化: 4096 字节 表2插入后大小: 16384 字节, 变化: 0 字节 表3插入后大小: 20480 字节, 变化: 4096 字节 ``` 3. **注意事项**: - 变化值可能为 0:当插入数据可放入现有空闲页时 - 非零值通常是页大小的倍数(默认 4096 字节) - 使用 `sync` 命令可强制刷新磁盘缓存:`sync test.db` - 对于大文件,可用 `ls -lh` 查看易读的大小(KB/MB) ### 进阶技巧: ```bash # 单行命令快速测量(示例表1) size_before=$(stat -c %s test.db); sqlite3 test.db "INSERT INTO table1 VALUES (1,'test');"; size_after=$(stat -c %s test.db); echo "变化: $((size_after - size_before)) 字节" ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值