A.上传SQL文件执行。
1.上传文件
public void uploadSQL(){ UploadFile upfile = getFile(); File file = upfile.getFile(); String uploadFilePath = UploadFileUtil.saveFile2(file); EventKit.post(new UploadSQLEvent(uploadFilePath)); // 后面就是开始执行这个SQL文件 Map<String, String> jsonMap = new HashMap<String, String>(); jsonMap.put("status", "success"); jsonMap.put("message", "上传成功,已经开始执行SQL文件,请查阅相关表"); renderJson(jsonMap); }
2.发送事件,开始执行
@Listener(order = 8, enableAsync = true) public class UploadSQLListener implements ApplicationListener<UploadSQLEvent>{ private static final Logger logger = LoggerFactory.getLogger(UploadSQLListener.class); @Override public void onApplicationEvent(UploadSQLEvent event){ String fileName = (String)event.getSource(); logger.info("--------fileName=" + fileName); Db.execute(new ICallback(){ @Override public Object call(Connection conn) throws SQLException{ SQLHelper.executeSQLFile(conn, fileName); return null; } }); } }
B.下载SQL文件
1.向数据库保存一条下载记录,并发送下载事件
public void dumpSQL(){ final String tableName = getPara("tableName"); String path = JFinal.me().getServletContext().getRealPath("/"); if(!path.endsWith(File.separator)){ path += File.separator; } String fileName = tableName + "-(" + DateKit.strFormat.format(new Date()) + ").sql"; EventKit.post(new DumpSQLEvent(new DumpSQLBean(tableName, path + fileName))); Record record = new Record(); record.set(COLUMN_TASK_ID, Utils.createRandom()); record.set(COLUMN_PATH, path); record.set(COLUMN_FILE_NAME, fileName); record.set(COLUMN_FILE_TIME, new Date()); Db.save(DUMP_TABLE, record); ClientJsonObject cjo = new ClientJsonObject(); cjo.resultCode = ClientApiConstant.ResultCode.SUCCESS_CODE; cjo.message = ClientApiConstant.Msg.SUCCESS; cjo.obj = tableName + " 表的导出任务已经开始,请下载 " + fileName; renderJson(JSON.toJSONString(cjo, true)); }
2.开始下载
@Listener(order = 7, enableAsync = true) public class DumpSQLListener implements ApplicationListener<DumpSQLEvent>{ private static final Logger logger = LoggerFactory.getLogger(DumpSQLListener.class); @Override public void onApplicationEvent(DumpSQLEvent event){ DumpSQLBean bean = (DumpSQLBean)event.getSource(); logger.info("tableName=" + bean.tableName + "--------fileName=" + bean.fileName); Db.execute(new ICallback(){ @Override public Object call(Connection conn) throws SQLException{ SQLHelper.createDumpSQLFile(conn, bean.tableName, bean.fileName); return null; } }); } }
3.等待下载完成,实际下载SQL文件,首先刷新下载列表
public void sqlRefresh(){ ClientJsonObject cjo = new ClientJsonObject(); List<Record> records = Db.find("select * from " + DUMP_TABLE); List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); for(Record record : records){ Date date = record.getDate(COLUMN_FILE_TIME); record.set(COLUMN_FILE_TIME, DateKit.sdfLong.format(date)); record.remove(COLUMN_PATH); list.add(record.getColumns()); } cjo.resultCode = ClientApiConstant.ResultCode.SUCCESS_CODE; cjo.message = ClientApiConstant.Msg.SUCCESS; cjo.obj = list; renderJson(JSON.toJSONString(cjo, true)); }
4.下载并删除下载记录
public void download(){ int id = getParaToInt("id"); Record record = Db.findById(DUMP_TABLE, id); renderFile(new File(record.getStr(COLUMN_PATH) + record.getStr(COLUMN_FILE_NAME))); } public void sqlClean(){ ClientJsonObject cjo = new ClientJsonObject(); // 1.删除磁盘上的临时文件 File file = null; List<Record> records = Db.find("select * from " + DUMP_TABLE); for(Record record : records){ String path = record.getStr(COLUMN_PATH); String fileName = record.getStr(COLUMN_FILE_NAME); file = new File(path + fileName); if(null != file && file.exists()){ file.delete(); } } // 2.删除数据库记录 Db.update("delete from " + DUMP_TABLE); cjo.resultCode = ClientApiConstant.ResultCode.SUCCESS_CODE; cjo.message = ClientApiConstant.Msg.SUCCESS; cjo.obj = "删除成功"; renderJson(JSON.toJSONString(cjo, true)); }
C.常量定义和表结构
1.常量定义
private static final String DUMP_TABLE = "dumpsql"; private static final String COLUMN_ID = "id"; private static final String COLUMN_PATH = "path"; private static final String COLUMN_FILE_NAME = "filename"; private static final String COLUMN_FILE_TIME = "time"; private static final String COLUMN_TASK_ID = "taskid";
2.表结构
CREATE TABLE `dumpsql` ( `id` int(11) NOT NULL AUTO_INCREMENT, `taskid` varchar(100) NOT NULL COMMENT '任务Id,导出任务', `path` varchar(100) NOT NULL COMMENT '绝对路径', `filename` varchar(100) NOT NULL COMMENT '文件名', `time` datetime DEFAULT NULL COMMENT '时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
D.核心SQLHelper用于生成和执行SQL文件
package cn.esstx.cq.server.util; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.Date; import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Record; import cn.esstx.cq.server.util.scriptrunner.ScriptRunner; public class SQLHelper{ /** * * @Title: createDumpSQLFile * @Description:根据表名生成创建表的结构和数据的文件 * @param conn,数据库连接 * @param tableName,表名 * @param fileName,绝对路径 */ public static void createDumpSQLFile(Connection conn, String tableName, String fileName){ try{ OutputStream os = new FileOutputStream(fileName); /* 注释信息 */ createComment(conn, tableName, os); /* 创建表的SQL */ createCreateSQL(conn, tableName, os); /* 生成数据的SQL */ createInsertSQL(conn, tableName, os); os.close(); } catch(Exception e){ e.printStackTrace(); } } /** * @Title: createComment * @Description:创建注释信息 * @param conn * @param tableName * @param os */ private static void createComment(Connection conn, String tableName, OutputStream os){ try{ String catalog = conn.getCatalog(); DatabaseMetaData metaData = conn.getMetaData(); StringBuffer buffer = new StringBuffer(); buffer.append("/*\nYYH ").append(metaData.getDatabaseProductName()).append(" Data Transfer\n") .append("Target Server Type : ").append(metaData.getDatabaseProductName()) .append("\n\nSource Server Version : ").append(metaData.getDatabaseProductVersion()) .append("\nSource Database : ").append(catalog).append("\n\nDate: ") .append(DateKit.sdfLong.format(new Date())).append("\n*/\n\n"); os.write(buffer.toString().getBytes()); os.flush(); } catch(Exception e){ e.printStackTrace(); } } /** * @Title: createCreateSQL * @Description:创建创建表的SQL语句 * @param conn * @param tableName * @param os * @throws IOException */ public static void createCreateSQL(Connection conn, String tableName, OutputStream os) throws IOException{ os.write("SET FOREIGN_KEY_CHECKS=0;\n\n".getBytes()); os.write(("DROP TABLE IF EXISTS `" + tableName + "`;\n").getBytes()); os.write(("-- ----------------------------\n-- Table structure for `" + tableName + "`\n-- ----------------------------\n").getBytes()); try{ Record record = Db.findFirst("show create table " + tableName); os.write(record.getStr("Create Table").getBytes()); } catch(Exception e){ e.printStackTrace(); } os.write(";\n\n".getBytes()); os.flush(); } /** * @Title: createInsertSQL * @Description:创建Insert SQL语句 * @param conn * @param tableName * @param os * @throws SQLException * @throws IOException */ public static void createInsertSQL(Connection conn, String tableName, OutputStream os) throws SQLException, IOException{ os.write(("-- ----------------------------\n-- Records of " + tableName + "\n-- ----------------------------\n") .getBytes()); int pageSize = 50; long count = 0; ResultSet set = query(conn, "select count(*) from " + tableName); if(set.next()){ count = set.getLong(1); } // System.out.println("count=" + count); int totalPage = (int)(count / pageSize); if(count % pageSize != 0){ totalPage++; } // System.out.println("totalPage=" + totalPage); for(int i = 0; i < totalPage; i++){ handleOnePage(conn, tableName, os, i + 1, pageSize); } } /** * @Title: handleOnePage * @Description:处理一页 * @param conn * @param tableName * @param os * @param pageNumber * @param pageSize * @throws SQLException * @throws IOException */ public static void handleOnePage(Connection conn, String tableName, OutputStream os, int pageNumber, int pageSize) throws SQLException, IOException{ int offset = pageSize * (pageNumber - 1); ResultSet set = query(conn, "select * from " + tableName + " limit " + offset + ", " + pageSize); ResultSetMetaData metaData = set.getMetaData(); int count = metaData.getColumnCount(); // List<String> labels = getLabels(metaData); StringBuffer insertSQL = null; String insert = "INSERT INTO `" + tableName + "` VALUES ("; while(set.next()){ insertSQL = new StringBuffer(insert); for(int i = 0; i < count; i++){ Object obj = set.getObject(i + 1); int type = metaData.getColumnType(i + 1); appendAColumn(insertSQL, obj, type); } String sql = insertSQL.substring(0, insertSQL.length() - 2);// 去掉最后的逗号和空格 sql = sql + ");\n"; // System.out.println(sql);// // 这个语句可以保存到文件中,在你的数据库中运行这个文件即可。 os.write(sql.getBytes()); os.flush(); insertSQL.setLength(0); } set.close(); } /** * @Title: appendAColumn * @Description:根据类型增加一列到StringBuffer * @param insertSQL * @param obj * @param type */ public static void appendAColumn(StringBuffer insertSQL, Object obj, int type){ if(null == obj){ insertSQL.append("null").append(", "); } else{ if(isChar(type)){// 根据列的类型看是否需要添加'' String objString = String.valueOf(obj); insertSQL.append("'" + handleQuote(objString) + "'").append(", ");// 还要进一步处理有\n,""的情况 } else{ insertSQL.append(obj).append(", "); } } } /** * @Title: handleQuote * @Description:处理字符串中的"" 、\n * @param string * @return String */ private static String handleQuote(String src){ String dest = src.replaceAll("\n", "\\\\n");// 把换行变成\n dest = dest.replaceAll("\"", "\\\\\"");// 把"换成\" return dest; } private static boolean isChar(int type){ switch(type){ case Types.CHAR: case Types.DATE: case Types.LONGNVARCHAR: case Types.LONGVARCHAR: case Types.NCHAR: case Types.NVARCHAR: case Types.SQLXML: case Types.VARCHAR: case Types.TIMESTAMP: case Types.TIME: return true; default: return false; } } public static ResultSet query(Connection connection, String sql, Object... args){ ResultSet rs = null; try{ PreparedStatement ps = connection.prepareStatement(sql); for(int i = 0; i < args.length; i++) ps.setObject(i + 1, args[i]); rs = ps.executeQuery(); } catch(Exception e){ e.printStackTrace(); } return rs; } /** * @Title: executeSQLFile * @Description:执行上传的SQL文件 * @param conn * @param fileName */ public static void executeSQLFile(Connection conn, String fileName){ try{ ScriptRunner runner = new ScriptRunner(conn, true, true); // runner.setErrorLogWriter(new PrintWriter(System.out));// 输出到标准流里 runner.setLogWriter(null);// 不输出日志 runner.runScript(new FileReader(fileName)); conn.close(); } catch(Exception e){ e.printStackTrace(); } } }
E.执行sql语句的scriptrunner
package cn.esstx.cq.server.util.scriptrunner; import java.io.IOException; import java.io.LineNumberReader; import java.io.PrintWriter; import java.io.Reader; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; /** * Tool to run database scripts */ public class ScriptRunner{ // private static final Log log = LogFactory.getLog(ScriptRunner.class); private Connection connection; private String driver; private String url; private String username; private String password; private boolean stopOnError; private boolean autoCommit; private PrintWriter logWriter = new PrintWriter(System.out); private PrintWriter errorLogWriter = new PrintWriter(System.err); /** * Default constructor */ public ScriptRunner(Connection connection, boolean autoCommit, boolean stopOnError){ this.connection = connection; this.autoCommit = autoCommit; this.stopOnError = stopOnError; } public ScriptRunner(String driver, String url, String username, String password, boolean autoCommit, boolean stopOnError){ this.driver = driver; this.url = url; this.username = username; this.password = password; this.autoCommit = autoCommit; this.stopOnError = stopOnError; } /** * Setter for logWriter property * * @param logWriter * - the new value of the logWriter property */ public void setLogWriter(PrintWriter logWriter){ this.logWriter = logWriter; } /** * Setter for errorLogWriter property * * @param errorLogWriter * - the new value of the errorLogWriter property */ public void setErrorLogWriter(PrintWriter errorLogWriter){ this.errorLogWriter = errorLogWriter; } /** * Runs an SQL script (read in using the Reader parameter) * * @param reader * - the source of the script */ public void runScript(Reader reader) throws IOException, SQLException{ try{ if(connection == null){ DriverManager.registerDriver((Driver)Resources.classForName(driver).newInstance()); Connection conn = DriverManager.getConnection(url, username, password); try{ if(conn.getAutoCommit() != autoCommit){ conn.setAutoCommit(autoCommit); } runScript(conn, reader); } finally{ conn.close(); } } else{ boolean originalAutoCommit = connection.getAutoCommit(); try{ if(originalAutoCommit != this.autoCommit){ connection.setAutoCommit(this.autoCommit); } runScript(connection, reader); } finally{ connection.setAutoCommit(originalAutoCommit); } } } catch(IOException e){ throw e; } catch(SQLException e){ throw e; } catch(Exception e){ throw new NestedRuntimeException("Error running script. Cause: " + e, e); } } /** * Runs an SQL script (read in using the Reader parameter) using the * connection passed in * * @param conn * - the connection to use for the script * @param reader * - the source of the script * @throws SQLException * if any SQL errors occur * @throws IOException * if there is an error reading from the Reader */ private void runScript(Connection conn, Reader reader) throws IOException, SQLException{ StringBuffer command = null; try{ LineNumberReader lineReader = new LineNumberReader(reader); String line = null; while((line = lineReader.readLine()) != null){ if(command == null){ command = new StringBuffer(); } String trimmedLine = line.trim(); if(trimmedLine.startsWith("--")){ println(trimmedLine); } else if(trimmedLine.length() < 1 || trimmedLine.startsWith("//")){ // Do nothing } else if(trimmedLine.length() < 1 || trimmedLine.startsWith("--")){ // Do nothing } else if(trimmedLine.endsWith(";")){ command.append(line.substring(0, line.lastIndexOf(";"))); command.append(" "); Statement statement = conn.createStatement(); println(command); boolean hasResults = false; if(stopOnError){ hasResults = statement.execute(command.toString()); } else{ try{ statement.execute(command.toString()); } catch(SQLException e){ e.fillInStackTrace(); printlnError("Error executing: " + command); printlnError(e); } } if(autoCommit && !conn.getAutoCommit()){ conn.commit(); } ResultSet rs = statement.getResultSet(); if(hasResults && rs != null){ ResultSetMetaData md = rs.getMetaData(); int cols = md.getColumnCount(); for(int i = 0; i < cols; i++){ String name = md.getColumnName(i); print(name + "\t"); } println(""); while(rs.next()){ for(int i = 0; i < cols; i++){ String value = rs.getString(i); print(value + "\t"); } println(""); } } command = null; try{ statement.close(); } catch(Exception e){ // Ignore to workaround a bug in Jakarta DBCP } Thread.yield(); } else{ command.append(line); command.append(" "); } } if(!autoCommit){ conn.commit(); } } catch(SQLException e){ e.fillInStackTrace(); printlnError("Error executing: " + command); printlnError(e); throw e; } catch(IOException e){ e.fillInStackTrace(); printlnError("Error executing: " + command); printlnError(e); throw e; } finally{ conn.rollback(); flush(); } } private void print(Object o){ if(logWriter != null){ System.out.print(o); } } private void println(Object o){ if(logWriter != null){ logWriter.println(o); } } private void printlnError(Object o){ if(errorLogWriter != null){ errorLogWriter.println(o); } } private void flush(){ if(logWriter != null){ logWriter.flush(); } if(errorLogWriter != null){ errorLogWriter.flush(); } } }
package cn.esstx.cq.server.util.scriptrunner; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.net.URLConnection; import java.util.Properties; /** * A class to simplify access to resources through the classloader. */ public class Resources extends Object{ private static ClassLoader defaultClassLoader; private Resources(){} /** * Returns the default classloader (may be null). * * @return The default classloader */ public static ClassLoader getDefaultClassLoader(){ return defaultClassLoader; } /** * Sets the default classloader * * @param defaultClassLoader * - the new default ClassLoader */ public static void setDefaultClassLoader(ClassLoader defaultClassLoader){ Resources.defaultClassLoader = defaultClassLoader; } /** * Returns the URL of the resource on the classpath * * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static URL getResourceURL(String resource) throws IOException{ return getResourceURL(getClassLoader(), resource); } /** * Returns the URL of the resource on the classpath * * @param loader * The classloader used to load the resource * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static URL getResourceURL(ClassLoader loader, String resource) throws IOException{ URL url = null; if(loader != null) url = loader.getResource(resource); if(url == null) url = ClassLoader.getSystemResource(resource); if(url == null) throw new IOException("Could not find resource " + resource); return url; } /** * Returns a resource on the classpath as a Stream object * * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static InputStream getResourceAsStream(String resource) throws IOException{ return getResourceAsStream(getClassLoader(), resource); } /** * Returns a resource on the classpath as a Stream object * * @param loader * The classloader used to load the resource * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException{ InputStream in = null; if(loader != null) in = loader.getResourceAsStream(resource); if(in == null) in = ClassLoader.getSystemResourceAsStream(resource); if(in == null) throw new IOException("Could not find resource " + resource); return in; } /** * Returns a resource on the classpath as a Properties object * * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static Properties getResourceAsProperties(String resource) throws IOException{ Properties props = new Properties(); InputStream in = null; String propfile = resource; in = getResourceAsStream(propfile); props.load(in); in.close(); return props; } /** * Returns a resource on the classpath as a Properties object * * @param loader * The classloader used to load the resource * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException{ Properties props = new Properties(); InputStream in = null; String propfile = resource; in = getResourceAsStream(loader, propfile); props.load(in); in.close(); return props; } /** * Returns a resource on the classpath as a Reader object * * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static Reader getResourceAsReader(String resource) throws IOException{ return new InputStreamReader(getResourceAsStream(resource)); } /** * Returns a resource on the classpath as a Reader object * * @param loader * The classloader used to load the resource * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException{ return new InputStreamReader(getResourceAsStream(loader, resource)); } /** * Returns a resource on the classpath as a File object * * @param resource * The resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static File getResourceAsFile(String resource) throws IOException{ return new File(getResourceURL(resource).getFile()); } /** * Returns a resource on the classpath as a File object * * @param loader * - the classloader used to load the resource * @param resource * - the resource to find * @return The resource * @throws IOException * If the resource cannot be found or read */ public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException{ return new File(getResourceURL(loader, resource).getFile()); } /** * Gets a URL as an input stream * * @param urlString * - the URL to get * @return An input stream with the data from the URL * @throws IOException * If the resource cannot be found or read */ public static InputStream getUrlAsStream(String urlString) throws IOException{ URL url = new URL(urlString); URLConnection conn = url.openConnection(); return conn.getInputStream(); } /** * Gets a URL as a Reader * * @param urlString * - the URL to get * @return A Reader with the data from the URL * @throws IOException * If the resource cannot be found or read */ public static Reader getUrlAsReader(String urlString) throws IOException{ return new InputStreamReader(getUrlAsStream(urlString)); } /** * Gets a URL as a Properties object * * @param urlString * - the URL to get * @return A Properties object with the data from the URL * @throws IOException * If the resource cannot be found or read */ public static Properties getUrlAsProperties(String urlString) throws IOException{ Properties props = new Properties(); InputStream in = null; String propfile = urlString; in = getUrlAsStream(propfile); props.load(in); in.close(); return props; } /** * Loads a class * * @param className * - the class to load * @return The loaded class * @throws ClassNotFoundException * If the class cannot be found (duh!) */ public static Class classForName(String className) throws ClassNotFoundException{ Class clazz = null; try{ clazz = getClassLoader().loadClass(className); } catch(Exception e){ // Ignore. Failsafe below. } if(clazz == null){ clazz = Class.forName(className); } return clazz; } /** * Creates an instance of a class * * @param className * - the class to create * @return An instance of the class * @throws ClassNotFoundException * If the class cannot be found (duh!) * @throws InstantiationException * If the class cannot be instantiaed * @throws IllegalAccessException * If the class is not public, or other access problems arise */ public static Object instantiate(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ return instantiate(classForName(className)); } /** * Creates an instance of a class * * @param clazz * - the class to create * @return An instance of the class * @throws InstantiationException * If the class cannot be instantiaed * @throws IllegalAccessException * If the class is not public, or other access problems arise */ public static Object instantiate(Class clazz) throws InstantiationException, IllegalAccessException{ return clazz.newInstance(); } private static ClassLoader getClassLoader(){ if(defaultClassLoader != null){ return defaultClassLoader; } else{ return Thread.currentThread().getContextClassLoader(); } } }
package cn.esstx.cq.server.util.scriptrunner; /** * Nexted exception implementation. Thanks Claus. */ public class NestedRuntimeException extends RuntimeException{ // @Fields serialVersionUID : private static final long serialVersionUID = 1L; private static final String CAUSED_BY = "\nCaused by: "; private Throwable cause = null; /** * Constructor */ public NestedRuntimeException(){} /** * Constructor * * @param msg * error message */ public NestedRuntimeException(String msg){ super(msg); } /** * Constructor * * @param cause * the nested exception (caused by) */ public NestedRuntimeException(Throwable cause){ super(); this.cause = cause; } /** * Constructor * * @param msg * error message * @param cause * the nested exception (caused by) */ public NestedRuntimeException(String msg, Throwable cause){ super(msg); this.cause = cause; } /** * Gets the causing exception, if any. * * @return The cause of the exception */ public Throwable getCause(){ return cause; } /** * Converts the exception to a string representation * * @return The string representation of the exception */ public String toString(){ if(cause == null){ return super.toString(); } else{ return super.toString() + CAUSED_BY + cause.toString(); } } /** * Sends a stack trace to System.err (including the root cause, if any) */ public void printStackTrace(){ super.printStackTrace(); if(cause != null){ System.err.println(CAUSED_BY); cause.printStackTrace(); } } /** * Sends a stack trace to the PrintStream passed in (including the root * cause, if any) * * @param ps * - the PrintStream to send the output to */ public void printStackTrace(java.io.PrintStream ps){ super.printStackTrace(ps); if(cause != null){ ps.println(CAUSED_BY); cause.printStackTrace(ps); } } /** * Sends a stack trace to the PrintWriter passed in (including the root * cause, if any) * * @param pw * - the PrintWriter to send the output to */ public void printStackTrace(java.io.PrintWriter pw){ super.printStackTrace(pw); if(cause != null){ pw.println(CAUSED_BY); cause.printStackTrace(pw); } } }
鸣谢:
scriptrunner来自于mybatis
connection来自JFinal
events结构来自JFinal-events
生成SQL语句的借鉴了JFinal的分页做法