使用休眠打印 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
中配置 DbmsOutputAspect
和 TransactionManager
<!-- 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>
我想知道 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
中配置DbmsOutputAspect
和 TransactionManager
<!-- 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>