Java tutorial
/** * e3db Fast database library for Grails * Copyright (C) 2009-2010 Collegeman.net, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package net.collegeman.grails.e3db; import java.util.*; import java.util.regex.*; import java.sql.*; import java.io.*; import javax.sql.*; import groovy.lang.*; import org.springframework.jdbc.core.simple.*; import org.springframework.jdbc.support.rowset.*; import org.springframework.transaction.*; import org.springframework.transaction.support.*; public class DB extends Template { public static Object withTransaction(final Closure closure) { TransactionTemplate transactionTemplate = new Template().getTransactionTemplate(); if (transactionTemplate != null) { return transactionTemplate.execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus transaction) { return closure.call(); } }); } else { return closure.call(); } } public static List<Map<String, Object>> list(String sql, Object... args) { return query(METHOD_LIST, sql, List.class, args); } public static List<Map<String, Object>> list(Closure closure) { return query(METHOD_LIST, closure, List.class); } public static GroovySqlRowSet rows(String sql, Object... args) { return new GroovySqlRowSet(rowSet(sql, args)); } public static SqlRowSet rowSet(String sql, Object... args) { return query(METHOD_ROW_SET, sql, SqlRowSet.class, args); } public static GroovySqlRowSet rows(Closure closure) { return new GroovySqlRowSet(rowSet(closure)); } public static SqlRowSet rowSet(Closure closure) { return query(METHOD_ROW_SET, closure, SqlRowSet.class); } public static Object get(String sql, Object... args) { return object(sql, Object.class, args); } public static Object get(Closure closure) { return object(closure, Object.class); } public static Integer getInt(String sql, Object... args) { return object(sql, Integer.class, args); } public static Integer getInt(Closure closure) { return object(closure, Integer.class); } public static Long getLong(String sql, Object... args) { return object(sql, Long.class, args); } public static Long getLong(Closure closure) { return object(closure, Long.class); } public static Object object(String sql, Object... args) { return query(METHOD_OBJECT, sql, Object.class, args); } public static <T> T object(String sql, Class<T> requiredType, Object... args) { return (T) query(METHOD_OBJECT, sql, requiredType, args); } public static <T> T object(Closure closure, Class<T> requiredType) { return (T) query(METHOD_OBJECT, closure, requiredType); } public static int update(String sql, Object... args) { return exec(sql, args); } public static int update(Closure closure) { return exec(closure); } public static int[] batch(String sql, List<List<Object>> batchArgs) { List<Object[]> batches = new ArrayList<Object[]>(); for (List<Object> batch : batchArgs) { batches.add(batch.toArray()); } if (shouldLogSql()) logSql(sql, batchArgs); return new Template().getSJT().batchUpdate(sql, batches); } public static String json(Closure closure) { JSONWithRowSetCompiler compiler = new JSONWithRowSetCompiler(); return compiler.compile(closure); } public static JSONWithRowSetCompiler json() { return new JSONWithRowSetCompiler(); } public static String json(String sql, Object... args) { return new JSON().compile(list(sql, args)); } public static String getJSON(String sql, Object... args) { return new JSON().compile(get(sql, args)); } public static String getJSON(Closure closure) { return new JSON().compile(get(closure)); } public static class JSONWithRowSetCompiler extends JSON { private boolean __doCount = false; private List<Integer> __recordCounts = new ArrayList<Integer>(); private boolean __handleErrors = true; private boolean __includeStackTrace = false; private String __successProperty = "success"; private String __errorProperty = "error"; protected JSONWithRowSetCompiler() { } public final boolean doCount() { return __doCount; } public final Integer recordCount() { return (__recordCounts.size() > 0 ? __recordCounts.get(0) : null); } public final Integer recordCount(int which) { return __recordCounts.get(which); } public final JSONWithRowSetCompiler handleErrors(boolean handleErrors) { __handleErrors = handleErrors; return this; } public final JSONWithRowSetCompiler includeStackTrace(boolean includeStackTrace) { __includeStackTrace = includeStackTrace; return this; } public final JSONWithRowSetCompiler successProperty(String successProperty) { __successProperty = successProperty; return this; } public final JSONWithRowSetCompiler errorProperty(String errorProperty) { __errorProperty = errorProperty; return this; } public final String compile(Object obj) { if (obj instanceof Closure) { __reset(); Closure closure = (Closure) obj; closure.setDelegate(this); closure.setResolveStrategy(Closure.DELEGATE_FIRST); try { closure.call(); setProperty(__successProperty, true); } catch (RuntimeException e) { if (__handleErrors) { setProperty(__successProperty, false); Throwable cause = e.getCause(); // the true cause of our problem; Exception e is already an invoker exception Map error = new HashMap<String, Object>(); error.put("message", cause.getMessage()); error.put("type", cause.getClass().getName()); error.put("cause", (cause.getCause() != null ? cause.getCause().getClass().getName() : null)); if (__includeStackTrace && cause.getCause() != null) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); cause.getCause().printStackTrace(new PrintWriter(baos)); error.put("stackTrace", baos.toString()); baos.close(); } catch (Exception ohcrap) { log.warn( "Failed to print the Stack Trace for an exception that occurred in JSON encoding or querying with E3DB: " + ohcrap.getMessage(), ohcrap); } } setProperty(__errorProperty, error); } else { throw e; } } return "{" + toString() + "}"; } else { return super.compile(obj); } } public final Object select(Closure closure) { __doCount = false; Template t = new Template(); closure.setDelegate(t); Object sql = closure.call(); if (hasLength(sql)) t.sql(String.valueOf(sql)); t.prepend("SELECT"); if (shouldLogSql()) logSql(t.getSql(), t.getQueryParams()); return t; } public final Object selectWithCount(Closure closure) { __doCount = true; Template t = new Template(); closure.setDelegate(t); closure.setResolveStrategy(Closure.DELEGATE_FIRST); Object sql = closure.call(); if (hasLength(sql)) t.sql(String.valueOf(sql)); if (t.hasSql()) { t.prepend("SELECT"); if (shouldLogSql()) logSql(t.getSql(), t.getQueryParams()); Integer count = 0; if (t.getQueryParams() instanceof Map) count = t.getSJT().queryForObject(t.getSql(), Integer.class, (Map) t.getQueryParams()); else count = t.getSJT().queryForObject(t.getSql(), Integer.class, ((List) t.getQueryParams()).toArray()); __recordCounts.add(count); if (count > 0) { // invocation two: doCount = false __doCount = false; t = new Template(); closure.setDelegate(t); sql = closure.call(); if (hasLength(sql)) t.sql(String.valueOf(sql)); t.prepend("SELECT"); if (shouldLogSql()) logSql(t.getSql(), t.getQueryParams()); return t; } else { return "[]"; } } else { return "[]"; } } } public static class JSON extends GroovyObjectSupport implements Cloneable { protected static List<Replacement> __replacements = new ArrayList<Replacement>(); static { __replacements.add(new Replacement("\\\\", "\\\\\\\\")); __replacements.add(new Replacement("\"", "\\\\\"")); __replacements.add(new Replacement("/", "\\\\/")); __replacements.add(new Replacement("\\n", "\\\\n")); __replacements.add(new Replacement("\b", "\\\\b")); __replacements.add(new Replacement("\\f", "\\\\f")); __replacements.add(new Replacement("\\r", "\\\\r")); __replacements.add(new Replacement("\\t", "\\\\t")); } protected StringBuilder __json = new StringBuilder(); protected boolean __firstProperty = true; protected JSON() { } public JSON clone() { return new JSON(); } public static final JSON createCompiler() { return new JSON(); } protected void __reset() { __json = new StringBuilder(); __firstProperty = true; } public String compile(Object object) { __reset(); return __value(object); } public final static String withCompiler(Closure closure) { JSON compiler = new JSON(); closure.setDelegate(compiler); closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure.call(); return "{" + compiler.toString() + "}"; } public void setProperty(String name, Object newValue) { if (!__firstProperty) __json.append(","); __firstProperty = false; __json.append(__string(name)); __json.append(":"); try { String value = __value(newValue); __json.append(value); } catch (RuntimeException e) { __json.append("null"); throw e; } } public Object invokeMethod(String name, Object args) { if (name == "call") { __reset(); return __value(args); } else { setProperty(name, args); return null; } } private String __string(Object target) { String str = String.valueOf(target); for (Replacement r : __replacements) str = r.replaceAll(str); return "\"" + str + "\""; } private boolean __isnumeric(Object value) { if (value == null) { return false; } else if (value.getClass().isPrimitive()) { String prim = value.getClass().getName(); return ("int".equals(prim) || "long".equals(prim) || "float".equals(prim) || "double".equals(prim) || "byte".equals(prim) || "short".equals(prim)); } else { return (value instanceof Number); } } private String __value(Object value) { if (value == null) { return "null"; } else if (Boolean.TRUE.equals(value)) { return "true"; } else if (Boolean.FALSE.equals(value)) { return "false"; } else if (__isnumeric(value)) { return String.valueOf(value); } else if (value instanceof String) { return __string(value); } else if (value instanceof Closure) { Closure c = (Closure) value; JSON compiler = clone(); c.setDelegate(compiler); c.setResolveStrategy(Closure.DELEGATE_FIRST); c.call(); return "{" + compiler.toString() + "}"; } else if (value instanceof Map) { Map map = (Map) value; StringBuilder sMap = new StringBuilder("{"); int i = 0; for (Object key : map.keySet()) { if (i++ > 0) sMap.append(","); sMap.append(__string(key)); sMap.append(":"); sMap.append(__value(map.get(key))); } sMap.append("}"); return sMap.toString(); } else if (value instanceof List) { return __value(((List) value).toArray()); } else if (value.getClass().isArray()) { Object[] values = (Object[]) value; if (values.length > 0) { StringBuilder sValues = new StringBuilder("["); for (int i = 0; i < values.length; i++) { if (i > 0) sValues.append(","); sValues.append(__value(values[i])); } sValues.append("]"); return sValues.toString(); } else { return "[]"; } } else if (value instanceof Template) { Template t = (Template) value; JSONRowMapper rm = new JSONRowMapper(); if (t.getQueryParams() instanceof Map) t.getSJT().query(t.getSql(), rm, (Map) t.getQueryParams()); else t.getSJT().query(t.getSql(), rm, ((List) t.getQueryParams()).toArray()); return rm.toString(); } else if (value instanceof JSONReady) { Object ready = ((JSONReady) value).toJSON(); if (ready instanceof String) return (String) ready; else return __value(ready); } else { StringBuilder sObj = new StringBuilder("{"); // TODO: use reflection to pull public property values sObj.append(__string(value.toString())); sObj.append("}"); return sObj.toString(); } } public String toString() { return __json.toString(); } private static class Replacement { private Pattern pattern; private String replacement; public Replacement(String pattern, String replacement) { this.pattern = Pattern.compile(pattern); this.replacement = replacement; } public String replaceAll(String target) { return pattern.matcher(target).replaceAll(replacement); } } } private static class JSONRowMapper<T> implements ParameterizedRowMapper<T> { private StringBuilder json = new StringBuilder(); private JSON compiler = new JSON(); private ResultSetMetaData meta; private String[] cols; public T mapRow(ResultSet rs, int rowNum) { try { StringBuilder record = new StringBuilder("{"); if (cols == null && meta == null) { meta = rs.getMetaData(); if (meta.getColumnCount() > 0) { cols = new String[meta.getColumnCount()]; for (int i = 1; i <= meta.getColumnCount(); i++) { String col = compiler.compile(meta.getColumnName(i)); if (i > 1) record.append(","); cols[i - 1] = col; record.append(col); record.append(":"); record.append(compiler.compile(rs.getObject(i))); } } } else if (cols != null) { for (int i = 1; i <= cols.length; i++) { if (i > 1) record.append(","); record.append(cols[i - 1]); record.append(":"); record.append(compiler.compile(rs.getObject(i))); } } record.append("}"); if (rowNum > 0) json.append(","); json.append(record.toString()); } catch (SQLException e) { throw new RuntimeException(e); } return null; } public String toString() { return "[" + json.toString() + "]"; } } }