Grails探索之访问存储过程及其事务控制

                     Grails探索之访问存储过程及其事务控制
 
给Grails项目配置好了数据源以后,就可以访问数据库了。Grails开发平台已经为我们配置好了Hibernate,我们可以使用GORM通过Hibernate访问数据库。这些在官方文档上有了一个初步的、详细的描述,这里就不再重述。
在很多的时候,我们会使用存储过程访问数据库,而不是使用ORM,因为存储过程对于复杂的数据库访问比起ORM有强大的优势。这就提出了一个问题,如何在Grails平台访问数据库的存储过程?
据说Hibernate为用户开放了访问存储过程的接口,但一方面我以前没有使用过Hibernate,对它不是很熟悉;另外一方面,我对GSQL的存储过程接口很感兴趣,感觉它比我以前使用的Spring的存储过程接口要好。所以,下面我将使用GSQL的存储过程接口在Grails平台来访问数据库。
我们先从简单的存储过程说起,假设我们有一个叫“ PA_PPRM_PivotCar.P_Tran”的存储过程,有三个输入参数,没有输出参数。大家可以试着写一个存储过程,有包没包都没关系,这个存储过程有多个输入参数,作用是简单的insert一个table。
下面我们来访问这个存储过程。
class LoginService {
   
        def dataSource
   
    def check(User u) {
 
我们对存储过程的访问都是在 service 层进行的,注意声明 dataSource ,这样 Grails 平台会将 dataSource 对象通过 Spring 注入进来,我们就可以在服务层使用数据源了。
我们对存储过程的访问在 check(User u) 方法里,下面是代码:
        Sql sql = new Sql(dataSource)
       
       
上面的代码是初始化Sql对象,记得引入该类,“ import groovy.sql.Sql”,然后就可以访问存储过程了:
        sql.call("call PA_PPRM_PivotCar.P_Tran(?,?,?)",['3','cheng','m'])
}
 
很简单,使用sql对象的call方法,第一个参数是一个字符串“ call PA_PPRM_PivotCar.P_Tran(?,?,?)”,使用过Jdbc访问过存储过程的人都知道这个字符串的意思,这里不再多说。第二个参数是存储过程的输入参数,以List对象组织,按存储过程的输入顺序作为List对象元素的顺序。
就这样就完成了对存储过程的访问,是不是很简单?
下面就再进一步,我们把上面的“ PA_PPRM_PivotCar.P_Tran”进行改造,给这个存储过程一个返回值,存储过程的功能还是insert一个table,因为这个table有一个主键,所以如果输入的记录主键重复的话,insert就会失败,这个存储过程的返回值就是insert成功还是失败,如果成功返回0,失败返回-1。
在这种情况下,在调用call方法之前的步骤还是和以前一样,我在这里不再重述。下面是call方法的代码:
def retn
      
       sql. call ( "call PA_PPRM_PivotCar.P_Tran(?,?,?,?)",['3','cheng','m', Sql.VARCHAR ] ){
           retn = it
    }
 
首先定义一个变量获取返回值,即“retn”变量。可以看到,存储过程名后面跟着四个“?”,其中三个代表存储过程的输入参数,一个代表存储过程的输出参数。
call方法的第二个参数仍然是一个List对象,先按顺序给定存储过程的输入值,完了以后再按顺序给定存储过程的返回值的类型代表值,如String类型的返回值的代表值是“ Sql.VARCHAR”等等。
最后通过一个闭包来获取存储过程的返回值,即:
{
           retn = it
    }
 
这样,我们获取返回值的存储过程调用就完成了。
你可能要问,如果有两个返回值怎么办?
def retn1
def retn2
      
       sql. call ( "call PA_PPRM_PivotCar.P_Tran(?,?,?,?,?)",['3','cheng','m', Sql.VARCHAR, Sql.VARCHAR ] ){
       value1,value2 ->
           retn1 = value1
           retn2 = value2
    }
 
上面演示了对一个返回两个 String 类型值的存储过程的调用情况,可以看到,有多少个返回值,我们的闭包就有多少个输入参数,每一个参数按顺序对应一个返回值。
 
下面,我们再把存储过程的输出参数难道加大,我们现在有这样一个存储过程“ PA_PPRM_PivotCar.P_PivotCar_GetList ”,它有一个输入参数,还有一个输出参数,但这个输出参数游标。大家可以做一个简单的存储过程,用来获取一个 table 的所有记录,把它放在游标里返回。
下面是访问这个存储过程的代码:
        sql. call ( "call PA_PPRM_PivotCar.P_PivotCar_GetList(?,?)" ,[ '2008' ,Sql.resultSet(OracleTypes.CURSOR)]){
        carModel ->
             carModel.eachRow(){
                x ->
                  println "car style: ${x.carStyle},car series: ${x.carseries},car type: ${x.cartype}"
             }
        }
 
先看 call 方法的输入参数,第一个参数和上面的没有什么不同,第二个参数不同在于输出参数类型的代表值,为“ Sql.resultSet(OracleTypes.CURSOR) ”,如果是访问的是 Oracle 数据库的话,游标的代表值固定就是它。
最后来看闭包对返回值的处理:闭包的输入参数是一个 resultset 类型对象,如“    carModel ”,所以要对它进行 eachRow 的处理,在 eachRow 的闭包里,我们就可以对它的输入参数 x 进行操作了。
在我的“ PA_PPRM_PivotCar.P_PivotCar_GetList ”存储过程里,游标里存的是这样一些参数: carStyle carseries cartype 。所以我们就可以使用“ x.carStyle ”、“ x.carseries ”和“ x.cartype ”来获取它们的值了。
上面代码的输出结果为逐行打印游标每一条记录里三个参数 carStyle carseries cartype 的值。
 
有了上面的基础,大家可以试着做一些练习:比如存储过程返回一个 String 类型值和一个游标,存储过程返回两个游标等等。
 
简单的存储过程调用到这里就可以告一段落,现在的问题是,如果我需要调用上面的存储过程,如“ PA_PPRM_PivotCar.P_Tran ”多次,即我一次对一个 table 进行多次 insert 操作,怎么办?
你肯定会说,这很简单啊,对上面的 call 语句进行多次调用就行了,比如把上面的 call 语句放在一个 for 循环里头,不就可以了。方法是对了,但这里有一个问题,聪明的你一定想到了,“事务”。
对了,怎么控制事务呢?
下面就是一个例子:
       def conn = dataSource.getConnection()
 
要对事务进行控制,首先要获取数据源的 Connection 对象,不能直接控制 dataSource 对象的 autoCommit 方法,我还没有搞清楚为什么。
然后是设置 Connection 对象的 autoCommit false ,即由我们自己来控制事务。
       conn.setAutoCommit( false )
 
 
使用 Connection 对象来初始化 Sql 对象。
       Sql sql = new Sql(conn)
 
然后是我们属性的事务控制方法, try…catch 语句。
       try
       {
 
然后调用多个存储过程。
               sql. call ( "call PA_PPRM_PivotCar.P_Tran(?,?,?)" ,[ '3' , 'cheng' , 'm' ])
              
              sql. call ( "call PA_PPRM_PivotCar.P_Tran(?,?,?)" ,[ '1' , 'feng' , 'm' ])
 
commit 事务。
              conn.commit()
       }
       catch (java.sql.SQLException e)
       {
 
如果失败,则回滚。
           conn.rollback()
           throw e;
       }
       finally
       {
 
最后,记得关闭 Connection 对象,因为 dataSource 对象会为你自动关闭 Connection 对象,但是你从 dataSource 对象中获取了 Connection 对象的话,就需要你自己关闭 Connection 对象。
           conn.close()
    }
 
 
以上就是一个简单的存储过程事务控制。大家可以写一个存储过程试试看。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值