001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.db;
017
018import com.jfinal.log.Log;
019import com.jfinal.plugin.activerecord.Config;
020import io.jboot.Jboot;
021import io.jboot.utils.DateUtil;
022import io.jboot.utils.StrUtil;
023
024import java.sql.SQLException;
025import java.util.Date;
026import java.util.regex.Matcher;
027
028/**
029 * @author michael yang (fuhai999@gmail.com)
030 * @Date: 2019/12/12
031 */
032public class SqlDebugger {
033
034
035    private static SqlDebugPrinter printer = SqlDebugPrinter.DEFAULT_PRINTER;
036
037    public static SqlDebugPrinter getPrinter() {
038        return printer;
039    }
040
041    public static void setPrinter(SqlDebugPrinter printer) {
042        SqlDebugger.printer = printer;
043    }
044
045    public static <T> T run(SqlRunner<T> runner, Config config, String sql, Object... paras) throws SQLException {
046        if (!printer.isPrintEnable(config)) {
047            return runner.run();
048        } else {
049            long timeMillis = System.currentTimeMillis();
050            try {
051                return runner.run();
052            } finally {
053                doDebug(System.currentTimeMillis() - timeMillis, sql, paras);
054            }
055        }
056    }
057
058
059    private static void doDebug(Long tookTimeMillis, String sql, Object... paras) {
060        if (paras != null) {
061            for (Object value : paras) {
062                // null
063                if (value == null) {
064                    sql = sql.replaceFirst("\\?", "null");
065                }
066                // number
067                else if (value instanceof Number || value instanceof Boolean) {
068                    sql = sql.replaceFirst("\\?", value.toString());
069                }
070                // numeric
071                else if (value instanceof String && StrUtil.isNumeric((String) value)) {
072                    sql = sql.replaceFirst("\\?", (String) value);
073                }
074                // other
075                else {
076                    StringBuilder sb = new StringBuilder();
077                    sb.append("'");
078                    if (value instanceof Date) {
079                        sb.append(DateUtil.toDateTimeString((Date) value));
080                    } else {
081                        sb.append(value);
082                    }
083                    sb.append("'");
084                    sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(sb.toString()));
085                }
086            }
087        }
088
089        printer.print(sql, tookTimeMillis);
090    }
091
092
093    public interface SqlDebugPrinter {
094
095        SqlDebugPrinter DEFAULT_PRINTER = new SqlDebugPrinter() {
096
097            private boolean printSqlEnable = Jboot.isDevMode();
098
099            @Override
100            public void setPrintEnable(boolean enable) {
101                this.printSqlEnable = enable;
102            }
103
104            @Override
105            public boolean isPrintEnable(Config config) {
106                return printSqlEnable;
107            }
108
109            @Override
110            public void print(String sql, Long tookTimeMillis) {
111                if (tookTimeMillis != null) {
112                    System.out.println("Jboot exec sql took " + tookTimeMillis + " ms >>>  " + sql);
113                } else {
114                    System.out.println("Jboot exec sql >>>  " + sql);
115                }
116            }
117        };
118
119        SqlDebugPrinter LOG_PRINTER = new SqlDebugPrinter() {
120
121            private boolean printSqlEnable = Jboot.isDevMode();
122            private Log log = Log.getLog("SqlDebugPrinter.LogPrinter");
123
124            @Override
125            public void setPrintEnable(boolean enable) {
126                this.printSqlEnable = enable;
127            }
128
129            @Override
130            public boolean isPrintEnable(Config config) {
131                return printSqlEnable;
132            }
133
134            @Override
135            public void print(String sql, Long tookTimeMillis) {
136                if (tookTimeMillis != null) {
137                    log.debug("Jboot exec sql took " + tookTimeMillis + " ms >>>  " + sql);
138                } else {
139                    log.debug("Jboot exec sql >>>  " + sql);
140                }
141            }
142        };
143
144        void setPrintEnable(boolean enable);
145
146        boolean isPrintEnable(Config config);
147
148        void print(String sql, Long tookTimeMillis);
149    }
150
151    public interface SqlRunner<V> {
152        V run() throws SQLException;
153    }
154}