我正在使用的项目和具有行级安全性的Oracle数据库。我需要能够在执行任何其他SQL语句之前调用call DBMS_APPLICATION_INFO.SET_CLIENT_INFO('userId');
。我试图想出一种在MyBatis中实现这一点的方法。我有几个但无法完成工作的想法包括以下内容:
<select id="selectIds" parameterType="string" resultType="Integer">
call DBMS_APPLICATION_INFO.SET_CLIENT_INFO(#{userId});
select id from FOO
</select>
但是,单个JDBC调用中不能有两个语句,而MyBatis不支持JDBC批处理语句,或者至少不支持我可以找到的语句。
<select id="selectMessageIds" parameterType="string" resultType="Integer">
<![CDATA[
declare
type ID_TYP is table of AGL_ID.ID_ID%type;
ALL_IDS ID_TYP;
begin
DBMS_APPLICATION_INFO.SET_CLIENT_INFO(#{userId});
select ID bulk collect
into ALL_IDS
from FOO
end;
]]>
</select>
然而,这是我得到的,因为我了解到你不能在一个过程中返回数据,只能在一个函数中,所以无法返回数据。
我考虑过只创建一个简单的MyBatis语句来设置客户端信息,并且需要在执行语句之前调用它。这似乎是最有希望的,但是,我们正在使用Spring和数据库连接池,我担心竞争条件。我想确保客户端信息不会流失并影响其他语句,因为连接不会被关闭,它们将被重用。
Oracle 10g
MyBatis 3.0.5
Spring 3.0.5
更新
忘了提一下我也在使用MyBatis Spring 1.0.1
答案 0 :(得分:2)
这听起来像是交易的完美候选人。您可以创建一个@Transactional
服务(或DAO)基类来进行DBMS_APPLICATION函数调用。所有其他服务类都可以扩展基础并调用必要的SQL。
在基类中,您需要确保只调用一次DBMS_APPLICATION函数。为此,请使用TransactionSynchronizationManager.hasResource()
和bindResource()
方法将布尔值或类似标记值绑定到当前TX。检查此值以确定是否需要进行函数调用。
如果函数调用只存在于DB中的“工作单元”,那么这应该就是您所需要的。如果在连接期间存在调用,那么基类在某种程度上需要在finally块中过于清理。
而不是基类,另一种可能性是使用AOP并在方法调用之前执行函数调用并将清理作为最终建议。这里的关键是确保在Spring的TransactionInterceptor之后调用你的拦截器(即在tx启动之后)。
答案 1 :(得分:0)
最安全的解决方案之一是设置一个特定的DatSourceUtils
1:http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/jdbc/datasource/DataSourceUtils.html并覆盖连接上的doGetConnection(DataSource dataSource)和setClientInfo
在SqlMapClientDaoSupport上编写您自己的抽象以传递客户信息。