| 3.??BM??DML2012-01-05 13:39:33by SeaCat 除了查询,BM也能完成insert,update,delete等常规的DML操作。有两种方式:
下面将首先以autocrud模式为例,介绍通过BM执行数据操作的基本概念,然后再介绍service模式。 以前面的emp.bm为例,通过BM测试工具,向autocrud/test.emp/insert传递一条JSON数据: {empno:1,employee_name:"test_employee",deptno:10} 执行之后,可以看到服务端返回的结果: {"result":{"empno":1,"__parameter_parsed__":true,"deptno":10,"employee_name":"test_employee"},"success":true} 查看数据库中的emp表记录,可以看到新建了一条empno等于1的记录: SQL> select empno,ename,deptno from emp where empno=1; EMPNO ENAME DEPTNO ---------- -------------------------------------------------------------------------------- ---------- 1 test_employee 10 查看执行日志,可以看到实际执行的SQL: 2011-08-03 16:15:16.930 [aurora.database] [CONFIG] ============= BEGIN [Insert] SQL Statement execution dump ============ INSERT INTO EMP ( empno,ename,job,mgr,hiredate,deptno,sal,comm) VALUES ( ?,?,?,?,?,?,?,?) ---------------------Binding info--------------------- No.1 Access path:@empno Data type of passed value :java.lang.Long Value:1 Output:false Database Type:null No.2 Access path:@employee_name Data type of passed value :java.lang.String Value:test_employee Output:false Database Type:null No.3 Access path:@job Data type of passed value :[null] Value:null Output:false Database Type:null No.4 Access path:@mgr Data type of passed value :[null] Value:null Output:false Database Type:null No.5 Access path:@hiredate Data type of passed value :[null] Value:null Output:false Database Type:null No.6 Access path:@deptno Data type of passed value :java.lang.Long Value:10 Output:false Database Type:null No.7 Access path:@sal Data type of passed value :[null] Value:null Output:false Database Type:null No.8 Access path:@comm Data type of passed value :[null] Value:null Output:false Database Type:null
Aurora根据BM中定义的字段,自动拼接insert SQL语句,并将客户端传递过来的参数绑定到SQL语句中,完成insert操作。 以下参数可以对insert语句进行更充分地控制:
缺省设置下,参与insert操作的字段,其数据来自于数据池的/parameter部分,与该字段同名的参数,对应的XPath路径为/parameter/@[字段名]。如果某个字段的参数来自于其他路径,可以通过parameterPath属性,设置期望的参数路径。例如:
<bm:field name="created_by" parameterPath="/session/@user_id" /> 如果某字段的insert参数是一个复杂的SQL表达式,而不是简单地来源于某个路径下的参数,那么可以通过insertExpression属性来设置需要实用的SQL表达式,例如: <bm:field name="creation_date" insertExpression="trunc(sysdate)" /> 在insertExpression中,也可以使用${}标记,来引用其他的参数。 例1:计算字段 <bm:field name="total_price" insertExpression=" ${@total_amount} * ${@unit_price} * my_pkg.get_discount( ${/session/@user_id} ) " /> 例2:子查询 <bm:field name="empno" insertExpression="(select max(empno)+1 from emp)"/>
update操作的执行与insert类似。我们用BM测试工具向autocrud/test.emp/update传递如下JSON参数: {empno:1,employee_name:"test_employee_update",deptno:20} 可以看到,数据库中的记录已被更新: SQL> select empno,ename,deptno from emp where empno=1; EMPNO ENAME DEPTNO ---------- -------------------------------------------------------------------------------- ---------- 1 test_employee_update 20 查看执行日志,对应的SQL操作是: 2011-08-03 17:07:54.180 [aurora.database] [CONFIG] ============= BEGIN [Update] SQL Statement execution dump ============ UPDATE EMP e SET e.ename=?,e.deptno=? WHERE e.empno = ? ---------------------Binding info--------------------- No.1 Access path:@employee_name Data type of passed value :java.lang.String Value:test_employee_update Output:false Database Type:null No.2 Access path:@deptno Data type of passed value :java.lang.Long Value:20 Output:false Database Type:null No.3 Access path:@empno Data type of passed value :java.lang.Long Value:1 Output:false Database Type:null 与insert不同的是,根据BM中的primary-key设置,update操作会自动拼接[主键字段]=[参数值]这样的where条件,以确保一次操作只更新一条记录。相应的,客户端执行update操作时,提交的参数也必需包含所有的主键字段。 update操作所涉及的字段参数,与insert类似:
BM在执行update操作时,只更新客户端提交的字段,参数中没有提交的字段就不会被拼接到update语句中。在上面的例子中,就只有ename和deptno字段用于update语句。 而有些字段是不依赖于客户端提交的参数的。例如,last_update_date字段记录最近一次更新操作的时间。无论客户端提交什么样的参数,执行update时都需要更新这个字段。这时,就需要在BM中设置该字段的forceUpdate属性为true。例如: <bm:field name="last_update_date" forceUpdate="true" updateExpression="sysdate" />
有时候,某字段的update语句是一段SQL表达式,这段表达式所依赖的输入参数的名称不一定和字段相同。例如: <bm:field name="total_amount" updateExpression="my_pkg.get_sum(${/parameter/@order_head_id})" /> 按照前面的逻辑,如果没有传递一个名叫total_amount的参数,这个字段就不会被更新。这时,就需要设置一下inputPath属性,设置成该字段所依赖的一个输入参数,如 inputPath="/parameter/@order_head_id"。 这样,只要传递了order_head_id参数,这个字段就会按updateExpression指定的表达式去执行update。 删除操作相对比较简单。向autocrud/test.emp/delete传递JSON参数:
{empno:1}
查看执行日志,对应的SQL操作是: 2011-08-03 17:36:49.96 [aurora.database] [CONFIG] ============= BEGIN [Delete] SQL Statement execution dump ============ DELETE FROM EMP t WHERE t.empno = ? ---------------------Binding info--------------------- No.1 Access path:@empno Data type of passed value :java.lang.Long Value:1 Output:false Database Type:null =============== END [Delete] SQL Statement execution dump ============
如果希望在一次交互中对多条记录进行操作,可以使用批量操作的功能。在autocrud模式下,客户端提交的参数应是一个JSON对象数组,其中的每个JSON对象代表一条需要处理的记录,并用一个状态字段(缺省名为_status)标识要对该记录进行的操作。例如: [ {deptno:1,_status:"insert",dname:"test_dept_1"}, {deptno:2,_status:"insert",dname:"test_dept_2"} ] 这将提交两条记录,其中的_status属性值都是insert,这表示要对记录执行插入操作。查看执行日志,可以看到针对这两条记录,执行了两次SQL: 2011-08-04 14:59:49.300 [aurora.database] [CONFIG] ============ Running model batch update with data from path /parameter, total 2 records 2011-08-04 14:59:49.300 [aurora.database] [CONFIG] execute insert on record No.0 for model test.dept 2011-08-04 14:59:49.657 [aurora.database] [CONFIG] ... ============= BEGIN [Insert] SQL Statement execution dump ============ INSERT INTO dept ( deptno,dname) VALUES ( ?,?) ---------------------Binding info--------------------- No.1 Access path:@deptno Data type of passed value :java.lang.Long Value:1 Output:false Database Type:null No.2 Access path:@dname Data type of passed value :java.lang.String Value:test_dept_1 Output:false Database Type:null =============== END [Insert] SQL Statement execution dump ============ 2011-08-04 14:59:49.658 [aurora.database] [CONFIG] execute insert on record No.1 for model test.dept ... ============= BEGIN [Insert] SQL Statement execution dump ============ INSERT INTO dept ( deptno,dname) VALUES ( ?,?) ---------------------Binding info--------------------- No.1 Access path:@deptno Data type of passed value :java.lang.Long Value:2 Output:false Database Type:null No.2 Access path:@dname Data type of passed value :java.lang.String Value:test_dept_2 Output:false Database Type:null =============== END [Insert] SQL Statement execution dump ============ 2011-08-04 14:59:49.674 [aurora.database] [CONFIG] ============ End of batch update for /parameter 在后台数据库中,相应地插入了两条记录: SQL> select deptno, dname from dept where dname like 'test%'; DEPTNO DNAME ---------- -------------------- 1 test_dept_1 2 test_dept_2 也可以混合各种操作,例如: [ {deptno:1,_status:"update",dname:"test1_updated"}, {deptno:2,_status:"delete"} ] 这将删掉deptno=2的记录,并更新deptno=1的dname字段。 实际应用中,经常有头行结构的数据一次提交保存的需求。BM为这种操作模式提供了级联操作的支持。例如: <bm:model xmlns:bm="http://www.aurora-framework.org/schema/bm" baseTable="dept" needAccessControl="false"> <bm:fields> <bm:field name="deptno" dataType="java.lang.Long" databaseType="BIGINT"/> <bm:field name="dname" dataType="java.lang.String" databaseType="VARCHAR"/> </bm:fields> <bm:primary-key> <bm:pk-field name="deptno"/> </bm:primary-key> <bm:cascade-operations> <bm:cascade-operation inputPath="employees" model="test.emp" operations="insert,update,delete"/> </bm:cascade-operations> </bm:model> 我们对该BM执行batch_update操作,并提交下面的数据: [ {deptno:3,_status:"insert",dname:"test3_created",employees:[{_status:"insert",employee_name:"emp_test_3.1",deptno:3},{_status:"insert",employee_name:"emp_test_3.2",deptno:3}]}, {deptno:1,_status:"update",dname:"test1_updated",employees:[{_status:"insert",employee_name:"emp_test_1.1",deptno:1},{_status:"insert",employee_name:"emp_test_1.2",deptno:1}]} ] 除了dept自己的参数之外,每条记录多了一个名叫employees的数组,里面各有两条状态是insert的员工记录。这表示,对第一条记录,在dept表新建一条deptno=3的记录,并在emp表中插入两条属于该部门的新员工记录;对第二条记录,更新deptno=1的dname字段,同时也在emp表中新建两条属于该部门的员工记录。执行后,查看数据库,可见: SQL> select deptno,dname from dept where dname like 'test%'; DEPTNO DNAME ---------- -------------------- 3 test3_created 1 test1_updated SQL> select empno,deptno,ename from emp where ename like 'emp%'; EMPNO DEPTNO ENAME ---------- ---------- -------------------------------------------------------------------------------- 8007 3 emp_test_3.1 8008 3 emp_test_3.2 8009 1 emp_test_1.1 8010 1 emp_test_1.2
在BM中,通过cascade-operations标记,设置需要进行级联操作的从表,每一个从表设置一条cascade-operation标记,在cascade-operation标记中,通过inputPath属性设置该表对应的子记录集在头记录中的属性名(如上例中的employees),operations属性设置子记录允许的操作,如insert,update,delete,execute等,用逗号分隔。
在上例中,员工记录的deptno字段是通过参数指定的。而实际应用中,子记录所对应的父记录主键值,通常来源于父记录。这时,可以在emp表中,设置deptno字段的parameterPath属性: <bm:field name="deptno" parameterPath="../../@deptno" /> 这表示deptno字段来源于上上层记录的deptno属性。在XPath中,".."表示当前路径的上一级。由于我们提交的参数结构是每条dept记录下面包含一个employees数组,employees下面包含emp记录,所以对于emp记录来说,向上两级就是它所述的dept记录。 考虑到emp表有可能是单独执行insert,通过参数传递deptno值,也有可能是作为dept的从表在批量操作中执行insert,通过父记录获取deptno值,我们要为两种应用设计不同的BM。后面将介绍BM的继承模式,对于批量操作模式,我们可以从原有的emp中派生一个emp_for_batch_update,其他设置不变,只是改变deptno字段的parameterPath属性。
Demo Attachments |
Comments
0 Responses to the article暂时没有评论。