简体   繁体   中英

Getting string representation of an SQL query from MyBatis mapper

I have question about MyBatis and executed SQL queries. Is there any chance I can get executed SQL from a mapper method represented as String?

There is SearchRepository that calls the searchTmp method in Mapper that calls and execute the searchTmp method from MyBatis XML file.

In the perfect world I would like to change the void return type of a searchTmp method to a String and force MyBatis to return the executed SQL as a string. Is there any chance I could acheive that?

 public class SearchRepository { private final SearchTmpMapper mapper; public void searchTmp() { SearchFilter filter = intializeFilter(); mapper.searchTmp(filter); } public interface SearchTmpMapper { void searchTmp( @Param("filter") SearchFilter filter); } <select id="searchTmp"> /* some SQL select */ </select>

You can do it using Spring AOP, but will require you to add an extra class to your app, that will implement the new "aspect". See below:

A couple of years ago I implemented an AOP aspect (I was using Spring) that recorded all executed SQL statements and their response time for the HotRod ORM I'm working on.

The code below (open source) records each SQL statement and their response time. It inner class (not shown here) ranks their execution from heaviest to lightest; a REST API can provide the "worst offenders" to the admin interface of the app.

The example below uses Spring AOP to do it:

@Aspect
@Component
public class SQLMetricsAspect {

  private ThreadLocal<String> sql = new ThreadLocal<String>();

  @Autowired
  private SQLMetrics sqlMetrics;

  @Around(value = "execution(* javax.sql.DataSource.getConnection())")
  public Object measureGetConnection(final ProceedingJoinPoint joinPoint) throws Throwable {
    try {
      Object conn = joinPoint.proceed();
      AspectJProxyFactory proxyFactory = new AspectJProxyFactory(conn);
      proxyFactory.addAspect(this);
      Object proxyConn = proxyFactory.getProxy();
      return proxyConn;
    } catch (Throwable e) { throw e; }
  }

  @Around(value = "execution(* java.sql.Connection.prepareStatement(..)) && args(sql)")
  public Object measurePrepareStatement(final ProceedingJoinPoint joinPoint, final String sql) throws Throwable {
    try {
      this.sql.set(sql);
      Object ps = joinPoint.proceed();
      AspectJProxyFactory proxyFactory = new AspectJProxyFactory(ps);
      proxyFactory.addAspect(this);
      PreparedStatement proxyPS = proxyFactory.getProxy();
      return proxyPS;
    } catch (Throwable e) { throw e; }
  }

  // Then advice each SQL execution method, as in:

  @Around(value = "execution(* java.sql.PreparedStatement.execute(..))")
  public Object adviceExecute(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  // All other java.sql.PreparedStatement declared methods...

The rest of the methods in Java 8 are:

  @Around(value = "execution(* java.sql.PreparedStatement.executeLargeUpdate(..))")
  public Object adviceExecuteLargeUpdate(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  @Around(value = "execution(* java.sql.PreparedStatement.executeQuery(..))")
  public Object adviceExecExecuteQuery(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  @Around(value = "execution(* java.sql.PreparedStatement.executeUpdate(..))")
  public Object adviceExecuteUpdate(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  // java.sql.Statement declared methods

  @Around(value = "execution(* java.sql.Statement.execute(..)) && args(sql)")
  public Object adviceExecute(final ProceedingJoinPoint joinPoint, final String sql) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.execute(..)) && args(sql, autoGeneratedKeys)")
  public Object adviceExecute(final ProceedingJoinPoint joinPoint, final String sql, final int autoGeneratedKeys)
      throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.execute(..)) && args(sql, columnIndexes)")
  public Object adviceExecute(final ProceedingJoinPoint joinPoint, final String sql, final int[] columnIndexes)
      throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.execute(..)) && args(sql, columnNames)")
  public Object adviceExecute(final ProceedingJoinPoint joinPoint, final String sql, final String[] columnNames)
      throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  // TODO Check how batches work
  @Around(value = "execution(* java.sql.Statement.executeBatch(..))")
  public Object adviceExecuteBatch(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  // TODO Check how batches work
  @Around(value = "execution(* java.sql.Statement.executeLargeBatch(..))")
  public Object adviceExecuteLargeBatch(final ProceedingJoinPoint joinPoint) throws Throwable {
    return measureSQLExecution(joinPoint, this.sql.get());
  }

  @Around(value = "execution(* java.sql.Statement.executeLargeUpdate(..)) && args(sql)")
  public Object adviceExecuteLargeUpdate(final ProceedingJoinPoint joinPoint, final String sql) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeLargeUpdate(..)) && args(sql, autoGeneratedKeys)")
  public Object adviceExecuteLargeUpdateAK(final ProceedingJoinPoint joinPoint, final String sql,
      final int autoGeneratedKeys) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeLargeUpdate(..)) && args(sql, columnIndexes)")
  public Object adviceExecuteLargeUpdate(final ProceedingJoinPoint joinPoint, final String sql,
      final int[] columnIndexes) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeLargeUpdate(..)) && args(sql, columnNames)")
  public Object adviceExecuteLargeUpdate(final ProceedingJoinPoint joinPoint, final String sql,
      final String[] columnNames) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeQuery(..)) && args(sql)")
  public Object adviceExecuteQuery(final ProceedingJoinPoint joinPoint, final String sql) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeUpdate(..)) && args(sql)")
  public Object adviceExecuteUpdate(final ProceedingJoinPoint joinPoint, final String sql) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeUpdate(..)) && args(sql, autoGeneratedKeys)")
  public Object adviceExecuteUpdateAK(final ProceedingJoinPoint joinPoint, final String sql,
      final int autoGeneratedKeys) throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeUpdate(..)) && args(sql, columnIndexes)")
  public Object adviceExecuteUpdate(final ProceedingJoinPoint joinPoint, final String sql, final int[] columnIndexes)
      throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

  @Around(value = "execution(* java.sql.Statement.executeUpdate(..)) && args(sql, columnNames)")
  public Object adviceExecuteUpdate(final ProceedingJoinPoint joinPoint, final String sql, final String[] columnNames)
      throws Throwable {
    return measureSQLExecution(joinPoint, sql);
  }

Finally, you implement the method to do whatever you want with the SQL statements. In my case I recorded the SQL statement, its execution time, and the execution error (if any):

  private Object measureSQLExecution(final ProceedingJoinPoint joinPoint,
      final String sql) throws Throwable {
    long start = System.currentTimeMillis();
    try {
      Object ps = joinPoint.proceed();
      long end = System.currentTimeMillis();
      this.sqlMetrics.record(sql, end - start, null);
      return ps;

    } catch (Throwable t) {
      long end = System.currentTimeMillis();
      this.sqlMetrics.record(sql, end - start, t);
      throw t;
    }
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM