3.??BM??DML

2012-01-05 13:39:33by SeaCat

通过BM执行DML

除了查询,BM也能完成insert,update,delete等常规的DML操作。有两种方式:

  1. autocrud模式,与查询类似,客户端以json方式提交参数到"[Web目录]/autocrud/[BM名称]/[操作名]"地址,调用BM完成某一种操作。

  2. service模式,通过一个svc文件定义一系列要执行的操作,再将结果以JSON或web service形式返回给客户端。

下面将首先以autocrud模式为例,介绍通过BM执行数据操作的基本概念,然后再介绍service模式。

通过BM执行insert

以前面的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语句进行更充分地控制:

字段控制
如果希望某个field不出现在insert语句中,可以设置field的forInsert="false"。
参数来源
缺省设置下,参与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)"/>
		

通过BM执行update

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类似:

Table 1. update操作的field控制参数

参数含义
forUpdate该字段是否会用于update操作中
updateExpression执行update操作所用的SQL表达式


update字段控制

BM在执行update操作时,只更新客户端提交的字段,参数中没有提交的字段就不会被拼接到update语句中。在上面的例子中,就只有ename和deptno字段用于update语句。

而有些字段是不依赖于客户端提交的参数的。例如,last_update_date字段记录最近一次更新操作的时间。无论客户端提交什么样的参数,执行update时都需要更新这个字段。这时,就需要在BM中设置该字段的forceUpdate属性为true。例如:

<bm:field name="last_update_date" forceUpdate="true" updateExpression="sysdate" />
			

自定义update语句

有时候,某字段的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。

通过BM执行删除

删除操作相对比较简单。向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

      暂时没有评论。

      发表评论