java - 使用 Hibernate 或 Spring 打印 DBMS_OUTPUT.put_line

标签 java spring hibernate plsql dbms-output

我想知道 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并获得了灵感,但我想知道是否有一个现成的解决方案可以避免鲸鱼繁殖。

最佳答案

由于没有人回答这个问题,我想没有任何现成的解决方案。因此,我自己构建了它,并在这里发布了可能感兴趣的人的代码。

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

基于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>

关于java - 使用 Hibernate 或 Spring 打印 DBMS_OUTPUT.put_line,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47138041/

相关文章:

database - Hibernate中的 native 查询性能

java - Hibernate脏对象的使用

java - 错误 : class org. hibernate.ejb.HibernatePersistence 将接口(interface) org.hibernate.ejb.AvailableSettings 作为父类(super class)

java - 如何使用java为二叉搜索树创建深度复制

java - 如何在 Java 中为子/嵌套对象编写 MongoDB 集合过滤器?

Spring MVC 主页 Controller

java - 运行 java jar 应用程序时如何修复 "java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory"错误

java - 关于 AbstractApplicationContext.getBeansOfType() 和 getBean() 的问题

java - 求图像 Java 的平均值

java - 使用 Intent 标志和 androidmanifest