使用休眠打印 DBMS_OUTPUT.put_line 或 Spring

Print DBMS_OUTPUT.put_line with Hibernate or Spring

我想知道 Hibernate 或 Spring 或任何第 3 方库是否提供将 DBMS_OUTPUT.put_line 消息直接打印到 system.out 或日志文件的功能。

目的是在控制台中同时显示 PLSQL 日志消息和 java 日志消息。

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

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

由于没有人回答这个问题,我想没有任何开箱即用的解决方案。所以我自己构建了它,post 我的代码在这里可能对它感兴趣的人。

欢迎任何想要改进此解决方案的人!只是不要羞于分享你的!

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

环境:Maven + Spring 4.2.6 + 休眠 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>