使用Hibernate或Spring打印DBMS_OUTPUT.put_line

时间:2017-11-06 13:24:38

标签: java spring hibernate plsql dbms-output

我想知道Hibernate或Spring或任何第三方lib是否能够将DBMS_OUTPUT.put_line消息直接打印到system.out或日志文件。

目的是在控制台中同时拥有PLSQL日志消息和Java日志消息。

我知道有一个类似的问题,答案是将PLSQL过程转换为返回日志消息的功能,但这并不适合我的情况。事实上,我的日志消息太复杂了,无法在函数中返回它。

我已阅读此https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:45027262935845并获得了灵感,但我想知道是否有一个开箱即用的解决方案来避免鲸鱼繁殖。

1 个答案:

答案 0 :(得分:1)

由于没有人回答这个问题,我认为没有任何开箱即用的解决方案。因此,我自己构建并在此处发布我可能感兴趣的代码。

欢迎任何想要改进此解决方案的人!分享你的东西真是太可惜了!

基于Spring AOP的解决方案。创建注释@DbmsOutput以在相关方法上添加DBMS_OUTPUT识别能力。

环境:Maven + Spring 4.2.6 + Hibernate 5.1.0

第1步:添加Spring AOP依赖项

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.4</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.4</version>
</dependency>

第2步:创建@DbmsOutput

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface DbmsOutput {
}

步骤3:为使用@DbmsOutput声明的所有方法创建拦截器。

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.jdbc.Work;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Aspect
@Order(Ordered.LOWEST_PRECEDENCE)
public class DbmsOutputAspect {

    private static final Logger LOGGER = Logger.getLogger(DbmsOutputAspect.class);

    @Autowired
    protected SessionFactory sessionFactory;
    /**
     * make Dbms output aware enable/disable configurable in spring bean declaration
     */
    private boolean enable = true;
    private int size = 1000000;

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(final boolean enable) {
        this.enable = enable;
    }

    public int getSize() {
        return size;
    }

    public void setSize(final int size) {
        this.size = size;
    }

    @Pointcut("execution(@DbmsOutput * *(..))")
    public void DbmsOutputInterceptMethod() {
    }

    @Around("DbmsOutputInterceptMethod()")
    public Object around(final ProceedingJoinPoint point)
            throws Throwable {

        if (isEnable()) {
            LOGGER.debug("before DBMS_OUTPUT point cut");
            Query queryEnable = sessionFactory.getCurrentSession().createSQLQuery("call dbms_output.enable(:size)");
            queryEnable.setParameter("size", getSize());
            queryEnable.executeUpdate();
        }
        try {
            return point.proceed(point.getArgs());
        } finally {
            if (isEnable()) {
                sessionFactory.getCurrentSession().doWork(new Work() {

                    @Override
                    public void execute(final Connection connection)
                            throws SQLException {

                        CallableStatement show_stmt = connection.prepareCall(
                                "declare " +
                                        " l_line varchar2(255); " +
                                        " l_done number; " +
                                        " l_buffer long; " +
                                        "begin " +
                                        " loop " +
                                        " exit when length(l_buffer)+255 > :maxbytes OR l_done = 1; " +
                                        " dbms_output.get_line( l_line, l_done ); " +
                                        " l_buffer := l_buffer || l_line || chr(10); " +
                                        " end loop; " +
                                        " :done := l_done; " +
                                        " :buffer := l_buffer; " +
                                        "end;");
                        int done = 0;

                        show_stmt.registerOutParameter(2, java.sql.Types.INTEGER);
                        show_stmt.registerOutParameter(3, java.sql.Types.VARCHAR);

                        for (;;) {
                            show_stmt.setInt(1, 32000);
                            show_stmt.executeUpdate();
                            LOGGER.info(show_stmt.getString(3));
                            done = show_stmt.getInt(2);
                            if (done == 1) {
                                break;
                            }
                        }
                    }
                });
                Query queryDisable = sessionFactory.getCurrentSession().createSQLQuery("call dbms_output.disable()");
                queryDisable.executeUpdate();
                LOGGER.debug("after DBMS_OUTPUT point cut");
            }
        }
    }
}

第4步:在Spring中配置DbmsOutputAspectTransactionManager

<!-- aspectj -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="DbmsOutputAspect" />

<!-- transaction -->
<tx:annotation-driven transaction-manager="transactionManager" order="0" />
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref bean="sessionFactory" />
    </property>
</bean>